mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-06-26 06:29:29 +00:00
Deploying to gh-pages from @ sehugg/8bitworkshop@5a93bc8b75 🚀
This commit is contained in:
parent
d1024c06c6
commit
784ec76080
7
gen/atari7800-5KKGRW7B.js
Normal file
7
gen/atari7800-5KKGRW7B.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import{I as z,h as W,k as N,p as F,t as H}from"./chunk-HB3LWF25.js";import{$ as I,J as B,U as M,V as f,W as O,Y as U,_ as E,da as _,g}from"./chunk-ATS7PSQG.js";import"./chunk-5XVCUSSZ.js";var d=0,L=2,P=8,X=U([[f.A,P+0,128],[f.B,P+1,128],[f.SELECT,L,-2],[f.START,L,-1],[f.UP,d,-16],[f.DOWN,d,-32],[f.LEFT,d,-64],[f.RIGHT,d,-128],[f.P2_A,P+2,128],[f.P2_B,P+3,128],[f.P2_UP,d,-1],[f.P2_DOWN,d,-2],[f.P2_LEFT,d,-4],[f.P2_RIGHT,d,-8]]);var D=263,K=258-16,b=451,j=28,q=16,J=24,Y=2,$=D*60*Y,T=class{constructor(){this.regs=new Uint8Array(32)}reset(){this.regs.fill(0)}read(t){return this.regs[t]|0}write(t,e){this.regs[t]=e}saveState(){return{regs:this.regs.slice(0)}}loadState(t){for(let e=0;e<32;e++)this.write(e,t.regs[e])}static stateToLongString(t){let e="";return e+=M(t.regs,0,32),e}},k=class{constructor(){this.cycles=0;this.regs=new Uint8Array(32);this.offset=-1;this.dll=0;this.dlstart=0;this.dli=!1;this.h16=!1;this.h8=!1;this.indirect=!1;this.pixels=new Uint8Array(320);this.WSYNC=0}reset(){this.regs.fill(0)}read(t){return this.regs[t]|0}write(t,e){this.regs[t]=e,t==4&&this.WSYNC++}saveState(){return{regs:this.regs.slice(0),offset:this.offset,dll:this.dll,dlstart:this.dlstart,dli:this.dli,h16:this.h16,h8:this.h8,indirect:this.indirect}}loadState(t){for(let e=0;e<32;e++)this.write(e,t.regs[e]|0);this.offset=t.offset|0,this.dll=t.dll|0,this.dlstart=t.dlstart|0,this.dli=!!t.dli,this.h16=!!t.h16,this.h8=!!t.h8,this.indirect=!!t.indirect}isDMAEnabled(){return(this.regs[28]&96)==64}getDLLStart(){return(this.regs[12]<<8)+this.regs[16]}getCharBaseAddress(){return(this.regs[20]<<8)+this.offset}setVBLANK(t){t?(this.regs[8]|=128,this.offset=-1,this.dll=this.getDLLStart(),this.dli=this.bus&&(this.bus.read(this.dll)&128)!=0):this.regs[8]&=~128}readDLLEntry(t){if(this.dll>=16384)return;let e=t.read(this.dll);this.offset=e&15,this.h16=(e&64)!=0,this.h8=(e&32)!=0,this.dlstart=(t.read(this.dll+1)<<8)+t.read(this.dll+2),this.dll=this.dll+3&65535,this.dli=(t.read(this.dll)&128)!=0}isHoley(t){return this.indirect?!1:!!(t&32768&&(this.h16&&t&4096||this.h8&&t&2048))}readDMA(t){return this.isHoley(t)?0:(this.cycles+=3,this.bus.read(t))}doDMA(t){if(this.bus=t,this.cycles=0,this.pixels.fill(this.regs[0]),this.isDMAEnabled()){this.cycles+=this.offset==0?J:q,this.offset<0&&this.readDLLEntry(t);let i=this.dlstart&65280,h=this.dlstart&255;do{let n=t.read(i+(h+0&511)),c=t.read(i+(h+1&511));if(c==0||i>=16384)break;let A=t.read(i+(h+2&511)),m=t.read(i+(h+3&511)),x=!1;if((c&31)==0){var e=m>>5,a=32-(m&31),r=t.read(i+(h+4&511)),s=c&128;x=(c&32)!=0,h+=5,this.cycles+=10}else{var r=m,e=c>>5,a=32-(c&31),s=0;h+=4,this.cycles+=8}this.indirect=x;let v=n+((A+(x?0:this.offset)&255)<<8);r*=2;let C=(this.regs[28]&3)+(s?4:0),y=x&&(this.regs[28]&16)!=0;y&&(a*=2);for(var o=0;o<a;o++){let p=this.readDMA(y?v+(o>>1):v+o);if(x){let u=(this.regs[20]+this.offset<<8)+p;y&&o&1&&(u++,this.cycles-=3),p=this.readDMA(u)}switch(C){case 0:for(let u=0;u<4;u++){var l=p>>6&3;l>0&&(this.pixels[r]=this.pixels[r+1]=this.regs[(e<<2)+l]),p<<=2,r=r+2&511}break;case 2:case 3:for(let u=0;u<8;u++){var l=p&128?1:0;l>0&&(this.pixels[r]=this.regs[(e<<2)+l]),p<<=1,r=r+1&511}break}}}while(this.cycles<b);this.offset-=1}return this.cycles}doInterrupt(){return this.dli&&this.offset<0?(this.dli=!1,!0):!1}static stateToLongString(t){let e="";return e+=M(t.regs,0,32),e+=`
|
||||
DLL: $`+g((t.regs[12]<<8)+t.regs[16],4)+" @ $"+g(t.dll,4),e+=`
|
||||
DL: $`+g(t.dlstart,4),e+=`
|
||||
Offset: `+t.offset,e+=`
|
||||
DLI? `+t.dli,e}},R=class extends F{constructor(){super();this.cpuFrequency=1789772;this.canvasWidth=320;this.numTotalScanlines=D;this.numVisibleScanlines=K;this.defaultROMSize=49152;this.cpuCyclesPerLine=113.5;this.sampleRate=$;this.ram=new Uint8Array(4096);this.regs6532=new Uint8Array(4);this.piatimer=0;this.timerinterval=1;this.tia=new T;this.maria=new k;this.lastFrameCycles=0;this.xtracyc=0;this.cpu=new H,this.read=I([[8,13,15,t=>(this.xtracyc++,this.readInput(t))],[0,31,31,t=>(this.xtracyc++,this.tia.read(t))],[32,63,31,t=>this.maria.read(t)],[64,255,255,t=>this.ram[t+2048]],[256,319,255,t=>this.read(t)],[320,511,511,t=>this.ram[t+2048]],[640,767,127,t=>(this.xtracyc++,this.readPIA(t))],[6144,10239,65535,t=>this.ram[t-6144]],[10240,16383,2047,t=>this.read(t|8192)],[16384,65535,65535,t=>this.rom?this.rom[t-16384]:0],[0,65535,65535,t=>this.probe&&this.probe.logIllegal(t)]]),this.write=I([[21,26,31,(t,e)=>{this.xtracyc++,this.pokey1.setTIARegister(t,e)}],[0,31,31,(t,e)=>{this.xtracyc++,this.tia.write(t,e)}],[32,63,31,(t,e)=>{this.maria.write(t,e)}],[64,255,255,(t,e)=>{this.ram[t+2048]=e}],[256,319,255,(t,e)=>{this.write(t,e)}],[320,511,511,(t,e)=>{this.ram[t+2048]=e}],[640,767,127,(t,e)=>{this.xtracyc++,this.writePIA(t,e)}],[6144,10239,65535,(t,e)=>{this.ram[t-6144]=e}],[10240,16383,2047,(t,e)=>{this.write(t|8192,e)}],[49151,49151,65535,(t,e)=>{}],[0,65535,65535,(t,e)=>{this.probe&&this.probe.logIllegal(t)}]]),this.connectCPUMemoryBus(this),this.dmaBus=this.probeDMABus(this),this.handler=O(this.inputs,X),this.pokey1=new W,this.audioadapter=new N(this.pokey1,Y,$)}readConst(t){let e=this.probe;this.probe=null;let a=this.read(t);return this.probe=e,a}readInput(t){switch(t){case 12:return~this.inputs[8]&128;case 13:return~this.inputs[9]&128;default:return this.inputs[t]|0}}readPIA(t){switch(t){case 0:case 2:return this.inputs[t];case 1:case 3:return this.regs6532[t];case 4:return this.getPIATimerValue();default:return 0}}writePIA(t,e){switch(t){case 0:case 1:case 2:case 3:this.regs6532[t]=e;return;case 20:this.setPIATimer(e,0);return;case 21:this.setPIATimer(e,3);return;case 22:this.setPIATimer(e,6);return;case 23:this.setPIATimer(e,10);return;case 24:this.setPIATimer(e,6);return}}setPIATimer(t,e){this.piatimer=t+1<<e,this.timerinterval=e}getPIATimerValue(){let t=this.piatimer;return t>0?t>>this.timerinterval:t&255}advanceCPU(){var t=super.advanceCPU();return this.tickPIATimer(t),this.xtracyc&&(t+=this.xtracyc,this.tickClocks(this.xtracyc),this.xtracyc=0),t}tickClocks(t){this.probe.logClocks(t),this.tickPIATimer(t)}tickPIATimer(t){this.piatimer=Math.max(-256,this.piatimer-t)}advanceFrame(t){var e=this.pixels,a=0,r,s=0,o=0,l=0;this.probe.logNewFrame();for(var i=0;i<D;i++){this.scanline=i;var h=i<K;for(this.maria.setVBLANK(!h),this.maria.WSYNC=0;s<j&&!this.maria.WSYNC;){if(t&&t()){t=null,i=999;break}s+=this.advanceCPU()<<2,l++}if(h){let c=this.maria.doDMA(this.dmaBus);if(this.tickClocks(c>>2),s+=c,e)for(var n=0;n<320;n++)e[a++]=V[this.maria.pixels[n]]}for((h||i==D-1)&&this.maria.doInterrupt()&&(this.probe.logInterrupt(0),this.cpu.NMI());s<b;){if(this.maria.WSYNC){this.probe.logWait(0),this.tickClocks(b-s>>2),s=b;break}if(t&&t()){t=null,i=999;break}s+=this.advanceCPU()<<2,l++}this.audio&&this.audioadapter.generate(this.audio),s-=b,o+=s,this.probe.logNewScanline()}return this.lastFrameCycles=o,l}getRasterX(){return this.lastFrameCycles%b}getRasterY(){return this.scanline}loadROM(t){t.length==49280&&(t=t.slice(128)),this.rom=E(t,this.defaultROMSize,!0)}reset(){super.reset(),this.tia.reset(),this.maria.reset(),this.inputs.fill(0),this.inputs[d]=255,this.inputs[L]=1+2+8}readAddress(t){return this.read(t)|0}loadState(t){this.cpu.loadState(t.c),this.ram.set(t.ram),this.tia.loadState(t.tia),this.maria.loadState(t.maria),this.regs6532.set(t.regs6532),this.piatimer=t.pia.timer,this.timerinterval=t.pia.interval,this.loadControlsState(t)}saveState(){return{c:this.cpu.saveState(),ram:this.ram.slice(0),tia:this.tia.saveState(),maria:this.maria.saveState(),regs6532:this.regs6532.slice(0),inputs:this.inputs.slice(0),pia:{timer:this.piatimer,interval:this.timerinterval}}}loadControlsState(t){this.inputs.set(t.inputs)}saveControlsState(){return{inputs:this.inputs.slice(0)}}getDebugCategories(){return["CPU","Stack","TIA","MARIA"]}getDebugInfo(t,e){switch(t){case"TIA":return T.stateToLongString(e.tia);case"MARIA":return k.stateToLongString(e.maria)+`
|
||||
Scanline: `+this.scanline}}getDebugDisplayLists(){let t={},e=this.maria.getDLLStart(),a=0;for(;a<240;){let r=this.readConst(e),s=r&15,o=(r&64)!=0,l=(r&32)!=0,i=(this.readConst(e+1)<<8)+this.readConst(e+2);e=e+3&65535;let h=(this.readConst(e)&128)!=0,n="DL $"+g(i,4)+" "+a+"-"+(a+s);o&&(n+=" H16"),l&&(n+=" H8"),h&&(n+=" DLI"),t[n]={$$:this._readDebugDisplayList(i)},a+=s+1}return t}_readDebugDisplayList(t){return()=>this.readDebugDisplayList(t)}readDebugDisplayList(t){let e=[],a=t&65280,r=t&255;do{let h=this.readConst(a+(r+0&511)),n=this.readConst(a+(r+1&511));if(n==0)break;let c=this.readConst(a+(r+2&511)),A=this.readConst(a+(r+3&511)),m=!1,x="";if((n&31)==0){var s=A>>5,o=32-(A&31),l=this.readConst(a+(r+4&511)),i=n&128;m=(n&32)!=0,r+=5,x+="X="+l+" W="+o+" P="+s+" "+(i?"WRITE":"")}else{var l=A,s=n>>5,o=32-(n&31),i=0;r+=4,x+="X="+l+" W="+o+" P="+s}let v=h+((c+(m?0:this.maria.offset)&255)<<8),C=(this.maria.regs[28]&3)+(i?4:0),y=m&&(this.maria.regs[28]&16)!=0;C&&(x+=" READMODE="+C),y&&(x+=" DBL"),m&&(x+=" CHR=$"+g(this.maria.regs[20]+this.maria.offset&255)+"xx"),x=" $"+g(v,4)+" "+x,e.push(x)}while(r<512);return e}},V=new Uint32Array(256);for(S=0;S<256;S++)V[S]=_(S);var S;var Q=[{id:"sprites.dasm",name:"Sprites (ASM)"},{id:"wsync.c",name:"WSYNC"},{id:"sprites.c",name:"Double Buffering"},{id:"scroll.c",name:"Scrolling"}],G=class extends z{constructor(){super(...arguments);this.getMemoryMap=function(){return{main:[{name:"TIA",start:0,size:32,type:"io"},{name:"MARIA",start:32,size:32,type:"io"},{name:"RAM (6166 Block 0)",start:64,size:192,type:"ram"},{name:"RAM (6166 Block 1)",start:320,size:192,type:"ram"},{name:"PIA",start:640,size:24,type:"io"},{name:"RAM",start:6144,size:4096,type:"ram"},{name:"Cartridge ROM",start:16384,size:49152,type:"rom"}]}}}newMachine(){return new R}getPresets(){return Q}getDefaultExtension(){return".c"}readAddress(t){return this.machine.readConst(t)}getROMExtension(){return".a78"}getDebugTree(){let t=super.getDebugTree();return t.display_list=this.machine.getDebugDisplayLists(),t}};B.atari7800=G;
|
||||
//# sourceMappingURL=atari7800-5KKGRW7B.js.map
|
7
gen/atari7800-5KKGRW7B.js.map
Normal file
7
gen/atari7800-5KKGRW7B.js.map
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,7 +0,0 @@
|
|||
import{I as W,h as E,k as N,p as F,t as _}from"./chunk-HB3LWF25.js";import{$ as w,J as k,U as M,V as r,W as I,Y as T,_ as U,da as O,g as b}from"./chunk-ATS7PSQG.js";import"./chunk-5XVCUSSZ.js";var h=0,P=2,A=8,j=T([[r.A,A+0,128],[r.B,A+1,128],[r.SELECT,P,-2],[r.START,P,-1],[r.UP,h,-16],[r.DOWN,h,-32],[r.LEFT,h,-64],[r.RIGHT,h,-128],[r.P2_A,A+2,128],[r.P2_B,A+3,128],[r.P2_UP,h,-1],[r.P2_DOWN,h,-2],[r.P2_LEFT,h,-4],[r.P2_RIGHT,h,-8]]);var S=262,z=258-16,u=454,q=28,K=2,Y=S*60*K,L=class{constructor(){this.regs=new Uint8Array(32)}reset(){this.regs.fill(0)}read(t){return this.regs[t]|0}write(t,e){this.regs[t]=e}saveState(){return{regs:this.regs.slice(0)}}loadState(t){for(let e=0;e<32;e++)this.write(e,t.regs[e])}static stateToLongString(t){let e="";return e+=M(t.regs,0,32),e}},R=class{constructor(){this.cycles=0;this.regs=new Uint8Array(32);this.offset=-1;this.dll=0;this.dlstart=0;this.dli=!1;this.h16=!1;this.h8=!1;this.pixels=new Uint8Array(320);this.WSYNC=0}reset(){this.regs.fill(0)}read(t){return this.regs[t]|0}write(t,e){this.regs[t]=e,t==4&&this.WSYNC++}saveState(){return{regs:this.regs.slice(0),offset:this.offset,dll:this.dll,dlstart:this.dlstart,dli:this.dli,h16:this.h16,h8:this.h8}}loadState(t){for(let e=0;e<32;e++)this.write(e,t.regs[e]|0);this.offset=t.offset|0,this.dll=t.dll|0,this.dlstart=t.dlstart|0,this.dli=!!t.dli,this.h16=!!t.h16,this.h8=!!t.h8}isDMAEnabled(){return(this.regs[28]&96)==64}getDLLStart(){return(this.regs[12]<<8)+this.regs[16]}getCharBaseAddress(){return(this.regs[20]<<8)+this.offset}setVBLANK(t){t?(this.regs[8]|=128,this.offset=-1,this.dll=this.getDLLStart(),this.dli=this.bus&&(this.bus.read(this.dll)&128)!=0):this.regs[8]&=~128}readDLLEntry(t){if(this.dll>=16384)return;let e=t.read(this.dll);this.offset=e&15,this.h16=(e&64)!=0,this.h8=(e&32)!=0,this.dlstart=(t.read(this.dll+1)<<8)+t.read(this.dll+2),this.dll=this.dll+3&65535,this.dli=(t.read(this.dll)&128)!=0}isHoley(t){return!!(t&32768&&(this.h16&&t&4096||this.h8&&t&2048))}readDMA(t){return this.isHoley(t)?0:(this.cycles+=3,this.bus.read(t))}doDMA(t){if(this.bus=t,this.cycles=0,this.pixels.fill(this.regs[0]),this.isDMAEnabled()){this.cycles+=16,this.offset<0&&this.readDLLEntry(t);let s=this.dlstart&65280,a=this.dlstart&255;do{let m=t.read(s+(a+0&511)),f=t.read(s+(a+1&511));if(f==0||s>=16384)break;let G=t.read(s+(a+2&511)),v=t.read(s+(a+3&511)),p=!1;if((f&31)==0){var e=v>>5,c=32-(v&31),n=t.read(s+(a+4&511)),i=f&128;p=(f&32)!=0,a+=5,this.cycles+=10}else{var n=v,e=f>>5,c=32-(f&31),i=0;a+=4,this.cycles+=8}let D=m+((G+(p?0:this.offset)&255)<<8);n*=2;let $=(this.regs[28]&3)+(i?4:0),C=p&&(this.regs[28]&16)!=0;C&&(c*=2);for(var o=0;o<c;o++){let d=this.readDMA(C?D+(o>>1):D+o);if(p){let x=(this.regs[20]+this.offset<<8)+d;C&&o&1&&(x++,this.cycles-=3),d=this.readDMA(x)}switch($){case 0:for(let x=0;x<4;x++){var l=d>>6&3;l>0&&(this.pixels[n]=this.pixels[n+1]=this.regs[(e<<2)+l]),d<<=2,n=n+2&511}break;case 2:case 3:for(let x=0;x<8;x++){var l=d&128?1:0;l>0&&(this.pixels[n]=this.regs[(e<<2)+l]),d<<=1,n=n+1&511}break}}}while(this.cycles<u);this.offset-=1}return this.cycles}doInterrupt(){return this.dli&&this.offset<0?(this.dli=!1,!0):!1}static stateToLongString(t){let e="";return e+=M(t.regs,0,32),e+=`
|
||||
DLL: $`+b((t.regs[12]<<8)+t.regs[16],4)+" @ $"+b(t.dll,4),e+=`
|
||||
DL: $`+b(t.dlstart,4),e+=`
|
||||
Offset: `+t.offset,e+=`
|
||||
DLI? `+t.dli,e}},B=class extends F{constructor(){super();this.cpuFrequency=1789772;this.canvasWidth=320;this.numTotalScanlines=S;this.numVisibleScanlines=z;this.defaultROMSize=49152;this.cpuCyclesPerLine=113.5;this.sampleRate=Y;this.ram=new Uint8Array(4096);this.regs6532=new Uint8Array(4);this.tia=new L;this.maria=new R;this.lastFrameCycles=0;this.xtracyc=0;this.cpu=new _,this.read=w([[8,13,15,t=>(this.xtracyc++,this.readInput(t))],[0,31,31,t=>(this.xtracyc++,this.tia.read(t))],[32,63,31,t=>this.maria.read(t)],[64,255,255,t=>this.ram[t+2048]],[256,319,255,t=>this.read(t)],[320,511,511,t=>this.ram[t+2048]],[640,767,3,t=>(this.xtracyc++,this.inputs[t])],[6144,10239,65535,t=>this.ram[t-6144]],[10240,16383,2047,t=>this.read(t|8192)],[16384,65535,65535,t=>this.rom?this.rom[t-16384]:0],[0,65535,65535,t=>this.probe&&this.probe.logIllegal(t)]]),this.write=w([[21,26,31,(t,e)=>{this.xtracyc++,this.pokey1.setTIARegister(t,e)}],[0,31,31,(t,e)=>{this.xtracyc++,this.tia.write(t,e)}],[32,63,31,(t,e)=>{this.maria.write(t,e)}],[64,255,255,(t,e)=>{this.ram[t+2048]=e}],[256,319,255,(t,e)=>{this.write(t,e)}],[320,511,511,(t,e)=>{this.ram[t+2048]=e}],[640,767,3,(t,e)=>{this.xtracyc++,this.regs6532[t]=e}],[6144,10239,65535,(t,e)=>{this.ram[t-6144]=e}],[10240,16383,2047,(t,e)=>{this.write(t|8192,e)}],[49151,49151,65535,(t,e)=>{}],[0,65535,65535,(t,e)=>{this.probe&&this.probe.logIllegal(t)}]]),this.connectCPUMemoryBus(this),this.dmaBus=this.probeDMABus(this),this.handler=I(this.inputs,j),this.pokey1=new E,this.audioadapter=new N(this.pokey1,K,Y)}readConst(t){let e=this.probe;this.probe=null;let c=this.read(t);return this.probe=e,c}readInput(t){switch(t){case 12:return~this.inputs[8]&128;case 13:return~this.inputs[9]&128;default:return this.inputs[t]|0}}advanceCPU(){var t=super.advanceCPU();return this.xtracyc&&(t+=this.xtracyc,this.probe.logClocks(this.xtracyc),this.xtracyc=0),t}advanceFrame(t){var e=this.pixels,c=0,n,i=0,o=0,l=0;this.probe.logNewFrame();for(var s=0;s<S;s++){this.scanline=s;var a=s<z;for(this.maria.setVBLANK(!a),this.maria.WSYNC=0;i<q&&!this.maria.WSYNC;){if(t&&t()){t=null,s=999;break}i+=this.advanceCPU()<<2,l++}if(a){let f=this.maria.doDMA(this.dmaBus);if(this.probe.logClocks(f>>2),i+=f,e)for(var m=0;m<320;m++)e[c++]=H[this.maria.pixels[m]]}for((a||s==S-1)&&this.maria.doInterrupt()&&(this.probe.logInterrupt(0),this.cpu.NMI());i<u;){if(this.maria.WSYNC){this.probe.logWait(0),this.probe.logClocks(u-i>>2),i=u;break}if(t&&t()){t=null,s=999;break}i+=this.advanceCPU()<<2,l++}this.audio&&this.audioadapter.generate(this.audio),i-=u,o+=i,this.probe.logNewScanline()}return this.lastFrameCycles=o,l}getRasterX(){return this.lastFrameCycles%u}getRasterY(){return this.scanline}loadROM(t){t.length==49280&&(t=t.slice(128)),this.rom=U(t,this.defaultROMSize,!0)}reset(){super.reset(),this.tia.reset(),this.maria.reset(),this.inputs.fill(0),this.inputs[h]=255,this.inputs[P]=1+2+8}readAddress(t){return this.read(t)|0}loadState(t){this.cpu.loadState(t.c),this.ram.set(t.ram),this.tia.loadState(t.tia),this.maria.loadState(t.maria),this.regs6532.set(t.regs6532),this.loadControlsState(t)}saveState(){return{c:this.cpu.saveState(),ram:this.ram.slice(0),tia:this.tia.saveState(),maria:this.maria.saveState(),regs6532:this.regs6532.slice(0),inputs:this.inputs.slice(0)}}loadControlsState(t){this.inputs.set(t.inputs)}saveControlsState(){return{inputs:this.inputs.slice(0)}}getDebugCategories(){return["CPU","Stack","TIA","MARIA"]}getDebugInfo(t,e){switch(t){case"TIA":return L.stateToLongString(e.tia);case"MARIA":return R.stateToLongString(e.maria)+`
|
||||
Scanline: `+this.scanline}}},H=new Uint32Array(256);for(g=0;g<256;g++)H[g]=O(g);var g;var X=[{id:"sprites.dasm",name:"Sprites (ASM)"},{id:"wsync.c",name:"WSYNC"},{id:"sprites.c",name:"Double Buffering"},{id:"scroll.c",name:"Scrolling"}],V=class extends W{constructor(){super(...arguments);this.getMemoryMap=function(){return{main:[{name:"TIA",start:0,size:32,type:"io"},{name:"MARIA",start:32,size:32,type:"io"},{name:"RAM (6166 Block 0)",start:64,size:192,type:"ram"},{name:"RAM (6166 Block 1)",start:320,size:192,type:"ram"},{name:"PIA",start:640,size:24,type:"io"},{name:"RAM",start:6144,size:4096,type:"ram"},{name:"Cartridge ROM",start:16384,size:49152,type:"rom"}]}}}newMachine(){return new B}getPresets(){return X}getDefaultExtension(){return".c"}readAddress(t){return this.machine.readConst(t)}getROMExtension(){return".a78"}};k.atari7800=V;
|
||||
//# sourceMappingURL=atari7800-CBAOXRGJ.js.map
|
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
10
gen/atari8-P6UBBVZK.js
Normal file
10
gen/atari8-P6UBBVZK.js
Normal file
File diff suppressed because one or more lines are too long
7
gen/atari8-P6UBBVZK.js.map
Normal file
7
gen/atari8-P6UBBVZK.js.map
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
File diff suppressed because one or more lines are too long
|
@ -1,2 +1,2 @@
|
|||
import{a as d}from"./chunk-3XE5YOCV.js";import{o as c}from"./chunk-R63KYPGV.js";import"./chunk-SXEVG2WS.js";import"./chunk-5SHCNQ2O.js";import"./chunk-Z2IKIN54.js";import{I as m,o as u,t as h}from"./chunk-HB3LWF25.js";import{$ as i,B as o,J as l,m as n}from"./chunk-ATS7PSQG.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-364UXGWH.js.map
|
||||
import{a as d}from"./chunk-3XE5YOCV.js";import{o as c}from"./chunk-MMLUO3PV.js";import"./chunk-SXEVG2WS.js";import"./chunk-5SHCNQ2O.js";import"./chunk-OCFU6ZP7.js";import{I as m,o as u,t as h}from"./chunk-HB3LWF25.js";import{$ as i,B as o,J as l,m as n}from"./chunk-ATS7PSQG.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-GTEJKQ3P.js.map
|
|
@ -1,2 +1,2 @@
|
|||
import{b as w,c as x}from"./chunk-Z2IKIN54.js";import{D as v,J as c,k as m,l as u,x as g,z as p}from"./chunk-ATS7PSQG.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-OCFU6ZP7.js";import{D as v,J as c,k as m,l as u,x as g,z as p}from"./chunk-ATS7PSQG.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
|
||||
|
|
|
@ -29,11 +29,14 @@ const Atari7800_KEYCODE_MAP = (0, emu_1.makeKeycodeMap)([
|
|||
]);
|
||||
// http://www.ataripreservation.org/websites/freddy.offenga/megazine/ISSUE5-PALNTSC.html
|
||||
// http://7800.8bitdev.org/index.php/7800_Software_Guide#APPENDIX_4:_FRAME_TIMING
|
||||
// https://forums.atariage.com/topic/224025-7800-hardware-facts/
|
||||
const CLK = 3579545;
|
||||
const linesPerFrame = 262;
|
||||
const linesPerFrame = 263;
|
||||
const numVisibleLines = 258 - 16;
|
||||
const colorClocksPerLine = 454; // 456?
|
||||
const colorClocksPerLine = 451; // 451? 452? 456?
|
||||
const colorClocksPreDMA = 28;
|
||||
const colorClocksShutdownOther = 16;
|
||||
const colorClocksShutdownLast = 24;
|
||||
const audioOversample = 2;
|
||||
const audioSampleRate = linesPerFrame * 60 * audioOversample;
|
||||
// TIA chip
|
||||
|
@ -76,6 +79,7 @@ class MARIA {
|
|||
this.dli = false;
|
||||
this.h16 = false;
|
||||
this.h8 = false;
|
||||
this.indirect = false;
|
||||
this.pixels = new Uint8Array(320);
|
||||
this.WSYNC = 0;
|
||||
}
|
||||
|
@ -101,6 +105,7 @@ class MARIA {
|
|||
dli: this.dli,
|
||||
h16: this.h16,
|
||||
h8: this.h8,
|
||||
indirect: this.indirect,
|
||||
};
|
||||
}
|
||||
loadState(s) {
|
||||
|
@ -112,6 +117,7 @@ class MARIA {
|
|||
this.dli = !!s.dli;
|
||||
this.h16 = !!s.h16;
|
||||
this.h8 = !!s.h8;
|
||||
this.indirect = !!s.indirect;
|
||||
}
|
||||
isDMAEnabled() {
|
||||
return (this.regs[0x1c] & 0x60) == 0x40;
|
||||
|
@ -148,6 +154,8 @@ class MARIA {
|
|||
this.dli = (bus.read(this.dll) & 0x80) != 0; // DLI flag is from next DLL entry
|
||||
}
|
||||
isHoley(a) {
|
||||
if (this.indirect)
|
||||
return false;
|
||||
if (a & 0x8000) {
|
||||
if (this.h16 && (a & 0x1000))
|
||||
return true;
|
||||
|
@ -157,8 +165,9 @@ class MARIA {
|
|||
return false;
|
||||
}
|
||||
readDMA(a) {
|
||||
if (this.isHoley(a))
|
||||
if (this.isHoley(a)) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
this.cycles += 3;
|
||||
return this.bus.read(a);
|
||||
|
@ -169,7 +178,8 @@ class MARIA {
|
|||
this.cycles = 0;
|
||||
this.pixels.fill(this.regs[0x0]);
|
||||
if (this.isDMAEnabled()) {
|
||||
this.cycles += 16; // TODO: last line in zone gets additional 8 cycles
|
||||
// last line in zone gets additional 8 cycles
|
||||
this.cycles += this.offset == 0 ? colorClocksShutdownLast : colorClocksShutdownOther;
|
||||
// time for a new DLL entry?
|
||||
if (this.offset < 0) {
|
||||
this.readDLLEntry(bus);
|
||||
|
@ -209,6 +219,7 @@ class MARIA {
|
|||
dlofs += 4;
|
||||
this.cycles += 8;
|
||||
}
|
||||
this.indirect = indirect;
|
||||
let gfxadr = b0 + (((b2 + (indirect ? 0 : this.offset)) & 0xff) << 8);
|
||||
xpos *= 2;
|
||||
// copy graphics data (direct)
|
||||
|
@ -292,6 +303,8 @@ class Atari7800 extends devices_1.BasicMachine {
|
|||
this.sampleRate = audioSampleRate;
|
||||
this.ram = new Uint8Array(0x1000);
|
||||
this.regs6532 = new Uint8Array(4);
|
||||
this.piatimer = 0;
|
||||
this.timerinterval = 1;
|
||||
this.tia = new TIA();
|
||||
this.maria = new MARIA();
|
||||
this.lastFrameCycles = 0;
|
||||
|
@ -304,7 +317,7 @@ class Atari7800 extends devices_1.BasicMachine {
|
|||
[0x0040, 0x00ff, 0xff, (a) => { return this.ram[a + 0x800]; }],
|
||||
[0x0100, 0x013f, 0xff, (a) => { return this.read(a); }],
|
||||
[0x0140, 0x01ff, 0x1ff, (a) => { return this.ram[a + 0x800]; }],
|
||||
[0x0280, 0x02ff, 0x3, (a) => { this.xtracyc++; return this.inputs[a]; }],
|
||||
[0x0280, 0x02ff, 0x7f, (a) => { this.xtracyc++; return this.readPIA(a); }],
|
||||
[0x1800, 0x27ff, 0xffff, (a) => { return this.ram[a - 0x1800]; }],
|
||||
[0x2800, 0x3fff, 0x7ff, (a) => { return this.read(a | 0x2000); }],
|
||||
[0x4000, 0xffff, 0xffff, (a) => { return this.rom ? this.rom[a - 0x4000] : 0; }],
|
||||
|
@ -317,7 +330,7 @@ class Atari7800 extends devices_1.BasicMachine {
|
|||
[0x0040, 0x00ff, 0xff, (a, v) => { this.ram[a + 0x800] = v; }],
|
||||
[0x0100, 0x013f, 0xff, (a, v) => { this.write(a, v); }],
|
||||
[0x0140, 0x01ff, 0x1ff, (a, v) => { this.ram[a + 0x800] = v; }],
|
||||
[0x0280, 0x02ff, 0x3, (a, v) => { this.xtracyc++; this.regs6532[a] = v; /*TODO*/ }],
|
||||
[0x0280, 0x02ff, 0x7f, (a, v) => { this.xtracyc++; this.writePIA(a, v); }],
|
||||
[0x1800, 0x27ff, 0xffff, (a, v) => { this.ram[a - 0x1800] = v; }],
|
||||
[0x2800, 0x3fff, 0x7ff, (a, v) => { this.write(a | 0x2000, v); }],
|
||||
[0xbfff, 0xbfff, 0xffff, (a, v) => { }],
|
||||
|
@ -344,15 +357,75 @@ class Atari7800 extends devices_1.BasicMachine {
|
|||
default: return this.inputs[a] | 0;
|
||||
}
|
||||
}
|
||||
readPIA(a) {
|
||||
switch (a) {
|
||||
case 0x0:
|
||||
case 0x2:
|
||||
return this.inputs[a]; // SWCHA, SWCHB
|
||||
case 0x1:
|
||||
case 0x3:
|
||||
return this.regs6532[a]; // CTLSWA, CTLSWB
|
||||
case 0x4:
|
||||
return this.getPIATimerValue(); // INTIM
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
writePIA(a, v) {
|
||||
switch (a) {
|
||||
case 0x0:
|
||||
case 0x1:
|
||||
case 0x2:
|
||||
case 0x3:
|
||||
this.regs6532[a] = v;
|
||||
return;
|
||||
case 0x14:
|
||||
this.setPIATimer(v, 0);
|
||||
return; // TIM1T
|
||||
case 0x15:
|
||||
this.setPIATimer(v, 3);
|
||||
return; // TIM8T
|
||||
case 0x16:
|
||||
this.setPIATimer(v, 6);
|
||||
return; // TIM64T
|
||||
case 0x17:
|
||||
this.setPIATimer(v, 10);
|
||||
return; // T1024T
|
||||
case 0x18:
|
||||
this.setPIATimer(v, 6);
|
||||
return; // TIM64TI (TODO)
|
||||
}
|
||||
}
|
||||
setPIATimer(v, shift) {
|
||||
this.piatimer = (v + 1) << shift;
|
||||
this.timerinterval = shift;
|
||||
}
|
||||
getPIATimerValue() {
|
||||
let t = this.piatimer;
|
||||
if (t > 0) {
|
||||
return t >> this.timerinterval;
|
||||
}
|
||||
else {
|
||||
return t & 0xff;
|
||||
}
|
||||
}
|
||||
advanceCPU() {
|
||||
var clk = super.advanceCPU();
|
||||
this.tickPIATimer(clk); // TODO?
|
||||
if (this.xtracyc) {
|
||||
clk += this.xtracyc;
|
||||
this.probe.logClocks(this.xtracyc);
|
||||
this.tickClocks(this.xtracyc);
|
||||
this.xtracyc = 0;
|
||||
}
|
||||
return clk;
|
||||
}
|
||||
tickClocks(clocks) {
|
||||
this.probe.logClocks(clocks);
|
||||
this.tickPIATimer(clocks);
|
||||
}
|
||||
tickPIATimer(clocks) {
|
||||
this.piatimer = Math.max(-256, this.piatimer - clocks);
|
||||
}
|
||||
advanceFrame(trap) {
|
||||
var idata = this.pixels;
|
||||
var iofs = 0;
|
||||
|
@ -384,7 +457,7 @@ class Atari7800 extends devices_1.BasicMachine {
|
|||
if (visible) {
|
||||
// do DMA for scanline?
|
||||
let dmaClocks = this.maria.doDMA(this.dmaBus);
|
||||
this.probe.logClocks(dmaClocks >> 2); // TODO: logDMA
|
||||
this.tickClocks(dmaClocks >> 2); // TODO: logDMA
|
||||
mc += dmaClocks;
|
||||
// copy line to frame buffer
|
||||
if (idata) {
|
||||
|
@ -402,7 +475,7 @@ class Atari7800 extends devices_1.BasicMachine {
|
|||
while (mc < colorClocksPerLine) {
|
||||
if (this.maria.WSYNC) {
|
||||
this.probe.logWait(0);
|
||||
this.probe.logClocks((colorClocksPerLine - mc) >> 2);
|
||||
this.tickClocks((colorClocksPerLine - mc) >> 2);
|
||||
mc = colorClocksPerLine;
|
||||
break;
|
||||
}
|
||||
|
@ -453,6 +526,8 @@ class Atari7800 extends devices_1.BasicMachine {
|
|||
this.tia.loadState(state.tia);
|
||||
this.maria.loadState(state.maria);
|
||||
this.regs6532.set(state.regs6532);
|
||||
this.piatimer = state.pia.timer;
|
||||
this.timerinterval = state.pia.interval;
|
||||
this.loadControlsState(state);
|
||||
}
|
||||
saveState() {
|
||||
|
@ -462,7 +537,8 @@ class Atari7800 extends devices_1.BasicMachine {
|
|||
tia: this.tia.saveState(),
|
||||
maria: this.maria.saveState(),
|
||||
regs6532: this.regs6532.slice(0),
|
||||
inputs: this.inputs.slice(0)
|
||||
inputs: this.inputs.slice(0),
|
||||
pia: { timer: this.piatimer, interval: this.timerinterval }
|
||||
};
|
||||
}
|
||||
loadControlsState(state) {
|
||||
|
@ -483,6 +559,83 @@ class Atari7800 extends devices_1.BasicMachine {
|
|||
//default: return super.getDebugInfo(category, state);
|
||||
}
|
||||
}
|
||||
getDebugDisplayLists() {
|
||||
// return display list in human-readable JSON object
|
||||
let display_lists = {};
|
||||
let dll_ofs = this.maria.getDLLStart();
|
||||
// read the address of each DLL entry
|
||||
let y = 0;
|
||||
while (y < 240) {
|
||||
let x = this.readConst(dll_ofs);
|
||||
let offset = (x & 0xf);
|
||||
let h16 = (x & 0x40) != 0;
|
||||
let h8 = (x & 0x20) != 0;
|
||||
let dlstart = (this.readConst(dll_ofs + 1) << 8) + this.readConst(dll_ofs + 2);
|
||||
dll_ofs = (dll_ofs + 3) & 0xffff; // TODO: can also only cross 1 page?
|
||||
let dli = (this.readConst(dll_ofs) & 0x80) != 0; // DLI flag is from next DLL entry
|
||||
let title = "DL $" + (0, util_1.hex)(dlstart, 4) + " " + y + "-" + (y + offset);
|
||||
if (h16)
|
||||
title += " H16";
|
||||
if (h8)
|
||||
title += " H8";
|
||||
if (dli)
|
||||
title += " DLI";
|
||||
display_lists[title] = { "$$": this._readDebugDisplayList(dlstart) };
|
||||
y += offset + 1;
|
||||
}
|
||||
return display_lists;
|
||||
}
|
||||
_readDebugDisplayList(dlstart) {
|
||||
return () => this.readDebugDisplayList(dlstart);
|
||||
}
|
||||
readDebugDisplayList(dlstart) {
|
||||
let display_list = [];
|
||||
let dlhi = dlstart & 0xff00;
|
||||
let dlofs = dlstart & 0xff;
|
||||
do {
|
||||
// read DL entry
|
||||
let b0 = this.readConst(dlhi + ((dlofs + 0) & 0x1ff));
|
||||
let b1 = this.readConst(dlhi + ((dlofs + 1) & 0x1ff));
|
||||
if (b1 == 0)
|
||||
break; // end of DL
|
||||
// display lists must be in RAM (TODO: probe?)
|
||||
let b2 = this.readConst(dlhi + ((dlofs + 2) & 0x1ff));
|
||||
let b3 = this.readConst(dlhi + ((dlofs + 3) & 0x1ff));
|
||||
// extended header?
|
||||
let indirect = false;
|
||||
let description = "";
|
||||
if ((b1 & 31) == 0) {
|
||||
var pal = b3 >> 5;
|
||||
var width = 32 - (b3 & 31);
|
||||
var xpos = this.readConst(dlhi + ((dlofs + 4) & 0x1ff));
|
||||
var writemode = b1 & 0x80;
|
||||
indirect = (b1 & 0x20) != 0;
|
||||
dlofs += 5;
|
||||
description += "X=" + xpos + " W=" + width + " P=" + pal + " " + (writemode ? "WRITE" : "");
|
||||
}
|
||||
else {
|
||||
// direct mode
|
||||
var xpos = b3;
|
||||
var pal = b1 >> 5;
|
||||
var width = 32 - (b1 & 31);
|
||||
var writemode = 0;
|
||||
dlofs += 4;
|
||||
description += "X=" + xpos + " W=" + width + " P=" + pal;
|
||||
}
|
||||
let gfxadr = b0 + (((b2 + (indirect ? 0 : this.maria.offset)) & 0xff) << 8);
|
||||
let readmode = (this.maria.regs[0x1c] & 0x3) + (writemode ? 4 : 0);
|
||||
let dbl = indirect && (this.maria.regs[0x1c] & 0x10) != 0;
|
||||
if (readmode)
|
||||
description += " READMODE=" + readmode;
|
||||
if (dbl)
|
||||
description += " DBL";
|
||||
if (indirect)
|
||||
description += " CHR=$" + (0, util_1.hex)((this.maria.regs[0x14] + this.maria.offset) & 0xff) + "xx";
|
||||
description = " $" + (0, util_1.hex)(gfxadr, 4) + " " + description;
|
||||
display_list.push(description);
|
||||
} while (dlofs < 0x200);
|
||||
return display_list;
|
||||
}
|
||||
}
|
||||
exports.Atari7800 = Atari7800;
|
||||
///
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -402,6 +402,58 @@ class Atari800 extends devices_1.BasicScanlineMachine {
|
|||
setPaddleInput(controller, value) {
|
||||
this.irq_pokey.pot_inputs[controller] = 255 - value;
|
||||
}
|
||||
getDebugDisplayList() {
|
||||
let pc = this.antic.getDlistAddr();
|
||||
const nextInsn = () => {
|
||||
let b = this.read(pc);
|
||||
pc = ((pc + 1) & 0x3ff) | (pc & ~0x3ff);
|
||||
return b;
|
||||
};
|
||||
let dlist = {};
|
||||
let y = 0;
|
||||
for (let i = 0; i < 256 && y < 240; i++) {
|
||||
let pc0 = pc;
|
||||
let op = nextInsn(); // get mode
|
||||
let mode = op & 0xf;
|
||||
let debugmsg = ""; // op=" + hex(op);
|
||||
let jmp = false;
|
||||
let lines;
|
||||
if (mode == 0) {
|
||||
lines = (((op >> 4) & 7) + 1);
|
||||
debugmsg += " blank=" + lines;
|
||||
}
|
||||
else {
|
||||
lines = antic_1.MODE_LINES[mode];
|
||||
debugmsg += " mode=" + (0, util_1.hex)(mode);
|
||||
debugmsg += " lines=" + lines;
|
||||
jmp = (op & ~0x40) == 0x01; // JMP insn?
|
||||
let lms = (op & 0x40) != 0 && (op & 0xf) != 0; // LMS insn?
|
||||
if (jmp && (op & 0x40)) {
|
||||
debugmsg += " JVB";
|
||||
}
|
||||
else if (jmp)
|
||||
debugmsg += " JMP";
|
||||
else if (lms)
|
||||
debugmsg += " LMS";
|
||||
if (this.antic.isPlayfieldDMAEnabled() && (jmp || lms)) {
|
||||
let dlarg_lo = nextInsn();
|
||||
let dlarg_hi = nextInsn();
|
||||
debugmsg += " $" + (0, util_1.hex)(dlarg_hi) + "" + (0, util_1.hex)(dlarg_lo);
|
||||
}
|
||||
if (op & 0x10) {
|
||||
debugmsg += " HSCROL";
|
||||
}
|
||||
if (op & 0x20) {
|
||||
debugmsg += " VSCROL";
|
||||
}
|
||||
}
|
||||
dlist["$" + (0, util_1.hex)(pc0) + " y=" + y] = debugmsg;
|
||||
if (jmp)
|
||||
break;
|
||||
y += lines;
|
||||
}
|
||||
return dlist;
|
||||
}
|
||||
}
|
||||
exports.Atari800 = Atari800;
|
||||
class Atari5200 extends Atari800 {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,6 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ANTIC = exports.MODE_SHIFT = void 0;
|
||||
exports.ANTIC = exports.MODE_SHIFT = exports.MODE_LINES = void 0;
|
||||
const emu_1 = require("../../common/emu");
|
||||
const util_1 = require("../../common/util");
|
||||
// ANTIC
|
||||
|
@ -38,7 +38,7 @@ const WSYNC_CYCLE = 212;
|
|||
const ANTIC_LEFT = 17 - 4; // gtia 34, 4 cycle delay
|
||||
const ANTIC_RIGHT = 110 - 4; // gtia 221, 4 cycle delay
|
||||
const LAST_DMA_H = 105; // last DMA cycle
|
||||
const MODE_LINES = [0, 0, 8, 10, 8, 16, 8, 16, 8, 4, 4, 2, 1, 2, 1, 1];
|
||||
exports.MODE_LINES = [0, 0, 8, 10, 8, 16, 8, 16, 8, 4, 4, 2, 1, 2, 1, 1];
|
||||
// how many bits before DMA clock repeats?
|
||||
const MODE_PERIOD = [0, 0, 2, 2, 2, 2, 4, 4, 8, 4, 4, 4, 4, 2, 2, 2];
|
||||
const MODE_YPERIOD = [0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 2, 1, 0, 0, 0, 0];
|
||||
|
@ -131,7 +131,7 @@ class ANTIC {
|
|||
this.dmaclock = 0;
|
||||
}
|
||||
else {
|
||||
this.linesleft = MODE_LINES[this.mode];
|
||||
this.linesleft = exports.MODE_LINES[this.mode];
|
||||
this.period = MODE_PERIOD[this.mode];
|
||||
if (this.jmp) {
|
||||
this.regs[DLISTL] = this.dlarg_lo;
|
||||
|
@ -187,8 +187,11 @@ class ANTIC {
|
|||
this.nmi();
|
||||
}
|
||||
}
|
||||
getDlistAddr() {
|
||||
return this.regs[DLISTL] + (this.regs[DLISTH] << 8);
|
||||
}
|
||||
nextInsn() {
|
||||
let pc = this.regs[DLISTL] + (this.regs[DLISTH] << 8);
|
||||
let pc = this.getDlistAddr();
|
||||
let b = this.read(pc);
|
||||
//console.log('nextInsn', hex(pc), hex(b), this.v);
|
||||
pc = ((pc + 1) & 0x3ff) | (pc & ~0x3ff);
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -31,6 +31,11 @@ class Atari7800Platform extends baseplatform_1.Base6502MachinePlatform {
|
|||
;
|
||||
readAddress(a) { return this.machine.readConst(a); }
|
||||
getROMExtension() { return ".a78"; }
|
||||
getDebugTree() {
|
||||
let tree = super.getDebugTree();
|
||||
tree['display_list'] = this.machine.getDebugDisplayLists();
|
||||
return tree;
|
||||
}
|
||||
}
|
||||
///
|
||||
emu_1.PLATFORMS['atari7800'] = Atari7800Platform;
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"version":3,"file":"atari7800.js","sourceRoot":"","sources":["../../src/platform/atari7800.ts"],"names":[],"mappings":";;AACA,oDAAiD;AACjD,yDAA2E;AAC3E,uCAA0C;AAE1C,IAAI,iBAAiB,GAAG;IACtB,EAAC,EAAE,EAAC,cAAc,EAAE,IAAI,EAAC,eAAe,EAAC;IACzC,EAAC,EAAE,EAAC,SAAS,EAAE,IAAI,EAAC,OAAO,EAAC;IAC5B,EAAC,EAAE,EAAC,WAAW,EAAE,IAAI,EAAC,kBAAkB,EAAC;IACzC,EAAC,EAAE,EAAC,UAAU,EAAE,IAAI,EAAC,WAAW,EAAC;CAClC,CAAC;AAEF,MAAM,iBAAkB,SAAQ,sCAAkC;IAAlE;;QAME,oDAAoD;QACpD,iBAAY,GAAG;YAAa,OAAO,EAAE,IAAI,EAAC;oBACtC,EAAC,IAAI,EAAC,KAAK,EAAC,KAAK,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC;oBAC3C,EAAC,IAAI,EAAC,OAAO,EAAC,KAAK,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC;oBAC7C,EAAC,IAAI,EAAC,oBAAoB,EAAC,KAAK,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC;oBAC3D,EAAC,IAAI,EAAC,oBAAoB,EAAC,KAAK,EAAC,KAAK,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC;oBAC5D,EAAC,IAAI,EAAC,KAAK,EAAC,KAAK,EAAC,KAAK,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC;oBAC5C,EAAC,IAAI,EAAC,KAAK,EAAC,KAAK,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,KAAK,EAAC;oBAChD,EAAC,IAAI,EAAC,eAAe,EAAC,KAAK,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,KAAK,EAAC;iBAC7D,EAAE,CAAA;QAAC,CAAC,CAAC;IAER,CAAC;IAfC,UAAU,KAAc,OAAO,IAAI,qBAAS,EAAE,CAAC,CAAC,CAAC;IACjD,UAAU,KAAc,OAAO,iBAAiB,CAAC,CAAC,CAAC;IACnD,mBAAmB,KAAK,OAAO,IAAI,CAAC,CAAC,CAAC;IAAA,CAAC;IACvC,WAAW,CAAC,CAAC,IAAW,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAW3D,eAAe,KAAK,OAAO,MAAM,CAAC,CAAC,CAAC;CACrC;AAED,GAAG;AAEH,eAAS,CAAC,WAAW,CAAC,GAAG,iBAAiB,CAAC"}
|
||||
{"version":3,"file":"atari7800.js","sourceRoot":"","sources":["../../src/platform/atari7800.ts"],"names":[],"mappings":";;AACA,oDAAiD;AACjD,yDAA2E;AAC3E,uCAA0C;AAE1C,IAAI,iBAAiB,GAAG;IACtB,EAAC,EAAE,EAAC,cAAc,EAAE,IAAI,EAAC,eAAe,EAAC;IACzC,EAAC,EAAE,EAAC,SAAS,EAAE,IAAI,EAAC,OAAO,EAAC;IAC5B,EAAC,EAAE,EAAC,WAAW,EAAE,IAAI,EAAC,kBAAkB,EAAC;IACzC,EAAC,EAAE,EAAC,UAAU,EAAE,IAAI,EAAC,WAAW,EAAC;CAClC,CAAC;AAEF,MAAM,iBAAkB,SAAQ,sCAAkC;IAAlE;;QAME,oDAAoD;QACpD,iBAAY,GAAG;YAAa,OAAO,EAAE,IAAI,EAAC;oBACtC,EAAC,IAAI,EAAC,KAAK,EAAC,KAAK,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC;oBAC3C,EAAC,IAAI,EAAC,OAAO,EAAC,KAAK,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC;oBAC7C,EAAC,IAAI,EAAC,oBAAoB,EAAC,KAAK,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC;oBAC3D,EAAC,IAAI,EAAC,oBAAoB,EAAC,KAAK,EAAC,KAAK,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC;oBAC5D,EAAC,IAAI,EAAC,KAAK,EAAC,KAAK,EAAC,KAAK,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC;oBAC5C,EAAC,IAAI,EAAC,KAAK,EAAC,KAAK,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,KAAK,EAAC;oBAChD,EAAC,IAAI,EAAC,eAAe,EAAC,KAAK,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,KAAK,EAAC;iBAC7D,EAAE,CAAA;QAAC,CAAC,CAAC;IAOR,CAAC;IApBC,UAAU,KAAc,OAAO,IAAI,qBAAS,EAAE,CAAC,CAAC,CAAC;IACjD,UAAU,KAAc,OAAO,iBAAiB,CAAC,CAAC,CAAC;IACnD,mBAAmB,KAAK,OAAO,IAAI,CAAC,CAAC,CAAC;IAAA,CAAC;IACvC,WAAW,CAAC,CAAC,IAAW,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAW3D,eAAe,KAAK,OAAO,MAAM,CAAC,CAAC,CAAC;IACpC,YAAY;QACV,IAAI,IAAI,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC;QAChC,IAAI,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED,GAAG;AAEH,eAAS,CAAC,WAAW,CAAC,GAAG,iBAAiB,CAAC"}
|
|
@ -64,6 +64,11 @@ class Atari800Platform extends baseplatform_1.Base6502MachinePlatform {
|
|||
else
|
||||
throw new Error('could not load BIOS file');
|
||||
}
|
||||
getDebugTree() {
|
||||
let tree = super.getDebugTree();
|
||||
tree['display_list'] = this.machine.getDebugDisplayList();
|
||||
return tree;
|
||||
}
|
||||
}
|
||||
class Atari5200Platform extends Atari800Platform {
|
||||
constructor() {
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -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-R63KYPGV.js";import"./chunk-SXEVG2WS.js";import"./chunk-5SHCNQ2O.js";import"./chunk-Z2IKIN54.js";import"./chunk-HB3LWF25.js";import"./chunk-ATS7PSQG.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-MMLUO3PV.js";import"./chunk-SXEVG2WS.js";import"./chunk-5SHCNQ2O.js";import"./chunk-OCFU6ZP7.js";import"./chunk-HB3LWF25.js";import"./chunk-ATS7PSQG.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
|
||||
|
|
11
index.html
11
index.html
|
@ -23,16 +23,6 @@ body {
|
|||
</style>
|
||||
<link rel="stylesheet" href="css/ui.css">
|
||||
|
||||
<!-- google analytics -->
|
||||
<script>
|
||||
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
|
||||
if (window.location.host.endsWith('8bitworkshop.com')) {
|
||||
ga('create', 'UA-54497476-9', 'auto');
|
||||
ga('set', 'anonymizeIp', true);
|
||||
}
|
||||
</script>
|
||||
<script async src='https://www.google-analytics.com/analytics.js'></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
@ -204,7 +194,6 @@ if (window.location.host.endsWith('8bitworkshop.com')) {
|
|||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="?platform=basic">BASIC</a></li>
|
||||
<li><a class="dropdown-item" href="?platform=zmachine">Z-Machine</a></li>
|
||||
<li><a class="dropdown-item" href="?platform=markdown">Markdown Text Editor</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown dropdown-submenu">
|
||||
|
|
|
@ -28,6 +28,10 @@ interface Atari7800State extends Atari7800StateBase, Atari7800ControlsState {
|
|||
offset,dll,dlstart : number;
|
||||
dli,h16,h8 : boolean;
|
||||
};
|
||||
pia : {
|
||||
timer: number;
|
||||
interval: number;
|
||||
}
|
||||
}
|
||||
|
||||
const SWCHA = 0;
|
||||
|
@ -56,11 +60,14 @@ const Atari7800_KEYCODE_MAP = makeKeycodeMap([
|
|||
|
||||
// http://www.ataripreservation.org/websites/freddy.offenga/megazine/ISSUE5-PALNTSC.html
|
||||
// http://7800.8bitdev.org/index.php/7800_Software_Guide#APPENDIX_4:_FRAME_TIMING
|
||||
// https://forums.atariage.com/topic/224025-7800-hardware-facts/
|
||||
const CLK = 3579545;
|
||||
const linesPerFrame = 262;
|
||||
const linesPerFrame = 263;
|
||||
const numVisibleLines = 258-16;
|
||||
const colorClocksPerLine = 454; // 456?
|
||||
const colorClocksPerLine = 451; // 451? 452? 456?
|
||||
const colorClocksPreDMA = 28;
|
||||
const colorClocksShutdownOther = 16;
|
||||
const colorClocksShutdownLast = 24;
|
||||
const audioOversample = 2;
|
||||
const audioSampleRate = linesPerFrame*60*audioOversample;
|
||||
|
||||
|
@ -106,6 +113,7 @@ class MARIA {
|
|||
dli : boolean = false;
|
||||
h16 : boolean = false;
|
||||
h8 : boolean = false;
|
||||
indirect : boolean = false;
|
||||
pixels = new Uint8Array(320);
|
||||
WSYNC : number = 0;
|
||||
|
||||
|
@ -130,6 +138,7 @@ class MARIA {
|
|||
dli: this.dli,
|
||||
h16: this.h16,
|
||||
h8: this.h8,
|
||||
indirect: this.indirect,
|
||||
};
|
||||
}
|
||||
loadState(s) {
|
||||
|
@ -141,6 +150,7 @@ class MARIA {
|
|||
this.dli = !!s.dli;
|
||||
this.h16 = !!s.h16;
|
||||
this.h8 = !!s.h8;
|
||||
this.indirect = !!s.indirect;
|
||||
}
|
||||
isDMAEnabled() {
|
||||
return (this.regs[0x1c] & 0x60) == 0x40;
|
||||
|
@ -174,6 +184,7 @@ class MARIA {
|
|||
this.dli = (bus.read(this.dll) & 0x80) != 0; // DLI flag is from next DLL entry
|
||||
}
|
||||
isHoley(a : number) : boolean {
|
||||
if (this.indirect) return false;
|
||||
if (a & 0x8000) {
|
||||
if (this.h16 && (a & 0x1000)) return true;
|
||||
if (this.h8 && (a & 0x800)) return true;
|
||||
|
@ -181,9 +192,9 @@ class MARIA {
|
|||
return false;
|
||||
}
|
||||
readDMA(a : number) : number {
|
||||
if (this.isHoley(a))
|
||||
if (this.isHoley(a)) {
|
||||
return 0;
|
||||
else {
|
||||
} else {
|
||||
this.cycles += 3;
|
||||
return this.bus.read(a);
|
||||
}
|
||||
|
@ -193,7 +204,8 @@ class MARIA {
|
|||
this.cycles = 0;
|
||||
this.pixels.fill(this.regs[0x0]);
|
||||
if (this.isDMAEnabled()) {
|
||||
this.cycles += 16; // TODO: last line in zone gets additional 8 cycles
|
||||
// last line in zone gets additional 8 cycles
|
||||
this.cycles += this.offset == 0 ? colorClocksShutdownLast : colorClocksShutdownOther;
|
||||
// time for a new DLL entry?
|
||||
if (this.offset < 0) {
|
||||
this.readDLLEntry(bus);
|
||||
|
@ -229,6 +241,7 @@ class MARIA {
|
|||
dlofs += 4;
|
||||
this.cycles += 8;
|
||||
}
|
||||
this.indirect = indirect;
|
||||
let gfxadr = b0 + (((b2 + (indirect?0:this.offset)) & 0xff) << 8);
|
||||
xpos *= 2;
|
||||
// copy graphics data (direct)
|
||||
|
@ -312,6 +325,8 @@ export class Atari7800 extends BasicMachine implements RasterFrameBased {
|
|||
cpu : MOS6502;
|
||||
ram : Uint8Array = new Uint8Array(0x1000);
|
||||
regs6532 = new Uint8Array(4);
|
||||
piatimer : number = 0;
|
||||
timerinterval : number = 1;
|
||||
tia : TIA = new TIA();
|
||||
maria : MARIA = new MARIA();
|
||||
pokey1; //TODO: type
|
||||
|
@ -335,7 +350,7 @@ export class Atari7800 extends BasicMachine implements RasterFrameBased {
|
|||
[0x0040, 0x00ff, 0xff, (a) => { return this.ram[a + 0x800]; }],
|
||||
[0x0100, 0x013f, 0xff, (a) => { return this.read(a); }], // shadow
|
||||
[0x0140, 0x01ff, 0x1ff, (a) => { return this.ram[a + 0x800]; }],
|
||||
[0x0280, 0x02ff, 0x3, (a) => { this.xtracyc++; return this.inputs[a]; }],
|
||||
[0x0280, 0x02ff, 0x7f, (a) => { this.xtracyc++; return this.readPIA(a); }],
|
||||
[0x1800, 0x27ff, 0xffff, (a) => { return this.ram[a - 0x1800]; }],
|
||||
[0x2800, 0x3fff, 0x7ff, (a) => { return this.read(a | 0x2000); }], // shadow
|
||||
[0x4000, 0xffff, 0xffff, (a) => { return this.rom ? this.rom[a - 0x4000] : 0; }],
|
||||
|
@ -348,7 +363,7 @@ export class Atari7800 extends BasicMachine implements RasterFrameBased {
|
|||
[0x0040, 0x00ff, 0xff, (a,v) => { this.ram[a + 0x800] = v; }],
|
||||
[0x0100, 0x013f, 0xff, (a,v) => { this.write(a,v); }], // shadow
|
||||
[0x0140, 0x01ff, 0x1ff, (a,v) => { this.ram[a + 0x800] = v; }],
|
||||
[0x0280, 0x02ff, 0x3, (a,v) => { this.xtracyc++; this.regs6532[a] = v; /*TODO*/ }],
|
||||
[0x0280, 0x02ff, 0x7f, (a,v) => { this.xtracyc++; this.writePIA(a,v) }],
|
||||
[0x1800, 0x27ff, 0xffff, (a,v) => { this.ram[a - 0x1800] = v; }],
|
||||
[0x2800, 0x3fff, 0x7ff, (a,v) => { this.write(a | 0x2000, v); }], // shadow
|
||||
[0xbfff, 0xbfff, 0xffff, (a,v) => { }], // TODO: bank switching?
|
||||
|
@ -378,16 +393,70 @@ export class Atari7800 extends BasicMachine implements RasterFrameBased {
|
|||
}
|
||||
}
|
||||
|
||||
readPIA(a:number) : number {
|
||||
switch (a) {
|
||||
case 0x0:
|
||||
case 0x2:
|
||||
return this.inputs[a]; // SWCHA, SWCHB
|
||||
case 0x1:
|
||||
case 0x3:
|
||||
return this.regs6532[a]; // CTLSWA, CTLSWB
|
||||
case 0x4:
|
||||
return this.getPIATimerValue(); // INTIM
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
writePIA(a:number, v:number) : void {
|
||||
switch (a) {
|
||||
case 0x0:
|
||||
case 0x1:
|
||||
case 0x2:
|
||||
case 0x3:
|
||||
this.regs6532[a] = v;
|
||||
return;
|
||||
case 0x14: this.setPIATimer(v, 0); return; // TIM1T
|
||||
case 0x15: this.setPIATimer(v, 3); return; // TIM8T
|
||||
case 0x16: this.setPIATimer(v, 6); return; // TIM64T
|
||||
case 0x17: this.setPIATimer(v, 10); return; // T1024T
|
||||
case 0x18: this.setPIATimer(v, 6); return; // TIM64TI (TODO)
|
||||
}
|
||||
}
|
||||
|
||||
setPIATimer(v:number, shift:number) : void {
|
||||
this.piatimer = (v + 1) << shift;
|
||||
this.timerinterval = shift;
|
||||
}
|
||||
|
||||
getPIATimerValue() : number {
|
||||
let t = this.piatimer;
|
||||
if (t > 0) {
|
||||
return t >> this.timerinterval;
|
||||
} else {
|
||||
return t & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
advanceCPU() : number {
|
||||
var clk = super.advanceCPU();
|
||||
this.tickPIATimer(clk); // TODO?
|
||||
if (this.xtracyc) {
|
||||
clk += this.xtracyc;
|
||||
this.probe.logClocks(this.xtracyc);
|
||||
this.tickClocks(this.xtracyc);
|
||||
this.xtracyc = 0;
|
||||
}
|
||||
return clk;
|
||||
}
|
||||
|
||||
tickClocks(clocks:number) {
|
||||
this.probe.logClocks(clocks);
|
||||
this.tickPIATimer(clocks);
|
||||
}
|
||||
tickPIATimer(clocks:number) {
|
||||
this.piatimer = Math.max(-256, this.piatimer - clocks);
|
||||
}
|
||||
|
||||
advanceFrame(trap) : number {
|
||||
var idata = this.pixels;
|
||||
var iofs = 0;
|
||||
|
@ -418,7 +487,7 @@ export class Atari7800 extends BasicMachine implements RasterFrameBased {
|
|||
if (visible) {
|
||||
// do DMA for scanline?
|
||||
let dmaClocks = this.maria.doDMA(this.dmaBus);
|
||||
this.probe.logClocks(dmaClocks >> 2); // TODO: logDMA
|
||||
this.tickClocks(dmaClocks >> 2); // TODO: logDMA
|
||||
mc += dmaClocks;
|
||||
// copy line to frame buffer
|
||||
if (idata) {
|
||||
|
@ -436,7 +505,7 @@ export class Atari7800 extends BasicMachine implements RasterFrameBased {
|
|||
while (mc < colorClocksPerLine) {
|
||||
if (this.maria.WSYNC) {
|
||||
this.probe.logWait(0);
|
||||
this.probe.logClocks((colorClocksPerLine - mc) >> 2);
|
||||
this.tickClocks((colorClocksPerLine - mc) >> 2);
|
||||
mc = colorClocksPerLine;
|
||||
break;
|
||||
}
|
||||
|
@ -491,6 +560,8 @@ export class Atari7800 extends BasicMachine implements RasterFrameBased {
|
|||
this.tia.loadState(state.tia);
|
||||
this.maria.loadState(state.maria);
|
||||
this.regs6532.set(state.regs6532);
|
||||
this.piatimer = state.pia.timer;
|
||||
this.timerinterval = state.pia.interval;
|
||||
this.loadControlsState(state);
|
||||
}
|
||||
saveState() : Atari7800State {
|
||||
|
@ -500,7 +571,8 @@ export class Atari7800 extends BasicMachine implements RasterFrameBased {
|
|||
tia:this.tia.saveState(),
|
||||
maria:this.maria.saveState(),
|
||||
regs6532:this.regs6532.slice(0),
|
||||
inputs:this.inputs.slice(0)
|
||||
inputs:this.inputs.slice(0),
|
||||
pia:{timer:this.piatimer, interval: this.timerinterval}
|
||||
};
|
||||
}
|
||||
loadControlsState(state:Atari7800ControlsState) : void {
|
||||
|
@ -522,6 +594,75 @@ export class Atari7800 extends BasicMachine implements RasterFrameBased {
|
|||
//default: return super.getDebugInfo(category, state);
|
||||
}
|
||||
}
|
||||
getDebugDisplayLists() {
|
||||
// return display list in human-readable JSON object
|
||||
let display_lists = {};
|
||||
let dll_ofs = this.maria.getDLLStart();
|
||||
// read the address of each DLL entry
|
||||
let y = 0;
|
||||
while (y < 240) {
|
||||
let x = this.readConst(dll_ofs);
|
||||
let offset = (x & 0xf);
|
||||
let h16 = (x & 0x40) != 0;
|
||||
let h8 = (x & 0x20) != 0;
|
||||
let dlstart = (this.readConst(dll_ofs+1)<<8) + this.readConst(dll_ofs+2);
|
||||
dll_ofs = (dll_ofs + 3) & 0xffff; // TODO: can also only cross 1 page?
|
||||
let dli = (this.readConst(dll_ofs) & 0x80) != 0; // DLI flag is from next DLL entry
|
||||
let title = "DL $" + hex(dlstart,4) + " " + y + "-" + (y+offset);
|
||||
if (h16) title += " H16";
|
||||
if (h8) title += " H8";
|
||||
if (dli) title += " DLI";
|
||||
display_lists[title] = { "$$": this._readDebugDisplayList(dlstart) };
|
||||
y += offset + 1;
|
||||
}
|
||||
return display_lists;
|
||||
}
|
||||
_readDebugDisplayList(dlstart: number) {
|
||||
return () => this.readDebugDisplayList(dlstart);
|
||||
}
|
||||
readDebugDisplayList(dlstart: number) {
|
||||
let display_list = [];
|
||||
let dlhi = dlstart & 0xff00;
|
||||
let dlofs = dlstart & 0xff;
|
||||
do {
|
||||
// read DL entry
|
||||
let b0 = this.readConst(dlhi + ((dlofs+0) & 0x1ff));
|
||||
let b1 = this.readConst(dlhi + ((dlofs+1) & 0x1ff));
|
||||
if (b1 == 0) break; // end of DL
|
||||
// display lists must be in RAM (TODO: probe?)
|
||||
let b2 = this.readConst(dlhi + ((dlofs+2) & 0x1ff));
|
||||
let b3 = this.readConst(dlhi + ((dlofs+3) & 0x1ff));
|
||||
// extended header?
|
||||
let indirect = false;
|
||||
let description = "";
|
||||
if ((b1 & 31) == 0) {
|
||||
var pal = b3 >> 5;
|
||||
var width = 32 - (b3 & 31);
|
||||
var xpos = this.readConst(dlhi + ((dlofs+4) & 0x1ff));
|
||||
var writemode = b1 & 0x80;
|
||||
indirect = (b1 & 0x20) != 0;
|
||||
dlofs += 5;
|
||||
description += "X=" + xpos + " W=" + width + " P=" + pal + " " + (writemode?"WRITE":"");
|
||||
} else {
|
||||
// direct mode
|
||||
var xpos = b3;
|
||||
var pal = b1 >> 5;
|
||||
var width = 32 - (b1 & 31);
|
||||
var writemode = 0;
|
||||
dlofs += 4;
|
||||
description += "X=" + xpos + " W=" + width + " P=" + pal;
|
||||
}
|
||||
let gfxadr = b0 + (((b2 + (indirect?0:this.maria.offset)) & 0xff) << 8);
|
||||
let readmode = (this.maria.regs[0x1c] & 0x3) + (writemode?4:0);
|
||||
let dbl = indirect && (this.maria.regs[0x1c] & 0x10) != 0;
|
||||
if (readmode) description += " READMODE=" + readmode;
|
||||
if (dbl) description += " DBL";
|
||||
if (indirect) description += " CHR=$" + hex((this.maria.regs[0x14] + this.maria.offset) & 0xff) + "xx";
|
||||
description = " $" + hex(gfxadr,4) + " " + description;
|
||||
display_list.push(description);
|
||||
} while (dlofs < 0x200);
|
||||
return display_list;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
|
|
|
@ -5,7 +5,7 @@ import { AcceptsKeyInput, AcceptsPaddleInput, AcceptsROM, BasicScanlineMachine,
|
|||
import { KeyFlags, Keys, makeKeycodeMap, newAddressDecoder, newKeyboardHandler } from "../common/emu";
|
||||
import { hex } from "../common/util";
|
||||
import { BaseWASIMachine } from "../common/wasmplatform";
|
||||
import { ANTIC, MODE_SHIFT } from "./chips/antic";
|
||||
import { ANTIC, MODE_LINES, MODE_SHIFT } from "./chips/antic";
|
||||
import { CONSOL, GTIA, TRIG0 } from "./chips/gtia";
|
||||
import { POKEY } from "./chips/pokey";
|
||||
|
||||
|
@ -410,6 +410,49 @@ export class Atari800 extends BasicScanlineMachine implements AcceptsPaddleInput
|
|||
this.irq_pokey.pot_inputs[controller] = 255 - value;
|
||||
}
|
||||
|
||||
getDebugDisplayList() {
|
||||
let pc = this.antic.getDlistAddr();
|
||||
const nextInsn = () => {
|
||||
let b = this.read(pc);
|
||||
pc = ((pc + 1) & 0x3ff) | (pc & ~0x3ff);
|
||||
return b;
|
||||
}
|
||||
let dlist = {};
|
||||
let y = 0;
|
||||
for (let i=0; i<256 && y<240; i++) {
|
||||
let pc0 = pc;
|
||||
let op = nextInsn(); // get mode
|
||||
let mode = op & 0xf;
|
||||
let debugmsg = ""; // op=" + hex(op);
|
||||
let jmp = false;
|
||||
let lines;
|
||||
if (mode == 0) {
|
||||
lines = (((op >> 4) & 7) + 1);
|
||||
debugmsg += " blank=" + lines;
|
||||
} else {
|
||||
lines = MODE_LINES[mode];
|
||||
debugmsg += " mode=" + hex(mode);
|
||||
debugmsg += " lines=" + lines;
|
||||
jmp = (op & ~0x40) == 0x01; // JMP insn?
|
||||
let lms = (op & 0x40) != 0 && (op & 0xf) != 0; // LMS insn?
|
||||
if (jmp && (op & 0x40)) { debugmsg += " JVB"; }
|
||||
else if (jmp) debugmsg += " JMP";
|
||||
else if (lms) debugmsg += " LMS";
|
||||
if (this.antic.isPlayfieldDMAEnabled() && (jmp || lms)) {
|
||||
let dlarg_lo = nextInsn();
|
||||
let dlarg_hi = nextInsn();
|
||||
debugmsg += " $" + hex(dlarg_hi) + "" + hex(dlarg_lo);
|
||||
}
|
||||
if (op & 0x10) { debugmsg += " HSCROL"; }
|
||||
if (op & 0x20) { debugmsg += " VSCROL"; }
|
||||
}
|
||||
dlist["$"+hex(pc0) + " y=" + y] = debugmsg;
|
||||
if (jmp) break;
|
||||
y += lines;
|
||||
}
|
||||
return dlist;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class Atari5200 extends Atari800 {
|
||||
|
|
|
@ -42,7 +42,7 @@ const ANTIC_LEFT = 17 - 4; // gtia 34, 4 cycle delay
|
|||
const ANTIC_RIGHT = 110 - 4; // gtia 221, 4 cycle delay
|
||||
const LAST_DMA_H = 105; // last DMA cycle
|
||||
|
||||
const MODE_LINES = [0, 0, 8, 10, 8, 16, 8, 16, 8, 4, 4, 2, 1, 2, 1, 1];
|
||||
export const MODE_LINES = [0, 0, 8, 10, 8, 16, 8, 16, 8, 4, 4, 2, 1, 2, 1, 1];
|
||||
// how many bits before DMA clock repeats?
|
||||
const MODE_PERIOD = [0, 0, 2, 2, 2, 2, 4, 4, 8, 4, 4, 4, 4, 2, 2, 2];
|
||||
const MODE_YPERIOD = [0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 2, 1, 0, 0, 0, 0];
|
||||
|
@ -199,8 +199,12 @@ export class ANTIC {
|
|||
}
|
||||
}
|
||||
|
||||
getDlistAddr() {
|
||||
return this.regs[DLISTL] + (this.regs[DLISTH] << 8);
|
||||
}
|
||||
|
||||
nextInsn(): number {
|
||||
let pc = this.regs[DLISTL] + (this.regs[DLISTH] << 8);
|
||||
let pc = this.getDlistAddr();
|
||||
let b = this.read(pc);
|
||||
//console.log('nextInsn', hex(pc), hex(b), this.v);
|
||||
pc = ((pc + 1) & 0x3ff) | (pc & ~0x3ff);
|
||||
|
|
|
@ -27,6 +27,11 @@ class Atari7800Platform extends Base6502MachinePlatform<Atari7800> implements Pl
|
|||
{name:'Cartridge ROM',start:0x4000,size:0xc000,type:'rom'},
|
||||
] } };
|
||||
getROMExtension() { return ".a78"; }
|
||||
getDebugTree() {
|
||||
let tree = super.getDebugTree();
|
||||
tree['display_list'] = this.machine.getDebugDisplayLists();
|
||||
return tree;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
|
|
|
@ -63,6 +63,11 @@ class Atari800Platform extends Base6502MachinePlatform<Atari800> {
|
|||
return new Uint8Array(biosBinary);
|
||||
} else throw new Error('could not load BIOS file');
|
||||
}
|
||||
getDebugTree() {
|
||||
let tree = super.getDebugTree();
|
||||
tree['display_list'] = this.machine.getDebugDisplayList();
|
||||
return tree;
|
||||
}
|
||||
}
|
||||
|
||||
class Atari5200Platform extends Atari800Platform {
|
||||
|
|
Loading…
Reference in New Issue
Block a user