From 149bcdff7305a6bba0ff004022f37b484e222262 Mon Sep 17 00:00:00 2001 From: sehugg Date: Fri, 18 Sep 2026 14:57:24 +0000 Subject: [PATCH] =?UTF-8?q?Deploying=20to=20gh-pages=20from=20@=20sehugg/8?= =?UTF-8?q?bitworkshop@b49579695f676dd1562477c424d388c58602cad8=20?= =?UTF-8?q?=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gen/atari8-GYR56LP5.js | 7 - gen/atari8-VDKLRU3M.js | 7 + ...GYR56LP5.js.map => atari8-VDKLRU3M.js.map} | 8 +- gen/{basic-NCWX6JLL.js => basic-FXG6Z7YU.js} | 4 +- ...-NCWX6JLL.js.map => basic-FXG6Z7YU.js.map} | 0 gen/chunk-5YJJ4DRA.js.map | 7 - gen/{chunk-4R3KPJX7.js => chunk-C7SFJC4F.js} | 4 +- ...-4R3KPJX7.js.map => chunk-C7SFJC4F.js.map} | 0 gen/{chunk-5YJJ4DRA.js => chunk-XRWSPMRR.js} | 88 +- gen/chunk-XRWSPMRR.js.map | 7 + gen/common/toolmeta.js | 23 +- gen/common/toolmeta.js.map | 2 +- gen/common/workertypes.js.map | 2 +- gen/{devel-TVV4ZUJO.js => devel-LWYSQMYN.js} | 4 +- ...-TVV4ZUJO.js.map => devel-LWYSQMYN.js.map} | 0 gen/embedui.js | 2 +- gen/ide/project.js | 64 +- gen/ide/project.js.map | 2 +- gen/ide/ui.js | 2 +- gen/ide/ui.js.map | 2 +- gen/machine/atari8.js | 91 +- gen/machine/atari8.js.map | 2 +- gen/machine/atari8disk.js | 68 + gen/machine/atari8disk.js.map | 1 + gen/platform/atari8.js | 7 +- gen/platform/atari8.js.map | 2 +- gen/server/server.js.map | 4 +- gen/src/common/toolmeta.js | 23 +- gen/src/common/toolmeta.js.map | 2 +- gen/src/common/workertypes.js.map | 2 +- gen/src/ide/project.js | 64 +- gen/src/ide/project.js.map | 2 +- gen/src/ide/ui.js | 2 +- gen/src/ide/ui.js.map | 2 +- gen/src/machine/atari8.js | 91 +- gen/src/machine/atari8.js.map | 2 +- gen/src/machine/atari8disk.js | 68 + gen/src/machine/atari8disk.js.map | 1 + gen/src/platform/atari8.js | 7 +- gen/src/platform/atari8.js.map | 2 +- gen/src/tools/testlib.js | 38 +- gen/src/tools/testlib.js.map | 2 +- gen/src/worker/tools/oscar64.js | 15 +- gen/src/worker/tools/oscar64.js.map | 2 +- gen/test/tsconfig.tsbuildinfo | 2 +- gen/test/unit/testlinking.js | 100 + gen/test/unit/testlinking.js.map | 2 +- gen/test/unit/testlistingfiles.js | 55 + gen/test/unit/testlistingfiles.js.map | 1 + gen/tools/testlib.js | 38 +- gen/tools/testlib.js.map | 2 +- gen/tsconfig.tsbuildinfo | 2 +- gen/ui.js | 2 +- gen/worker/bundle.js | 13 +- gen/worker/bundle.js.map | 4 +- gen/worker/tools/oscar64.js | 15 +- gen/worker/tools/oscar64.js.map | 2 +- presets/atari8-800/atari.h | 2033 +++++++++++++++++ presets/atari8-800/common.c | 66 +- presets/atari8-800/common.h | 7 +- presets/atari8-800/libdemo.cpp | 98 + presets/atari8-800/pokeymusic.cpp | 186 ++ src/common/toolmeta.ts | 26 +- src/common/workertypes.ts | 7 + src/ide/project.ts | 68 +- src/ide/ui.ts | 2 +- src/machine/atari8.ts | 85 +- src/machine/atari8disk.ts | 67 + src/platform/atari8.ts | 6 +- src/tools/testlib.ts | 40 +- src/worker/tools/oscar64.ts | 13 +- 71 files changed, 3489 insertions(+), 188 deletions(-) delete mode 100644 gen/atari8-GYR56LP5.js create mode 100644 gen/atari8-VDKLRU3M.js rename gen/{atari8-GYR56LP5.js.map => atari8-VDKLRU3M.js.map} (52%) rename gen/{basic-NCWX6JLL.js => basic-FXG6Z7YU.js} (99%) rename gen/{basic-NCWX6JLL.js.map => basic-FXG6Z7YU.js.map} (100%) delete mode 100644 gen/chunk-5YJJ4DRA.js.map rename gen/{chunk-4R3KPJX7.js => chunk-C7SFJC4F.js} (97%) rename gen/{chunk-4R3KPJX7.js.map => chunk-C7SFJC4F.js.map} (100%) rename gen/{chunk-5YJJ4DRA.js => chunk-XRWSPMRR.js} (81%) create mode 100644 gen/chunk-XRWSPMRR.js.map rename gen/{devel-TVV4ZUJO.js => devel-LWYSQMYN.js} (93%) rename gen/{devel-TVV4ZUJO.js.map => devel-LWYSQMYN.js.map} (100%) create mode 100644 gen/machine/atari8disk.js create mode 100644 gen/machine/atari8disk.js.map create mode 100644 gen/src/machine/atari8disk.js create mode 100644 gen/src/machine/atari8disk.js.map create mode 100644 gen/test/unit/testlistingfiles.js create mode 100644 gen/test/unit/testlistingfiles.js.map create mode 100644 presets/atari8-800/atari.h create mode 100644 presets/atari8-800/libdemo.cpp create mode 100644 presets/atari8-800/pokeymusic.cpp create mode 100644 src/machine/atari8disk.ts diff --git a/gen/atari8-GYR56LP5.js b/gen/atari8-GYR56LP5.js deleted file mode 100644 index a51ba2ec..00000000 --- a/gen/atari8-GYR56LP5.js +++ /dev/null @@ -1,7 +0,0 @@ -import{a as nt}from"./chunk-WIKZL6VD.js";import{b as ht,c as ot,d as Y}from"./chunk-LZK7RTFU.js";import{A as rt,J as at,g as $,k as tt,m as et,r as st,x as it}from"./chunk-2XSLQJUY.js";import{K as D,V as O,W as i,X as Z,Z as J,a as U,aa as T,g as m,r as A}from"./chunk-TYDI7JZQ.js";import"./chunk-VSC4MAII.js";var Ot=[0,25,17,9],Tt=[0,89,97,105],b=0,Mt=1,F=2,H=3,kt=4,vt=5,Ct=7,Lt=9,C=10,Pt=11,Kt=12,Vt=13,lt=14,wt=15,L=15;var Nt=13,Ut=106,Yt=105,Q=[0,0,8,10,8,16,8,16,8,4,4,2,1,2,1,1],Ft=[0,0,2,2,2,2,4,4,8,8,4,4,4,2,2,2],Ht=[0,0,0,0,0,1,0,1,0,0,2,1,0,0,0,0],ct=[0,0,1,1,2,2,2,2,8,4,4,2,2,2,2,1],M=class{constructor(e,t){this.regs=new Uint8Array(16);this.left=-1;this.right=-1;this.dma_enabled=!1;this.dliop=0;this.mode=0;this.jmp=!1;this.lms=!1;this.dlarg_lo=0;this.dlarg_hi=0;this.period=0;this.scanaddr=0;this.startaddr=0;this.pfbyte=0;this.ch=0;this.linesleft=0;this.yofs=0;this.isfirstline=!1;this.v=0;this.h=0;this.linebuf=new Uint8Array(48);this.dmaclock=0;this.dmaidx=0;this.output=0;this.dramrefresh=!1;this.in_vscroll=0;this.read=e,this.nmi=t}reset(){this.regs.fill(0),this.regs[lt]=0,this.regs[L]=127,this.regs[Kt]=0,this.regs[Vt]=255,this.setReg(b,0),this.h=this.v=0,this.startaddr=this.scanaddr=0,this.dmaclock=0}saveState(){return A(0,{},this)}loadState(e){A(0,this,e),this.setReg(b,e.regs[b])}static stateToLongString(e){let t="";return t+="H: "+U(e.h,3)+" V: "+U(e.v,3)+` -`,t+="DLIOp: "+m(e.dliop,2)+" Lines: "+e.yofs+"/"+e.linesleft,t+=" DMA "+(e.dma_enabled?"ON ":"off"),e.dma_enabled&&(t+=" idx "+e.dmaidx+" clk "+m(e.dmaclock)),t+=` -`,t+="Addr: "+m(e.scanaddr,4)+` -`,t+=O(e.regs,0,16).replace("$00","Regs"),t}setReg(e,t){switch(e){case C:this.regs[C]=255;return;case wt:this.regs[L]=31;return}this.regs[e]=t}readReg(e){switch(e){case L:return this.regs[e];case Pt:return this.v>>1;default:return 255}}processDLIEntry(){if(this.mode==0)this.linesleft=(this.dliop>>4&7)+1,this.dmaclock=0;else{this.linesleft=Q[this.mode],this.period=Ft[this.mode],this.jmp?(this.regs[F]=this.dlarg_lo,this.regs[H]=this.dlarg_hi,this.mode=this.period=0,this.dliop&64&&(this.linesleft=1,this.dma_enabled=!1),this.dmaclock=0):this.lms&&(this.scanaddr=this.dlarg_lo+(this.dlarg_hi<<8)),this.startaddr=this.scanaddr;let e=this.regs[b]&3,t=this.dliop&16?(this.regs[kt]&15)>>1:0;this.dliop&16&&e<3&&e++,this.left=Ot[e]+t,this.right=Tt[e]+t;let s=this.regs[vt]&15;this.dliop&32^this.in_vscroll&&(this.in_vscroll?this.linesleft=s+1:(this.linesleft-=s,this.yofs+=s),this.linesleft&=15,this.in_vscroll^=32)}}nextLine(){this.linesleft>0&&(this.linesleft--,this.yofs++,this.isfirstline=!1,this.mode>=8&&this.linesleft&&(this.scanaddr=this.startaddr))}triggerNMI(e){this.regs[L]=e|31,this.regs[lt]&e&&this.nmi()}getDlistAddr(){return this.regs[F]+(this.regs[H]<<8)}nextInsn(){let e=this.getDlistAddr(),t=this.read(e);return e=e+1&1023|e&-1024,this.regs[F]=e&255,this.regs[H]=e>>8,t}nextScreen(){let e=this.read(this.scanaddr);return this.incScanAddr(),e}incScanAddr(){this.scanaddr=this.scanaddr+1&4095|this.scanaddr&-4096}dlDMAEnabled(){return this.regs[b]&32}isVisibleScanline(){return this.v>=8&&this.v<248}isPlayfieldDMAEnabled(){return this.dma_enabled&&!this.linesleft}isPlayerDMAEnabled(){return this.regs[b]&8}isMissileDMAEnabled(){return this.regs[b]&12}isWSYNC(){return this.regs[C]!=0}clockPulse(){let e=this.isWSYNC();if(!this.isVisibleScanline())this.doVBlank();else{switch(this.h){case 0:this.isMissileDMAEnabled()&&(this.doPlayerMissileDMA(3),e=!0);break;case 1:if(this.isPlayfieldDMAEnabled()){let t=this.nextInsn();this.jmp=(t&-65)==1,this.lms=(t&64)!=0&&(t&15)!=0,this.mode=t&15,this.dliop=t,this.yofs=0,this.isfirstline=!0,e=!0}break;case 2:case 3:case 4:case 5:this.isPlayerDMAEnabled()&&(this.doPlayerMissileDMA(this.h+2),e=!0);break;case 6:case 7:this.isPlayfieldDMAEnabled()&&this.isfirstline&&(this.jmp||this.lms)&&(this.h==6&&(this.dlarg_lo=this.nextInsn()),this.h==7&&(this.dlarg_hi=this.nextInsn()),e=!0);break;case 8:this.isfirstline&&this.processDLIEntry(),this.dliop&128&&this.linesleft==1&&this.triggerNMI(128);break;case 9:break;case 111:this.dma_enabled&&this.nextLine(),++this.v;break}if(this.output=0,this.mode>=2&&this.period){let t=this.h<=Yt;this.dmaclock=this.dmaclock<<1&511,this.dmaclock&1<=8&&this.incScanAddr(),e=t),this.output=this.h>=this.left+3&&this.h<=this.right+2?4:0}}return(this.hUt)&&(this.output=2),this.incHorizCounter(),!e&&this.dramrefresh&&(this.read(0),this.dramrefresh=!1,e=!0),e}incHorizCounter(){switch(this.h){case 25:case 29:case 33:case 37:case 41:case 45:case 49:case 53:case 57:this.dramrefresh=!0;break;case 102:this.regs[C]=0;break;case 113:this.h=0;return}++this.h}doVBlank(){this.linesleft=this.mode=this.period=0,this.h==111&&this.v++,this.v==248&&this.h==0&&this.triggerNMI(64),this.v==262&&this.h==112&&(this.v=0),this.v==7&&this.h==113&&(this.dma_enabled=this.dlDMAEnabled()!=0),this.output=2,this.dmaclock=0}doPlayerMissileDMA(e){let t=this.regs[b]&16,s=this.regs[Ct]<<8;t?(s&=63488,s|=e<<8,s|=this.v&255):(s&=64512,s|=e<<7,s|=this.v>>1),this.read(s)}readBitmapData(){let e=this.mode;if(e<8){let t=this.ch,s=this.yofs>>Ht[this.mode],r=s&7,n=this.regs[Lt];(e&14)==6?(t&=63,n&=254):(t&=127,n&=252);let h=(t<<3)+(n<<8);if((e&14)==2){let f=this.regs[Mt],l=e==3&&(t&96)==96;f&4?this.pfbyte=this.read(h+(r^7)):this.pfbyte=this.read(h+r),l&&s<2&&(this.pfbyte=0),!l&&s>7&&(this.pfbyte=0),this.ch&128&&(f&1&&(this.pfbyte=0),f&2&&(this.pfbyte^=255))}else this.pfbyte=this.read(h+r)}else this.pfbyte=this.nextScreen()}shiftout(){if(this.output==4)switch(this.mode){case 2:case 3:case 15:{let e=this.pfbyte>>7&1;return this.pfbyte<<=1,e?8:6}case 6:case 7:{let e=this.pfbyte>>7&1;return this.pfbyte<<=1,e?(this.ch>>6)+4:0}case 9:case 11:case 12:{let e=this.pfbyte>>7&1;return this.pfbyte<<=1,e?4:0}case 4:case 5:{let e=this.pfbyte>>6&3;return this.pfbyte<<=2,this.ch&128?[0,4,5,7][e]:[0,4,5,6][e]}case 8:case 10:case 13:case 14:{let e=this.pfbyte>>6&3;return this.pfbyte<<=2,[0,4,5,6][e]}}return this.output}};var o=0;var Qt=2;var ft=4;var dt=6;var g=8,Bt=9,zt=10,Xt=11,qt=13,B=14,E=15;var Wt=8,Gt=9,jt=10,Zt=13,S=14,P=15;var xt=128,ut=64,Jt=32,mt=16,gt=8;var $t=1,_t=28,te=114;var pt=511,bt=131071,x=0,u=1,_=2,c=3,y=114,ee=15;var se=8;var z,K;function ie(){z=new Uint8Array(511),K=new Uint8Array(16385);let a=511;for(let e=0;e<511;e++)a=(((a>>5^a)&1)<<8)+(a>>1),z[e]=a;a=131071;for(let e=0;e<16385;e++)a=(((a>>5^a)&255)<<9)+(a>>8),K[e]=a>>1}var k=class{constructor(e,t){this.irq=e;this.antic_xpos=t;this.regs=new Uint8Array(16);this.readregs=new Uint8Array(16);this.divnirq=new Uint32Array(4);this.divnmax=new Uint32Array(4);this.pot_inputs=new Uint8Array(8);this.basemult=0;this.pot_scanline=0;this.random_scanline_counter=0;this.kbcode=0;this.DELAYED_SERIN_IRQ=0;this.DELAYED_SEROUT_IRQ=0;this.DELAYED_XMTDONE_IRQ=0;this.init()}saveState(){return A(0,{},this)}loadState(e){A(0,this,e)}init(){this.readregs.fill(255),this.readregs[P]=239,this.basemult=_t,this.pot_inputs.fill(128),ie()}read(e){let t=this.readregs[e];switch(e&=15,e){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:return t=this.pot_inputs[e],t>3;s&=7,t=(K[r]>>s)+(K[r+1]<<8-s)}}break}return t&255}write(e,t){switch(e&=15,this.regs[e]=t,e){case g:t&$t?this.basemult=te:this.basemult=_t,this.update_counter(1<=3?(this.readregs[S]|=8,this.DELAYED_XMTDONE_IRQ=2*this.DELAYED_SEROUT_IRQ-2):(this.DELAYED_SEROUT_IRQ=0,this.DELAYED_XMTDONE_IRQ=0));break;case Bt:this.divnirq[x]=this.divnmax[x],this.divnirq[u]=this.divnmax[u],this.divnirq[_]=this.divnmax[_],this.divnirq[c]=this.divnmax[c];break;case E:t&4&&(this.pot_scanline=228),(t&3)==0&&(this.DELAYED_SERIN_IRQ=0,this.DELAYED_SEROUT_IRQ=0,this.DELAYED_XMTDONE_IRQ=0);break}this.snd_update(e)}update_counter(e){e&1<0&&--this.DELAYED_SERIN_IRQ==0&&(this.readregs[Zt]=this.SIO_GetByte(),this.generateIRQ(32)),this.DELAYED_SEROUT_IRQ>0&&--this.DELAYED_SEROUT_IRQ==0&&this.generateIRQ(16),this.DELAYED_XMTDONE_IRQ>0&&--this.DELAYED_XMTDONE_IRQ==0&&this.generateIRQ(8),this.advanceIRQTimer(x,1),this.advanceIRQTimer(u,2),this.advanceIRQTimer(c,4))}advanceIRQTimer(e,t){(this.divnirq[e]-=y)<0&&(this.divnirq[e]+=this.divnmax[e],this.generateIRQ(t))}generateIRQ(e){this.regs[B]&e&&(this.irq(),this.readregs[S]&=~e)}static stateToLongString(e){let t="";return t+=`Write Registers: -`,t+=O(e.regs,0,16),t+=`Read Registers: -`,t+=O(e.readregs,0,16),t}CASSETTE_IOLineStatus(){return 0}siocheck(){return((this.regs[o+_]==40||this.regs[o+_]==16||this.regs[o+_]==8||this.regs[o+_]==10)&&this.regs[o+c]==0||(this.regs[E]&120)==40)&&(this.regs[g]&40)==40}SIO_PutByte(e){console.log("SIO put byte",e)}SIO_GetByte(){return 0}};var re=[i.VK_L,i.VK_J,i.VK_SEMICOLON,i.VK_F4,i.VK_F5,i.VK_K,i.VK_BACK_SLASH,i.VK_TILDE,i.VK_O,null,i.VK_P,i.VK_U,i.VK_ENTER,i.VK_I,i.VK_MINUS2,i.VK_EQUALS2,i.VK_V,i.VK_F7,i.VK_C,i.VK_F6,i.VK_F4,i.VK_B,i.VK_X,i.VK_Z,i.VK_4,null,i.VK_3,i.VK_6,i.VK_ESCAPE,i.VK_5,i.VK_2,i.VK_1,i.VK_COMMA,i.VK_SPACE,i.VK_PERIOD,i.VK_N,null,i.VK_M,i.VK_SLASH,null,i.VK_R,null,i.VK_E,i.VK_Y,i.VK_TAB,i.VK_T,i.VK_W,i.VK_Q,i.VK_9,null,i.VK_0,i.VK_7,i.VK_BACK_SPACE,i.VK_8,null,null,i.VK_F,i.VK_H,i.VK_D,null,i.VK_CAPS_LOCK,i.VK_G,i.VK_S,i.VK_A],ae=J([[i.UP,0,1],[i.DOWN,0,2],[i.LEFT,0,4],[i.RIGHT,0,8],[{c:16,n:"Shift",plyr:0,button:0},2,1],[i.VK_F1,3,1],[i.VK_F2,3,2],[i.VK_F3,3,4]]),v=class extends st{constructor(){super();this.cpuFrequency=1789773;this.numTotalScanlines=262;this.cpuCyclesPerLine=114;this.canvasWidth=336;this.numVisibleScanlines=224;this.aspectRatio=this.canvasWidth/this.numVisibleScanlines*.857;this.firstVisibleScanline=16;this.firstVisibleClock=76;this.defaultROMSize=32768;this.overscan=!0;this.audioOversample=2;this.sampleRate=this.numTotalScanlines*60*this.audioOversample;this.run_address=-1;this.inputs=new Uint8Array(4);this.linergb=new Uint32Array(this.canvasWidth);this.lastdmabyte=0;this.keycode=0;this.cart_80=!1;this.cart_a0=!1;this.xexdata=null;this.keyboard_active=!0;this.d500=new Uint8Array(256);this.cpu=new et,this.ram=new Uint8Array(65536),this.bios=new Uint8Array(10240),this.bus=this.newBus(),this.connectCPUMemoryBus(this.bus),this.antic=new M(this.readDMA.bind(this),this.antic_nmi.bind(this)),this.gtia=new Y,this.irq_pokey=new k(this.pokey_irq.bind(this),()=>this.antic.h),this.audio_pokey=$(1),this.audioadapter=new tt(this.audio_pokey.pokey1,this.audioOversample,this.sampleRate),this.handler=Z(this.inputs,ae,this.getKeyboardFunction(),!0)}newBus(){return{read:T([[0,32767,65535,t=>this.ram[t]],[32768,40959,65535,t=>this.cart_80?this.rom[t-32768]:this.ram[t]],[40960,49151,65535,t=>this.cart_a0?this.rom[t-32768]:this.ram[t]],[53248,53503,31,t=>this.gtia.readReg(t)],[53760,54015,15,t=>this.readPokey(t)],[54016,54271,15,t=>this.readPIA(t)],[54272,54527,15,t=>this.antic.readReg(t)],[54528,54783,255,t=>this.d500[t]],[55296,65535,65535,t=>this.bios[t-55296]]]),write:T([[0,49146,65535,(t,s)=>{this.ram[t]=s}],[49147,49151,65535,(t,s)=>{this.ram[t]=s,this.initCartA()}],[53248,53503,31,(t,s)=>{this.gtia.setReg(t,s)}],[53760,54015,15,(t,s)=>{this.writePokey(t,s)}],[54272,54527,15,(t,s)=>{this.antic.setReg(t,s)}],[54528,54783,255,(t,s)=>{this.writeMapper(t,s)}]])}}loadBIOS(t){this.bios.set(t)}reset(){super.reset(),this.antic.reset(),this.gtia.reset(),this.keycode=0}read(t){return this.bus.read(t)}readDMA(t){let s=this.bus.read(t);return this.probe.logDMARead(t,s),this.lastdmabyte=s,s}readConst(t){return t<53248||t>=54528?this.bus.read(t):255}write(t,s){this.bus.write(t,s)}readPokey(t){switch(t&15){case 9:return this.keycode&255;case 15:return~this.keycode>>6&4|~this.keycode>>3&8|18;default:return this.irq_pokey.read(t)}}readPIA(t){if(t==0||t==1)return~this.inputs[t]}writePokey(t,s){this.audio_pokey.pokey1.setRegister(t,s),this.irq_pokey.write(t,s)}startScanline(){this.gtia.sync();for(let t=0;t<4;t++)this.gtia.readregs[ht+t]=~this.inputs[2]>>t&1;this.gtia.readregs[ot]=~this.inputs[3]&7,this.audio&&this.audioadapter.generate(this.audio),this.irq_pokey.advanceScanline()}drawScanline(){let t=this.antic.v-this.firstVisibleScanline;t>=0&&t{this.gtia.clockPulse1(),this.linergb[t++]=this.gtia.rgb},r=()=>{this.gtia.clockPulse2(),this.linergb[t++]=this.gtia.rgb};this.gtia.clockPulse4(),this.antic.dliop&16&&this.antic.regs[4]&1?(t+=2,this.gtia.setBias(-1)):this.gtia.setBias(0);let n=ct[this.antic.mode],h=this.antic.h&1;return(n<8||h)&&(this.gtia.an=this.antic.shiftout()),s(),n==1&&(this.gtia.an=this.antic.shiftout()),r(),n<=2&&(this.gtia.an=this.antic.shiftout()),s(),n==1&&(this.gtia.an=this.antic.shiftout()),r(),1}loadState(t){this.loadControlsState(t),this.cpu.loadState(t.c),this.ram.set(t.ram),this.antic.loadState(t.antic),this.gtia.loadState(t.gtia),this.irq_pokey.loadState(t.pokey),this.lastdmabyte=t.lastdmabyte,this.cart_80=t.cart_80,this.cart_a0=t.cart_a0}saveState(){return{c:this.cpu.saveState(),ram:this.ram.slice(0),antic:this.antic.saveState(),gtia:this.gtia.saveState(),pokey:this.irq_pokey.saveState(),inputs:this.inputs.slice(0),lastdmabyte:this.lastdmabyte,keycode:this.keycode,cart_80:this.cart_80,cart_a0:this.cart_a0}}loadControlsState(t){this.inputs.set(t.inputs),this.keycode=t.keycode}saveControlsState(){return{inputs:this.inputs.slice(0),keycode:this.keycode}}getRasterY(){return this.antic.v}getRasterX(){return this.antic.h}getRasterCanvasPosition(){return{x:this.antic.h*4-this.firstVisibleClock,y:this.antic.v-this.firstVisibleScanline}}getDebugCategories(){return["CPU","Stack","ANTIC","GTIA","POKEY"]}getDebugInfo(t,s){switch(t){case"ANTIC":return M.stateToLongString(s.antic);case"GTIA":return Y.stateToLongString(s.gtia);case"POKEY":return k.stateToLongString(s.pokey)}}getKeyboardFunction(){return(t,s,r,n)=>{if(!this.keyboard_active)return!1;if(n&65){var h=re;if(s==i.VK_F9.c)return this.irq_pokey.generateIRQ(128),!0;for(var f=0;f>8}if(s>t.length)throw new Error("Bad .XEX file format")}h>=0&&(r[n++]=169,r[n++]=160,r[n++]=141,r[n++]=255,r[n++]=213,r[n++]=76,r[n++]=h&255,r[n++]=h>>8,this.ram[10]=0,this.ram[11]=213,this.run_address=54528)}initCartA(){this.cpu.getPC()==61823&&this.xexdata&&this.loadXEX(this.xexdata)}setPaddleInput(t,s){this.irq_pokey.pot_inputs[t]=255-s}getDebugDisplayList(){let t=this.antic.getDlistAddr(),s=()=>{let h=this.read(t);return t=t+1&1023|t&-1024,h},r=[],n=0;for(let h=0;h<256&&n<240;h++){let f=t,l=s(),p=l&15,d="",I=!1,R;if(p==0)R=(l>>4&7)+1,d+=" blank="+R;else{R=Q[p],d+=" mode="+m(p),d+=" lines="+R,I=(l&-65)==1;let j=(l&64)!=0&&(l&15)!=0;if(l&16&&(d+=" HSCROL"),l&32&&(d+=" VSCROL"),l&128&&(d+=" DLI"),I&&l&64?d+=" JVB":I?d+=" JMP":j&&(d+=" LMS"),I||j){let Rt=s(),Dt=s();d+=" $"+m(Dt)+m(Rt)}}if(r.push("$"+m(f)+" y="+n+" "+d),I)break;n+=R}return r}},V=class extends v{newBus(){return{read:T([[0,16383,65535,e=>this.ram[e]],[16384,49151,65535,e=>this.rom?this.rom[e-16384]:0],[49152,53247,31,e=>this.gtia.readReg(e)],[54272,54527,15,e=>this.antic.readReg(e)],[59392,61439,15,e=>this.readPokey(e)],[63488,65535,2047,e=>this.bios[e]]]),write:T([[0,16383,65535,(e,t)=>{this.ram[e]=t}],[49152,53247,31,(e,t)=>{this.gtia.setReg(e,t)}],[54272,54527,15,(e,t)=>{this.antic.setReg(e,t)}],[59392,61439,15,(e,t)=>{this.writePokey(e,t)}]])}}};var G=[{id:"hello.dasm",name:"Hello World",category:"Assembly"},{id:"hellopm.dasm",name:"Hello Sprites"},{id:"helloconio.c",name:"Text Mode",category:"C"},{id:"siegegame.c",name:"Siege Game"},{id:"hellodlist.c",name:"Display List"},{id:"libdemo.c",name:"Common Library"}],yt=G.concat([{id:"scrolldemo.c",name:"Scrolling"},{id:"hello.cpp",name:"Hello Oscar64",category:"C++ (Oscar64)"},{id:"sieve.bas",name:"Benchmark",category:"FastBasic"},{id:"pmtest.bas",name:"Sprites Test"},{id:"dli.bas",name:"DLI Test"},{id:"joyas.bas",name:"Match-3 Game"}]),Et={main:[{name:"RAM",start:0,size:49152,type:"ram"},{name:"Left Cartridge ROM",start:40960,size:8192,type:"rom"},{name:"GTIA",start:53248,size:32,type:"io"},{name:"POKEY",start:53760,size:16,type:"io"},{name:"PIA",start:54016,size:4,type:"io"},{name:"ANTIC",start:54272,size:16,type:"io"},{name:"Cartridge Control Line",start:54784,size:256,type:"io"},{name:"ROM",start:55296,size:2048,type:"rom"},{name:"Character Set",start:57344,size:1024,type:"rom"},{name:"ROM",start:58368,size:7168,type:"rom"}]},At={main:[{name:"RAM",start:0,size:16384,type:"ram"},{name:"Cartridge ROM",start:16384,size:32768,type:"rom"},{name:"GTIA",start:49152,size:32,type:"io"},{name:"ANTIC",start:54272,size:16,type:"io"},{name:"POKEY",start:59392,size:16,type:"io"},{name:"ATARI Character Set",start:63488,size:1024,type:"rom"},{name:"ROM",start:64512,size:1024,type:"rom"}]};function St(a){return a.endsWith(".bas")||a.endsWith(".fb")||a.endsWith(".fbi")?"fastbasic":it(a)}var w=class extends at{constructor(){super(...arguments);this.getToolForFilename=St;this.showHelp=It;this.getROMExtension=ne;this.biosPath="res/altirra/kernel.rom"}newMachine(){return new v}getPresets(){return yt}getDefaultExtensions(){return[...super.getDefaultExtensions(),".bas"]}readAddress(t){return this.machine.readConst(t)}getMemoryMap(){return Et}async start(){let t=await this.loadKernel();await super.start(),this.machine.loadBIOS(t)}async loadKernel(){var t=await fetch(this.biosPath);if(t.status==200||t.size){var s=await t.arrayBuffer();return new Uint8Array(s)}else throw new Error("could not load BIOS file")}getDebugTree(){let t=super.getDebugTree();return t.display_list=this.machine.getDebugDisplayList(),t}},X=class extends w{constructor(){super(...arguments);this.biosPath="res/altirra/superkernel.rom"}getPresets(){return G}newMachine(){return new V}getMemoryMap(){return At}},N=class extends nt{constructor(){super(...arguments);this.getToolForFilename=St;this.getOpcodeMetadata=rt;this.showHelp=It}getPresets(){return G}getDefaultExtensions(){return[".c",".bas",".cpp",".acme",".xa",".ca65",".dasm",".ecs",".wiz"]}},q=class extends N{constructor(){super(...arguments);this.getMemoryMap=function(){return Et}}getPresets(){return yt.filter(t=>!t.id.endsWith(".cpp"))}loadROM(t,s){this.started?(this.loadROMFile(s),this.loadRegion(":cartleft:cart:rom",s)):this.startModule(this.mainElement,{jsfile:"mame8bitws.js",biosfile:"a800xl.zip",cfgfile:"a800xl.cfg",driver:"a800xl",width:336*2,height:450,romfn:"/emulator/cart.rom",romdata:new Uint8Array(s),romsize:8192,preInit:function(r){}})}start(){}},W=class extends N{loadROM(e,t){this.started?(this.loadROMFile(t),this.loadRegion(":cartleft:cart:rom",t)):this.startModule(this.mainElement,{jsfile:"mame8bitws.js",biosfile:"a5200/5200.rom",cfgfile:"a5200.cfg",driver:"a5200",width:336*2,height:450,romfn:"/emulator/cart.rom",romdata:new Uint8Array(t),romsize:32768,preInit:function(s){}})}start(){}getMemoryMap(){return At}};function ne(a){return a==null?".bin":a[0]==255&&a[1]==255?".xex":".rom"}function It(){return"https://8bitworkshop.com/docs/platforms/atari8/"}D["atari8-800.xlmame"]=q;D["atari8-5200.mame"]=W;D["atari8-800"]=w;D["atari8-5200"]=X; -//# sourceMappingURL=atari8-GYR56LP5.js.map diff --git a/gen/atari8-VDKLRU3M.js b/gen/atari8-VDKLRU3M.js new file mode 100644 index 00000000..8a765917 --- /dev/null +++ b/gen/atari8-VDKLRU3M.js @@ -0,0 +1,7 @@ +import{a as ot}from"./chunk-WIKZL6VD.js";import{b as lt,c as ct,d as Y}from"./chunk-LZK7RTFU.js";import{A as nt,J as ht,g as et,k as st,m as it,r as rt,x as at}from"./chunk-2XSLQJUY.js";import{K as R,V as D,W as i,X as J,Z as tt,a as F,aa as T,g,r as E}from"./chunk-TYDI7JZQ.js";import"./chunk-VSC4MAII.js";var kt=[0,25,17,9],Mt=[0,89,97,105],y=0,vt=1,H=2,Q=3,Ct=4,Lt=5,Kt=7,Pt=9,C=10,Vt=11,wt=12,Nt=13,ft=14,Ut=15,L=15;var Ft=13,Yt=106,Ht=105,B=[0,0,8,10,8,16,8,16,8,4,4,2,1,2,1,1],Qt=[0,0,2,2,2,2,4,4,8,8,4,4,4,2,2,2],Bt=[0,0,0,0,0,1,0,1,0,0,2,1,0,0,0,0],dt=[0,0,1,1,2,2,2,2,8,4,4,2,2,2,2,1],k=class{constructor(e,t){this.regs=new Uint8Array(16);this.left=-1;this.right=-1;this.dma_enabled=!1;this.dliop=0;this.mode=0;this.jmp=!1;this.lms=!1;this.dlarg_lo=0;this.dlarg_hi=0;this.period=0;this.scanaddr=0;this.startaddr=0;this.pfbyte=0;this.ch=0;this.linesleft=0;this.yofs=0;this.isfirstline=!1;this.v=0;this.h=0;this.linebuf=new Uint8Array(48);this.dmaclock=0;this.dmaidx=0;this.output=0;this.dramrefresh=!1;this.in_vscroll=0;this.read=e,this.nmi=t}reset(){this.regs.fill(0),this.regs[ft]=0,this.regs[L]=127,this.regs[wt]=0,this.regs[Nt]=255,this.setReg(y,0),this.h=this.v=0,this.startaddr=this.scanaddr=0,this.dmaclock=0}saveState(){return E(0,{},this)}loadState(e){E(0,this,e),this.setReg(y,e.regs[y])}static stateToLongString(e){let t="";return t+="H: "+F(e.h,3)+" V: "+F(e.v,3)+` +`,t+="DLIOp: "+g(e.dliop,2)+" Lines: "+e.yofs+"/"+e.linesleft,t+=" DMA "+(e.dma_enabled?"ON ":"off"),e.dma_enabled&&(t+=" idx "+e.dmaidx+" clk "+g(e.dmaclock)),t+=` +`,t+="Addr: "+g(e.scanaddr,4)+` +`,t+=D(e.regs,0,16).replace("$00","Regs"),t}setReg(e,t){switch(e){case C:this.regs[C]=255;return;case Ut:this.regs[L]=31;return}this.regs[e]=t}readReg(e){switch(e){case L:return this.regs[e];case Vt:return this.v>>1;default:return 255}}processDLIEntry(){if(this.mode==0)this.linesleft=(this.dliop>>4&7)+1,this.dmaclock=0;else{this.linesleft=B[this.mode],this.period=Qt[this.mode],this.jmp?(this.regs[H]=this.dlarg_lo,this.regs[Q]=this.dlarg_hi,this.mode=this.period=0,this.dliop&64&&(this.linesleft=1,this.dma_enabled=!1),this.dmaclock=0):this.lms&&(this.scanaddr=this.dlarg_lo+(this.dlarg_hi<<8)),this.startaddr=this.scanaddr;let e=this.regs[y]&3,t=this.dliop&16?(this.regs[Ct]&15)>>1:0;this.dliop&16&&e<3&&e++,this.left=kt[e]+t,this.right=Mt[e]+t;let s=this.regs[Lt]&15;this.dliop&32^this.in_vscroll&&(this.in_vscroll?this.linesleft=s+1:(this.linesleft-=s,this.yofs+=s),this.linesleft&=15,this.in_vscroll^=32)}}nextLine(){this.linesleft>0&&(this.linesleft--,this.yofs++,this.isfirstline=!1,this.mode>=8&&this.linesleft&&(this.scanaddr=this.startaddr))}triggerNMI(e){this.regs[L]=e|31,this.regs[ft]&e&&this.nmi()}getDlistAddr(){return this.regs[H]+(this.regs[Q]<<8)}nextInsn(){let e=this.getDlistAddr(),t=this.read(e);return e=e+1&1023|e&-1024,this.regs[H]=e&255,this.regs[Q]=e>>8,t}nextScreen(){let e=this.read(this.scanaddr);return this.incScanAddr(),e}incScanAddr(){this.scanaddr=this.scanaddr+1&4095|this.scanaddr&-4096}dlDMAEnabled(){return this.regs[y]&32}isVisibleScanline(){return this.v>=8&&this.v<248}isPlayfieldDMAEnabled(){return this.dma_enabled&&!this.linesleft}isPlayerDMAEnabled(){return this.regs[y]&8}isMissileDMAEnabled(){return this.regs[y]&12}isWSYNC(){return this.regs[C]!=0}clockPulse(){let e=this.isWSYNC();if(!this.isVisibleScanline())this.doVBlank();else{switch(this.h){case 0:this.isMissileDMAEnabled()&&(this.doPlayerMissileDMA(3),e=!0);break;case 1:if(this.isPlayfieldDMAEnabled()){let t=this.nextInsn();this.jmp=(t&-65)==1,this.lms=(t&64)!=0&&(t&15)!=0,this.mode=t&15,this.dliop=t,this.yofs=0,this.isfirstline=!0,e=!0}break;case 2:case 3:case 4:case 5:this.isPlayerDMAEnabled()&&(this.doPlayerMissileDMA(this.h+2),e=!0);break;case 6:case 7:this.isPlayfieldDMAEnabled()&&this.isfirstline&&(this.jmp||this.lms)&&(this.h==6&&(this.dlarg_lo=this.nextInsn()),this.h==7&&(this.dlarg_hi=this.nextInsn()),e=!0);break;case 8:this.isfirstline&&this.processDLIEntry(),this.dliop&128&&this.linesleft==1&&this.triggerNMI(128);break;case 9:break;case 111:this.dma_enabled&&this.nextLine(),++this.v;break}if(this.output=0,this.mode>=2&&this.period){let t=this.h<=Ht;this.dmaclock=this.dmaclock<<1&511,this.dmaclock&1<=8&&this.incScanAddr(),e=t),this.output=this.h>=this.left+3&&this.h<=this.right+2?4:0}}return(this.hYt)&&(this.output=2),this.incHorizCounter(),!e&&this.dramrefresh&&(this.read(0),this.dramrefresh=!1,e=!0),e}incHorizCounter(){switch(this.h){case 25:case 29:case 33:case 37:case 41:case 45:case 49:case 53:case 57:this.dramrefresh=!0;break;case 102:this.regs[C]=0;break;case 113:this.h=0;return}++this.h}doVBlank(){this.linesleft=this.mode=this.period=0,this.h==111&&this.v++,this.v==248&&this.h==0&&this.triggerNMI(64),this.v==262&&this.h==112&&(this.v=0),this.v==7&&this.h==113&&(this.dma_enabled=this.dlDMAEnabled()!=0),this.output=2,this.dmaclock=0}doPlayerMissileDMA(e){let t=this.regs[y]&16,s=this.regs[Kt]<<8;t?(s&=63488,s|=e<<8,s|=this.v&255):(s&=64512,s|=e<<7,s|=this.v>>1),this.read(s)}readBitmapData(){let e=this.mode;if(e<8){let t=this.ch,s=this.yofs>>Bt[this.mode],r=s&7,a=this.regs[Pt];(e&14)==6?(t&=63,a&=254):(t&=127,a&=252);let l=(t<<3)+(a<<8);if((e&14)==2){let c=this.regs[vt],o=e==3&&(t&96)==96;c&4?this.pfbyte=this.read(l+(r^7)):this.pfbyte=this.read(l+r),o&&s<2&&(this.pfbyte=0),!o&&s>7&&(this.pfbyte=0),this.ch&128&&(c&1&&(this.pfbyte=0),c&2&&(this.pfbyte^=255))}else this.pfbyte=this.read(l+r)}else this.pfbyte=this.nextScreen()}shiftout(){if(this.output==4)switch(this.mode){case 2:case 3:case 15:{let e=this.pfbyte>>7&1;return this.pfbyte<<=1,e?8:6}case 6:case 7:{let e=this.pfbyte>>7&1;return this.pfbyte<<=1,e?(this.ch>>6)+4:0}case 9:case 11:case 12:{let e=this.pfbyte>>7&1;return this.pfbyte<<=1,e?4:0}case 4:case 5:{let e=this.pfbyte>>6&3;return this.pfbyte<<=2,this.ch&128?[0,4,5,7][e]:[0,4,5,6][e]}case 8:case 10:case 13:case 14:{let e=this.pfbyte>>6&3;return this.pfbyte<<=2,[0,4,5,6][e]}}return this.output}};var d=0;var zt=2;var xt=4;var ut=6;var _=8,Xt=9,qt=10,Wt=11,Gt=13,z=14,A=15;var jt=8,Zt=9,$t=10,Jt=13,I=14,K=15;var mt=128,gt=64,te=32,_t=16,pt=8;var ee=1,bt=28,se=114;var yt=511,St=131071,u=0,m=1,p=2,x=3,S=114,ie=15;var re=8;var X,P;function ae(){X=new Uint8Array(511),P=new Uint8Array(16385);let n=511;for(let e=0;e<511;e++)n=(((n>>5^n)&1)<<8)+(n>>1),X[e]=n;n=131071;for(let e=0;e<16385;e++)n=(((n>>5^n)&255)<<9)+(n>>8),P[e]=n>>1}var M=class{constructor(e,t){this.irq=e;this.antic_xpos=t;this.regs=new Uint8Array(16);this.readregs=new Uint8Array(16);this.divnirq=new Uint32Array(4);this.divnmax=new Uint32Array(4);this.pot_inputs=new Uint8Array(8);this.basemult=0;this.pot_scanline=0;this.random_scanline_counter=0;this.kbcode=0;this.DELAYED_SERIN_IRQ=0;this.DELAYED_SEROUT_IRQ=0;this.DELAYED_XMTDONE_IRQ=0;this.init()}saveState(){return E(0,{},this)}loadState(e){E(0,this,e)}init(){this.readregs.fill(255),this.readregs[K]=239,this.basemult=bt,this.pot_inputs.fill(128),ae()}read(e){let t=this.readregs[e];switch(e&=15,e){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:return t=this.pot_inputs[e],t>3;s&=7,t=(P[r]>>s)+(P[r+1]<<8-s)}}break}return t&255}write(e,t){switch(e&=15,this.regs[e]=t,e){case _:t&ee?this.basemult=se:this.basemult=bt,this.update_counter(1<=3?(this.readregs[I]|=8,this.DELAYED_XMTDONE_IRQ=2*this.DELAYED_SEROUT_IRQ-2):(this.DELAYED_SEROUT_IRQ=0,this.DELAYED_XMTDONE_IRQ=0));break;case Xt:this.divnirq[u]=this.divnmax[u],this.divnirq[m]=this.divnmax[m],this.divnirq[p]=this.divnmax[p],this.divnirq[x]=this.divnmax[x];break;case A:t&4&&(this.pot_scanline=228),(t&3)==0&&(this.DELAYED_SERIN_IRQ=0,this.DELAYED_SEROUT_IRQ=0,this.DELAYED_XMTDONE_IRQ=0);break}this.snd_update(e)}update_counter(e){e&1<0&&--this.DELAYED_SERIN_IRQ==0&&(this.readregs[Jt]=this.SIO_GetByte(),this.generateIRQ(32)),this.DELAYED_SEROUT_IRQ>0&&--this.DELAYED_SEROUT_IRQ==0&&this.generateIRQ(16),this.DELAYED_XMTDONE_IRQ>0&&--this.DELAYED_XMTDONE_IRQ==0&&this.generateIRQ(8),this.advanceIRQTimer(u,1),this.advanceIRQTimer(m,2),this.advanceIRQTimer(x,4))}advanceIRQTimer(e,t){(this.divnirq[e]-=S)<0&&(this.divnirq[e]+=this.divnmax[e],this.generateIRQ(t))}generateIRQ(e){this.regs[z]&e&&(this.irq(),this.readregs[I]&=~e)}static stateToLongString(e){let t="";return t+=`Write Registers: +`,t+=D(e.regs,0,16),t+=`Read Registers: +`,t+=D(e.readregs,0,16),t}CASSETTE_IOLineStatus(){return 0}siocheck(){return((this.regs[d+p]==40||this.regs[d+p]==16||this.regs[d+p]==8||this.regs[d+p]==10)&&this.regs[d+x]==0||(this.regs[A]&120)==40)&&(this.regs[_]&40)==40}SIO_PutByte(e){console.log("SIO put byte",e)}SIO_GetByte(){return 0}};var V=class{constructor(e){if(this.data=e,e.length>=16&&(e[0]|e[1]<<8)==662){let s=(e[2]|e[3]<<8)*16,r=e[4]|e[5]<<8;this.sectorsize=r==256?256:128,this.sectorsize==256?this.numsectors=3+Math.floor((s-16-384)/256):this.numsectors=Math.floor((s-16)/128)}else this.sectorsize=128,this.numsectors=Math.floor(e.length/128)}sectorOffset(e){return this.sectorsize==256?e<3?16+e*128:400+(e-3)*256:16+e*128}readSector(e,t){let s=e-1;if(s<0||s>=this.numsectors)return null;let r=this.sectorOffset(s);return this.data.subarray(r,Math.min(r+t,this.data.length))}writeSector(e,t,s){let r=e-1;if(r<0||r>=this.numsectors)return!1;let a=this.sectorOffset(r);return this.data.set(t.subarray(0,Math.min(s,this.data.length-a)),a),!0}};var q=160,ne=240,he=241,oe=[173,0,3,201,49,144,12,201,64,176,8,141,240,213,173,241,213,168,96,76,80,233],le=[i.VK_L,i.VK_J,i.VK_SEMICOLON,i.VK_F4,i.VK_F5,i.VK_K,i.VK_BACK_SLASH,i.VK_TILDE,i.VK_O,null,i.VK_P,i.VK_U,i.VK_ENTER,i.VK_I,i.VK_MINUS2,i.VK_EQUALS2,i.VK_V,i.VK_F7,i.VK_C,i.VK_F6,i.VK_F4,i.VK_B,i.VK_X,i.VK_Z,i.VK_4,null,i.VK_3,i.VK_6,i.VK_ESCAPE,i.VK_5,i.VK_2,i.VK_1,i.VK_COMMA,i.VK_SPACE,i.VK_PERIOD,i.VK_N,null,i.VK_M,i.VK_SLASH,null,i.VK_R,null,i.VK_E,i.VK_Y,i.VK_TAB,i.VK_T,i.VK_W,i.VK_Q,i.VK_9,null,i.VK_0,i.VK_7,i.VK_BACK_SPACE,i.VK_8,null,null,i.VK_F,i.VK_H,i.VK_D,null,i.VK_CAPS_LOCK,i.VK_G,i.VK_S,i.VK_A],ce=tt([[i.UP,0,1],[i.DOWN,0,2],[i.LEFT,0,4],[i.RIGHT,0,8],[{c:16,n:"Shift",plyr:0,button:0},2,1],[i.VK_F1,3,1],[i.VK_F2,3,2],[i.VK_F3,3,4]]),v=class extends rt{constructor(){super();this.cpuFrequency=1789773;this.numTotalScanlines=262;this.cpuCyclesPerLine=114;this.canvasWidth=336;this.numVisibleScanlines=224;this.aspectRatio=this.canvasWidth/this.numVisibleScanlines*.857;this.firstVisibleScanline=16;this.firstVisibleClock=76;this.defaultROMSize=32768;this.overscan=!0;this.audioOversample=2;this.sampleRate=this.numTotalScanlines*60*this.audioOversample;this.run_address=-1;this.inputs=new Uint8Array(4);this.linergb=new Uint32Array(this.canvasWidth);this.lastdmabyte=0;this.keycode=0;this.cart_80=!1;this.cart_a0=!1;this.xexdata=null;this.disk=null;this.keyboard_active=!0;this.d500=new Uint8Array(256);this.cpu=new it,this.ram=new Uint8Array(65536),this.bios=new Uint8Array(10240),this.bus=this.newBus(),this.connectCPUMemoryBus(this.bus),this.antic=new k(this.readDMA.bind(this),this.antic_nmi.bind(this)),this.gtia=new Y,this.irq_pokey=new M(this.pokey_irq.bind(this),()=>this.antic.h),this.audio_pokey=et(1),this.audioadapter=new st(this.audio_pokey.pokey1,this.audioOversample,this.sampleRate),this.handler=J(this.inputs,ce,this.getKeyboardFunction(),!0)}newBus(){return{read:T([[0,32767,65535,t=>this.ram[t]],[32768,40959,65535,t=>this.cart_80?this.rom[t-32768]:this.ram[t]],[40960,49151,65535,t=>this.cart_a0?this.rom[t-32768]:this.ram[t]],[53248,53503,31,t=>this.gtia.readReg(t)],[53760,54015,15,t=>this.readPokey(t)],[54016,54271,15,t=>this.readPIA(t)],[54272,54527,15,t=>this.antic.readReg(t)],[54528,54783,255,t=>this.d500[t]],[55296,65535,65535,t=>this.bios[t-55296]]]),write:T([[0,49146,65535,(t,s)=>{this.ram[t]=s}],[49147,49151,65535,(t,s)=>{this.ram[t]=s,this.initCartA()}],[53248,53503,31,(t,s)=>{this.gtia.setReg(t,s)}],[53760,54015,15,(t,s)=>{this.writePokey(t,s)}],[54272,54527,15,(t,s)=>{this.antic.setReg(t,s)}],[54528,54783,255,(t,s)=>{this.writeMapper(t,s)}]])}}loadBIOS(t){this.bios.set(t),this.installSIOStub()}installSIOStub(){this.bios[3161]=76,this.bios[3162]=54528+q&255,this.bios[3163]=54528+q>>8,this.d500.set(oe,q)}reset(){super.reset(),this.antic.reset(),this.gtia.reset(),this.keycode=0}read(t){return this.bus.read(t)}readDMA(t){let s=this.bus.read(t);return this.probe.logDMARead(t,s),this.lastdmabyte=s,s}readConst(t){return t<53248||t>=54528?this.bus.read(t):255}write(t,s){this.bus.write(t,s)}readPokey(t){switch(t&15){case 9:return this.keycode&255;case 15:return~this.keycode>>6&4|~this.keycode>>3&8|18;default:return this.irq_pokey.read(t)}}readPIA(t){if(t==0||t==1)return~this.inputs[t]}writePokey(t,s){this.audio_pokey.pokey1.setRegister(t,s),this.irq_pokey.write(t,s)}startScanline(){this.gtia.sync();for(let t=0;t<4;t++)this.gtia.readregs[lt+t]=~this.inputs[2]>>t&1;this.gtia.readregs[ct]=~this.inputs[3]&7,this.audio&&this.audioadapter.generate(this.audio),this.irq_pokey.advanceScanline()}drawScanline(){let t=this.antic.v-this.firstVisibleScanline;t>=0&&t{this.gtia.clockPulse1(),this.linergb[t++]=this.gtia.rgb},r=()=>{this.gtia.clockPulse2(),this.linergb[t++]=this.gtia.rgb};this.gtia.clockPulse4(),this.antic.dliop&16&&this.antic.regs[4]&1?(t+=2,this.gtia.setBias(-1)):this.gtia.setBias(0);let a=dt[this.antic.mode],l=this.antic.h&1;return(a<8||l)&&(this.gtia.an=this.antic.shiftout()),s(),a==1&&(this.gtia.an=this.antic.shiftout()),r(),a<=2&&(this.gtia.an=this.antic.shiftout()),s(),a==1&&(this.gtia.an=this.antic.shiftout()),r(),1}loadState(t){this.loadControlsState(t),this.cpu.loadState(t.c),this.ram.set(t.ram),this.antic.loadState(t.antic),this.gtia.loadState(t.gtia),this.irq_pokey.loadState(t.pokey),this.lastdmabyte=t.lastdmabyte,this.cart_80=t.cart_80,this.cart_a0=t.cart_a0}saveState(){return{c:this.cpu.saveState(),ram:this.ram.slice(0),antic:this.antic.saveState(),gtia:this.gtia.saveState(),pokey:this.irq_pokey.saveState(),inputs:this.inputs.slice(0),lastdmabyte:this.lastdmabyte,keycode:this.keycode,cart_80:this.cart_80,cart_a0:this.cart_a0}}loadControlsState(t){this.inputs.set(t.inputs),this.keycode=t.keycode}saveControlsState(){return{inputs:this.inputs.slice(0),keycode:this.keycode}}getRasterY(){return this.antic.v}getRasterX(){return this.antic.h}getRasterCanvasPosition(){return{x:this.antic.h*4-this.firstVisibleClock,y:this.antic.v-this.firstVisibleScanline}}getDebugCategories(){return["CPU","Stack","ANTIC","GTIA","POKEY"]}getDebugInfo(t,s){switch(t){case"ANTIC":return k.stateToLongString(s.antic);case"GTIA":return Y.stateToLongString(s.gtia);case"POKEY":return M.stateToLongString(s.pokey)}}getKeyboardFunction(){return(t,s,r,a)=>{if(!this.keyboard_active)return!1;if(a&65){var l=le;if(s==i.VK_F9.c)return this.irq_pokey.generateIRQ(128),!0;for(var c=0;c>8}if(s>t.length)throw new Error("Bad .XEX file format")}l>=0&&(r[a++]=169,r[a++]=160,r[a++]=141,r[a++]=255,r[a++]=213,r[a++]=76,r[a++]=l&255,r[a++]=l>>8,this.ram[10]=0,this.ram[11]=213,this.run_address=54528)}initCartA(){this.cpu.getPC()==61823&&this.xexdata&&this.loadXEX(this.xexdata)}setPaddleInput(t,s){this.irq_pokey.pot_inputs[t]=255-s}getDebugDisplayList(){let t=this.antic.getDlistAddr(),s=()=>{let l=this.read(t);return t=t+1&1023|t&-1024,l},r=[],a=0;for(let l=0;l<256&&a<240;l++){let c=t,o=s(),f=o&15,h="",b=!1,O;if(f==0)O=(o>>4&7)+1,h+=" blank="+O;else{O=B[f],h+=" mode="+g(f),h+=" lines="+O,b=(o&-65)==1;let $=(o&64)!=0&&(o&15)!=0;if(o&16&&(h+=" HSCROL"),o&32&&(h+=" VSCROL"),o&128&&(h+=" DLI"),b&&o&64?h+=" JVB":b?h+=" JMP":$&&(h+=" LMS"),b||$){let Dt=s(),Tt=s();h+=" $"+g(Tt)+g(Dt)}}if(r.push("$"+g(c)+" y="+a+" "+h),b)break;a+=O}return r}},w=class extends v{newBus(){return{read:T([[0,16383,65535,e=>this.ram[e]],[16384,49151,65535,e=>this.rom?this.rom[e-16384]:0],[49152,53247,31,e=>this.gtia.readReg(e)],[54272,54527,15,e=>this.antic.readReg(e)],[59392,61439,15,e=>this.readPokey(e)],[63488,65535,2047,e=>this.bios[e]]]),write:T([[0,16383,65535,(e,t)=>{this.ram[e]=t}],[49152,53247,31,(e,t)=>{this.gtia.setReg(e,t)}],[54272,54527,15,(e,t)=>{this.antic.setReg(e,t)}],[59392,61439,15,(e,t)=>{this.writePokey(e,t)}]])}}};var Z=[{id:"hello.dasm",name:"Hello World",category:"Assembly"},{id:"hellopm.dasm",name:"Hello Sprites"},{id:"helloconio.c",name:"Text Mode",category:"C"},{id:"siegegame.c",name:"Siege Game"},{id:"hellodlist.c",name:"Display List"},{id:"libdemo.c",name:"Library Demo"}],At=Z.concat([{id:"scrolldemo.c",name:"Scrolling"},{id:"hello.cpp",name:"Hello Oscar64",category:"Oscar64"},{id:"libdemo.cpp",name:"Library Demo Oscar64"},{id:"sieve.bas",name:"Benchmark",category:"FastBasic"},{id:"pmtest.bas",name:"Sprites Test"},{id:"dli.bas",name:"DLI Test"},{id:"joyas.bas",name:"Match-3 Game"}]),Et={main:[{name:"RAM",start:0,size:49152,type:"ram"},{name:"Left Cartridge ROM",start:40960,size:8192,type:"rom"},{name:"GTIA",start:53248,size:32,type:"io"},{name:"POKEY",start:53760,size:16,type:"io"},{name:"PIA",start:54016,size:4,type:"io"},{name:"ANTIC",start:54272,size:16,type:"io"},{name:"Cartridge Control Line",start:54784,size:256,type:"io"},{name:"ROM",start:55296,size:2048,type:"rom"},{name:"Character Set",start:57344,size:1024,type:"rom"},{name:"ROM",start:58368,size:7168,type:"rom"}]},It={main:[{name:"RAM",start:0,size:16384,type:"ram"},{name:"Cartridge ROM",start:16384,size:32768,type:"rom"},{name:"GTIA",start:49152,size:32,type:"io"},{name:"ANTIC",start:54272,size:16,type:"io"},{name:"POKEY",start:59392,size:16,type:"io"},{name:"ATARI Character Set",start:63488,size:1024,type:"rom"},{name:"ROM",start:64512,size:1024,type:"rom"}]};function Ot(n){return n.endsWith(".bas")||n.endsWith(".fb")||n.endsWith(".fbi")?"fastbasic":at(n)}var N=class extends ht{constructor(){super(...arguments);this.getToolForFilename=Ot;this.showHelp=Rt;this.getROMExtension=fe;this.biosPath="res/altirra/kernel.rom"}newMachine(){return new v}getPresets(){return At}getDefaultExtensions(){return[...super.getDefaultExtensions(),".bas"]}readAddress(t){return this.machine.readConst(t)}getMemoryMap(){return Et}async start(){let t=await this.loadKernel();await super.start(),this.machine.loadBIOS(t)}async loadKernel(){var t=await fetch(this.biosPath);if(t.status==200||t.size){var s=await t.arrayBuffer();return new Uint8Array(s)}else throw new Error("could not load BIOS file")}getDebugTree(){let t=super.getDebugTree();return t.display_list=this.machine.getDebugDisplayList(),t}},W=class extends N{constructor(){super(...arguments);this.biosPath="res/altirra/superkernel.rom"}getPresets(){return Z}newMachine(){return new w}getMemoryMap(){return It}},U=class extends ot{constructor(){super(...arguments);this.getToolForFilename=Ot;this.getOpcodeMetadata=nt;this.showHelp=Rt}getPresets(){return Z}getDefaultExtensions(){return[".c",".bas",".cpp",".acme",".xa",".ca65",".dasm",".ecs",".wiz"]}},G=class extends U{constructor(){super(...arguments);this.getMemoryMap=function(){return Et}}getPresets(){return At.filter(t=>!t.id.endsWith(".cpp"))}loadROM(t,s){this.started?(this.loadROMFile(s),this.loadRegion(":cartleft:cart:rom",s)):this.startModule(this.mainElement,{jsfile:"mame8bitws.js",biosfile:"a800xl.zip",cfgfile:"a800xl.cfg",driver:"a800xl",width:336*2,height:450,romfn:"/emulator/cart.rom",romdata:new Uint8Array(s),romsize:8192,preInit:function(r){}})}start(){}},j=class extends U{loadROM(e,t){this.started?(this.loadROMFile(t),this.loadRegion(":cartleft:cart:rom",t)):this.startModule(this.mainElement,{jsfile:"mame8bitws.js",biosfile:"a5200/5200.rom",cfgfile:"a5200.cfg",driver:"a5200",width:336*2,height:450,romfn:"/emulator/cart.rom",romdata:new Uint8Array(t),romsize:32768,preInit:function(s){}})}start(){}getMemoryMap(){return It}};function fe(n){return n==null?".bin":n[0]==150&&n[1]==2?".atr":n[0]==255&&n[1]==255?".xex":".rom"}function Rt(){return"https://8bitworkshop.com/docs/platforms/atari8/"}R["atari8-800.xlmame"]=G;R["atari8-5200.mame"]=j;R["atari8-800"]=N;R["atari8-5200"]=W; +//# sourceMappingURL=atari8-VDKLRU3M.js.map diff --git a/gen/atari8-GYR56LP5.js.map b/gen/atari8-VDKLRU3M.js.map similarity index 52% rename from gen/atari8-GYR56LP5.js.map rename to gen/atari8-VDKLRU3M.js.map index 2e91560c..f335bbda 100644 --- a/gen/atari8-GYR56LP5.js.map +++ b/gen/atari8-VDKLRU3M.js.map @@ -1,7 +1,7 @@ { "version": 3, - "sources": ["../src/machine/chips/antic.ts", "../src/machine/chips/pokey.ts", "../src/machine/atari8.ts", "../src/platform/atari8.ts"], - "sourcesContent": ["import { dumpRAM } from \"../../common/emu\";\nimport { hex, lpad, safe_extend } from \"../../common/util\";\n\n// ANTIC\n// https://www.atarimax.com/jindroush.atari.org/atanttim.html\n// http://www.virtualdub.org/blog/pivot/entry.php?id=243\n// http://www.beipmu.com/Antic_Timings.txt\n// https://user.xmission.com/~trevin/atari/antic_regs.html\n// https://user.xmission.com/~trevin/atari/antic_insns.html\n// http://www.atarimuseum.com/videogames/consoles/5200/conv_to_5200.html\n// https://www.virtualdub.org/downloads/Altirra%20Hardware%20Reference%20Manual.pdf\n\nconst PF_LEFT = [0, 25, 17, 9];\nconst PF_RIGHT = [0, 25 + 64, 17 + 80, 9 + 96];\n\nconst DMACTL = 0;\nconst CHACTL = 1;\nconst DLISTL = 2;\nconst DLISTH = 3;\nconst HSCROL = 4;\nconst VSCROL = 5;\nconst PMBASE = 7;\nconst CHBASE = 9;\nconst WSYNC = 10;\nconst VCOUNT = 11;\nconst PENH = 12;\nconst PENV = 13;\nconst NMIEN = 14;\nconst NMIRES = 15;\nconst NMIST = 15;\n\nconst PFNONE = 0;\nconst PFNARROW = 1;\nconst PFNORMAL = 2;\nconst PFWIDE = 3;\n\nconst NMIST_CYCLE = 12;\nconst NMI_CYCLE = 24;\nconst WSYNC_CYCLE = 212;\n\nconst ANTIC_LEFT = 17 - 4; // gtia 34, 4 cycle delay\nconst ANTIC_RIGHT = 110 - 4; // gtia 221, 4 cycle delay\nconst LAST_DMA_H = 105; // last DMA cycle\n\nexport const MODE_LINES = [0, 0, 8, 10, 8, 16, 8, 16, 8, 4, 4, 2, 1, 2, 1, 1];\n// how many bits before DMA clock repeats?\nconst MODE_PERIOD = [0, 0, 2, 2, 2, 2, 4, 4, 8, 8, 4, 4, 4, 2, 2, 2];\nconst MODE_YPERIOD = [0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 2, 1, 0, 0, 0, 0];\n//const MODE_BPP = [0, 0, 1, 1, 2, 2, 1, 1, 2, 1, 2, 1, 1, 2, 2, 1];\n// how many color clocks / pixel * 2\nexport const MODE_SHIFT = [0, 0, 1, 1, 2, 2, 2, 2, 8, 4, 4, 2, 2, 2, 2, 1];\n\nexport class ANTIC {\n read: (address: number) => number;\t// bus read function\n nmi: () => void; // generate NMI\n\n regs = new Uint8Array(0x10);\t\t\t\t// registers\n\n // Initialized so they are always own properties and therefore always\n // round-trip through saveState()/loadState() -- safe_extend() copies only\n // the keys present in the source, so a field that is merely declared is\n // absent from a save state and keeps its old value on load, which leaks\n // state backwards across a rewind. -1 behaves exactly as undefined did in\n // every comparison below (h is never negative, and h >= 2 && h <= 1 is\n // never true), so this does not change emulation.\n left: number = -1;\n right: number = -1;\t\t\t\t// left/right clocks for mode\n\n dma_enabled: boolean = false;\n dliop: number = 0; // dli operation\n mode: number = 0;\t\t\t// current mode\n jmp = false; // TODO\n lms = false; // TODO\n dlarg_lo: number = 0;\n dlarg_hi: number = 0;\n period: number = 0;\t\t// current mode period bitmask\n scanaddr: number = 0; // Scan Address (via LMS)\n startaddr: number = 0;\t// Start of line Address\n pfbyte: number = 0;\t\t// playfield byte fetched\n ch: number = 0;\t\t\t\t// char read\n linesleft: number = 0; // # of lines left in mode\n yofs: number = 0;\t\t\t// yofs fine\n isfirstline: boolean = false;\n v: number = 0;\t\t\t\t\t// vertical scanline #\n h: number = 0;\t\t\t\t\t// horizontal color clock\n\n linebuf = new Uint8Array(48);\n dmaclock: number = 0;\n dmaidx: number = 0;\n output: number = 0;\n dramrefresh = false;\n in_vscroll = 0;\n\n constructor(readfn, nmifn) {\n this.read = readfn; // bus read function\n this.nmi = nmifn; // NMI function\n }\n reset() {\n this.regs.fill(0);\n this.regs[NMIEN] = 0x00;\n this.regs[NMIST] = 0x7f;\n this.regs[PENH] = 0x00;\n this.regs[PENV] = 0xff;\n this.setReg(DMACTL, 0x0);\n this.h = this.v = 0;\n this.startaddr = this.scanaddr = 0;\n this.dmaclock = 0;\n }\n saveState() {\n return safe_extend(0, {}, this);\n }\n loadState(s) {\n safe_extend(0, this, s);\n this.setReg(DMACTL, s.regs[DMACTL]);\n }\n static stateToLongString(state): string {\n let s = \"\";\n s += \"H: \" + lpad(state.h, 3) + \" V: \" + lpad(state.v, 3) + \"\\n\";\n s += \"DLIOp: \" + hex(state.dliop, 2) + \" Lines: \" + state.yofs + \"/\" + state.linesleft;\n s += \" DMA \" + (state.dma_enabled ? \"ON \" : \"off\")\n if (state.dma_enabled) s += \" idx \" + state.dmaidx + \" clk \" + hex(state.dmaclock) \n s += \"\\n\"\n s += \"Addr: \" + hex(state.scanaddr, 4) + \"\\n\";\n s += dumpRAM(state.regs, 0, 16).replace('$00', 'Regs');\n return s;\n }\n setReg(a: number, v: number) {\n switch (a) {\n case WSYNC:\n this.regs[WSYNC] = 0xff;\n return; // this is readonly (we reset it)\n case NMIRES:\n this.regs[NMIST] = 0x1f;\n return; // this is readonly, don't mess with it\n }\n this.regs[a] = v;\n }\n readReg(a: number) {\n switch (a) {\n case NMIST:\n return this.regs[a];\n case VCOUNT:\n return this.v >> 1;\n default:\n return 0xff;\n }\n }\n processDLIEntry() {\n if (this.mode == 0) { // N Blank Lines\n this.linesleft = ((this.dliop >> 4) & 7) + 1;\n this.dmaclock = 0;\n } else {\n this.linesleft = MODE_LINES[this.mode];\n this.period = MODE_PERIOD[this.mode];\n if (this.jmp) {\n this.regs[DLISTL] = this.dlarg_lo;\n this.regs[DLISTH] = this.dlarg_hi;\n this.mode = this.period = 0;\n // JVB (Jump and wait for Vertical Blank)\n if (this.dliop & 0x40) {\n this.linesleft = 1; //(248 - this.v) & 0xff; // TODO?\n this.dma_enabled = false;\n }\n this.dmaclock = 0;\n } else if (this.lms) {\n this.scanaddr = this.dlarg_lo + (this.dlarg_hi << 8);\n //console.log('scanaddr', hex(this.scanaddr));\n }\n this.startaddr = this.scanaddr;\n // horiz scroll\n let effwidth = this.regs[DMACTL] & 3;\n let hscroll = (this.dliop & 0x10) ? (this.regs[HSCROL] & 15) >> 1 : 0;\n if ((this.dliop & 0x10) && effwidth < 3) effwidth++;\n this.left = PF_LEFT[effwidth] + hscroll;\n this.right = PF_RIGHT[effwidth] + hscroll;\n // vertical scroll\n let vscrol = this.regs[VSCROL] & 0xf;\n if ((this.dliop & 0x20) ^ this.in_vscroll) {\n if (this.in_vscroll) {\n this.linesleft = vscrol+1; // exiting\n } else {\n this.linesleft -= vscrol; // entering\n this.yofs += vscrol;\n }\n this.linesleft &= 0xf;\n this.in_vscroll ^= 0x20;\n }\n }\n }\n\n nextLine() {\n if (this.linesleft > 0) {\n this.linesleft--;\n this.yofs++;\n this.isfirstline = false;\n if (this.mode >= 8 && this.linesleft) {\n this.scanaddr = this.startaddr; // reset line addr\n }\n }\n }\n\n triggerNMI(mask: number) {\n this.regs[NMIST] = mask | 0x1f;\n if (this.regs[NMIEN] & mask) {\n this.nmi();\n }\n }\n\n getDlistAddr() {\n return this.regs[DLISTL] + (this.regs[DLISTH] << 8);\n }\n\n nextInsn(): number {\n let pc = this.getDlistAddr();\n let b = this.read(pc);\n //console.log('nextInsn', hex(pc), hex(b), this.v);\n pc = ((pc + 1) & 0x3ff) | (pc & ~0x3ff);\n this.regs[DLISTL] = pc & 0xff;\n this.regs[DLISTH] = pc >> 8;\n return b;\n }\n\n nextScreen(): number {\n let b = this.read(this.scanaddr);\n this.incScanAddr();\n return b;\n }\n incScanAddr() {\n this.scanaddr = ((this.scanaddr + 1) & 0xfff) | (this.scanaddr & ~0xfff);\n }\n\n dlDMAEnabled() { return this.regs[DMACTL] & 0b100000; }\n\n isVisibleScanline() {\n return this.v >= 8 && this.v < 248;\n }\n isPlayfieldDMAEnabled() {\n return this.dma_enabled && !this.linesleft;\n }\n isPlayerDMAEnabled() {\n return this.regs[DMACTL] & 0b1000;\n }\n isMissileDMAEnabled() {\n return this.regs[DMACTL] & 0b1100;\n }\n isWSYNC() {\n return this.regs[WSYNC] != 0;\n }\n\n clockPulse(): boolean {\n let did_dma = this.isWSYNC();\n if (!this.isVisibleScanline()) {\n this.doVBlank();\n } else {\n switch (this.h) {\n case 0:\n if (this.isMissileDMAEnabled()) {\n this.doPlayerMissileDMA(3);\n did_dma = true;\n }\n break;\n case 1:\n if (this.isPlayfieldDMAEnabled()) {\n let op = this.nextInsn(); // get mode\n // TODO: too many booleans\n this.jmp = (op & ~0x40) == 0x01; // JMP insn?\n this.lms = (op & 0x40) != 0 && (op & 0xf) != 0; // LMS insn?\n this.mode = op & 0xf;\n this.dliop = op;\n this.yofs = 0;\n this.isfirstline = true;\n did_dma = true;\n }\n break;\n case 2: case 3: case 4: case 5:\n if (this.isPlayerDMAEnabled()) {\n this.doPlayerMissileDMA(this.h + 2);\n did_dma = true;\n }\n break;\n case 6:\n case 7:\n if (this.isPlayfieldDMAEnabled() && this.isfirstline && (this.jmp || this.lms)) {\n if (this.h == 6) this.dlarg_lo = this.nextInsn();\n if (this.h == 7) this.dlarg_hi = this.nextInsn();\n did_dma = true;\n }\n break;\n case 8:\n // TODO? is this at cycle 8?\n if (this.isfirstline) {\n this.processDLIEntry();\n }\n if (this.dliop & 0x80) { // TODO: what if DLI disabled?\n if (this.linesleft == 1) {\n this.triggerNMI(0x80); // DLI interrupt\n }\n }\n break;\n case 9:\n break;\n case 111:\n if (this.dma_enabled) this.nextLine();\n ++this.v;\n break;\n }\n this.output = 0; // background color (TODO: only for blank lines)\n if (this.mode >= 2 && this.period) {\n let candma = this.h <= LAST_DMA_H;\n this.dmaclock = (this.dmaclock << 1) & 0x1ff;\n if (this.dmaclock & (1 << this.period)) {\n this.dmaclock |= 1;\n }\n if (this.h == this.left) { this.dmaclock |= 1; this.dmaidx = 0; }\n if (this.h == this.right) { this.dmaclock &= ~1; this.dmaidx++; }\n if (this.dmaclock & 1) {\n if (this.mode < 8 && this.isfirstline) { // only read chars on 1st line\n if (candma) {\n this.linebuf[this.dmaidx] = this.nextScreen(); // read char name\n } else {\n this.incScanAddr();\n }\n did_dma = candma;\n }\n this.dmaidx++;\n } else if (this.dmaclock & 8) {\n this.ch = this.linebuf[this.dmaidx - 4 / this.period]; // latch char\n if (candma) {\n this.readBitmapData(); // read bitmap\n } else {\n if (this.mode >= 8) this.incScanAddr();\n }\n did_dma = candma;\n }\n this.output = this.h >= this.left + 3 && this.h <= this.right + 2 ? 4 : 0;\n }\n }\n if (this.h < ANTIC_LEFT || this.h > ANTIC_RIGHT) this.output = 2;\n this.incHorizCounter();\n if (!did_dma && this.dramrefresh) {\n this.read(0); // to log a VRAM_READ event\n this.dramrefresh = false;\n did_dma = true;\n }\n return did_dma;\n }\n incHorizCounter() {\n switch (this.h) {\n case 25: case 25 + 4 * 1: case 25 + 4 * 2: case 25 + 4 * 3: case 25 + 4 * 4:\n case 25 + 4 * 5: case 25 + 4 * 6: case 25 + 4 * 7: case 25 + 4 * 8:\n this.dramrefresh = true;\n break;\n case 102:\n this.regs[WSYNC] = 0; // TODO: dram refresh delay to 106?\n break;\n case 113:\n this.h = 0;\n return\n }\n ++this.h;\n }\n doVBlank() {\n this.linesleft = this.mode = this.period = 0;\n if (this.h == 111) { this.v++; }\n if (this.v == 248 && this.h == 0) { this.triggerNMI(0x40); } // VBI\n if (this.v == 262 && this.h == 112) { this.v = 0; }\n if (this.v == 7 && this.h == 113) { \n this.dma_enabled = this.dlDMAEnabled() != 0;\n }\n this.output = 2; // blank\n this.dmaclock = 0;\n }\n\n doPlayerMissileDMA(section: number) {\n let oneline = this.regs[DMACTL] & 0x10;\n let pmaddr = this.regs[PMBASE] << 8;\n if (oneline) {\n pmaddr &= 0xf800;\n pmaddr |= section << 8;\n pmaddr |= this.v & 0xff;\n } else {\n pmaddr &= 0xfc00;\n pmaddr |= section << 7;\n pmaddr |= this.v >> 1;\n }\n this.read(pmaddr);\n }\n\n readBitmapData() {\n const mode = this.mode;\n if (mode < 8) {\t// character mode\n let ch = this.ch;\n let y = this.yofs >> MODE_YPERIOD[this.mode];\n let addrofs = y & 7;\n let chbase = this.regs[CHBASE];\n // modes 6 & 7\n if ((mode & 0xe) == 6) { // or 7\n ch &= 0x3f;\n chbase &= 0xfe;\n } else {\n ch &= 0x7f;\n chbase &= 0xfc;\n }\n let addr = (ch << 3) + (chbase << 8);\n // modes 2 & 3\n if ((mode & 0xe) == 2) { // or 3\n let chactl = this.regs[CHACTL];\n let mode3lc = mode == 3 && (ch & 0x60) == 0x60;\n if (chactl & 4)\n this.pfbyte = this.read(addr + (addrofs ^ 7)); // mirror\n else\n this.pfbyte = this.read(addr + addrofs);\n if (mode3lc && y < 2) { this.pfbyte = 0; }\n if (!mode3lc && y > 7) { this.pfbyte = 0; }\n if (this.ch & 0x80) {\n if (chactl & 1)\n this.pfbyte = 0x0; // blank\n if (chactl & 2)\n this.pfbyte ^= 0xff; // invert\n }\n } else {\n this.pfbyte = this.read(addr + addrofs);\n }\n } else {\t// map mode\n this.pfbyte = this.nextScreen();\n }\n }\n\n shiftout() {\n if (this.output == 4) { // visible pixel?\n switch (this.mode) {\n case 2: case 3:\n case 15:\n {\n let v = (this.pfbyte >> 7) & 1;\n this.pfbyte <<= 1;\n return v ? 8 : 6;\n }\n case 6: case 7:\n {\n let v = (this.pfbyte >> 7) & 1;\n this.pfbyte <<= 1;\n return v ? (this.ch >> 6) + 4 : 0;\n }\n case 9: case 11: case 12:\n {\n let v = (this.pfbyte >> 7) & 1;\n this.pfbyte <<= 1;\n return v ? 4 : 0;\n }\n case 4: case 5:\n {\n let v = (this.pfbyte >> 6) & 3;\n this.pfbyte <<= 2;\n if (this.ch & 0x80)\n return [0, 4, 5, 7][v];\n else\n return [0, 4, 5, 6][v];\n }\n case 8: case 10:\n case 13: case 14:\n {\n let v = (this.pfbyte >> 6) & 3;\n this.pfbyte <<= 2;\n return [0, 4, 5, 6][v];\n }\n }\n }\n return this.output;\n }\n\n}\n", "/*\n * pokey.c - POKEY sound chip emulation\n *\n * Copyright (C) 1995-1998 David Firth\n * Copyright (C) 1998-2008 Atari800 development team (see DOC/CREDITS)\n *\n * This file is part of the Atari800 emulator project which emulates\n * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.\n *\n * Atari800 is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * Atari800 is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with Atari800; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n*/\n\nimport { dumpRAM } from \"../../common/emu\"\nimport { hex, lpad, safe_extend } from \"../../common/util\"\n\nconst AUDF1 = 0x00\nconst AUDC1 = 0x01\nconst AUDF2 = 0x02\nconst AUDC2 = 0x03\nconst AUDF3 = 0x04\nconst AUDC3 = 0x05\nconst AUDF4 = 0x06\nconst AUDC4 = 0x07\nconst AUDCTL = 0x08\nconst STIMER = 0x09\nconst SKRES = 0x0a\nconst POTGO = 0x0b\nconst SEROUT = 0x0d\nconst IRQEN = 0x0e\nconst SKCTL = 0x0f\n\nconst POT0 = 0x00\nconst POT1 = 0x01\nconst POT2 = 0x02\nconst POT3 = 0x03\nconst POT4 = 0x04\nconst POT5 = 0x05\nconst POT6 = 0x06\nconst POT7 = 0x07\nconst ALLPOT = 0x08\nconst KBCODE = 0x09\nconst RANDOM = 0x0a\nconst SERIN = 0x0d\nconst IRQST = 0x0e\nconst SKSTAT = 0x0f\n\n/* definitions for AUDCx (D201, D203, D205, D207) */\nconst NOTPOLY5 = 0x80 /* selects POLY5 or direct CLOCK */\nconst POLY4 = 0x40 /* selects POLY4 or POLY17 */\nconst PURETONE = 0x20 /* selects POLY4/17 or PURE tone */\nconst VOL_ONLY = 0x10 /* selects VOLUME OUTPUT ONLY */\nconst VOLUME_MASK = 0x0f /* volume mask */\n\n/* definitions for AUDCTL (D208) */\nconst POLY9 = 0x80 /* selects POLY9 or POLY17 */\nconst CH1_179 = 0x40 /* selects 1.78979 MHz for Ch 1 */\nconst CH3_179 = 0x20 /* selects 1.78979 MHz for Ch 3 */\nconst CH1_CH2 = 0x10 /* clocks channel 1 w/channel 2 */\nconst CH3_CH4 = 0x08 /* clocks channel 3 w/channel 4 */\nconst CH1_FILTER = 0x04 /* selects channel 1 high pass filter */\nconst CH2_FILTER = 0x02 /* selects channel 2 high pass filter */\nconst CLOCK_15 = 0x01 /* selects 15.6999kHz or 63.9210kHz */\n\n/* for accuracy, the 64kHz and 15kHz clocks are exact divisions of\n the 1.79MHz clock */\nconst DIV_64 = 28 /* divisor for 1.79MHz clock to 64 kHz */\nconst DIV_15 = 114 /* divisor for 1.79MHz clock to 15 kHz */\n\n/* the size (in entries) of the 4 polynomial tables */\nconst POLY4_SIZE = 0x000f\nconst POLY5_SIZE = 0x001f\nconst POLY9_SIZE = 0x01ff\nconst POLY17_SIZE = 0x0001ffff\n\nconst CHAN1 = 0\nconst CHAN2 = 1\nconst CHAN3 = 2\nconst CHAN4 = 3\n\nconst ANTIC_LINE_C = 114\n\n/* Some defines about the serial I/O timing. Currently fixed! */\nconst SIO_XMTDONE_INTERVAL = 15\nconst SIO_SERIN_INTERVAL = 8\nconst SIO_SEROUT_INTERVAL = 8\nconst SIO_ACK_INTERVAL = 36\n\nvar poly9: Uint8Array;\nvar poly17: Uint8Array;\n\nfunction initPolyTables() {\n poly9 = new Uint8Array(511);\n poly17 = new Uint8Array(16385);\n /* initialise poly9_lookup */\n let reg = 0x1ff;\n for (let i = 0; i < 511; i++) {\n reg = ((((reg >> 5) ^ reg) & 1) << 8) + (reg >> 1);\n poly9[i] = reg;\n }\n /* initialise poly17_lookup */\n reg = 0x1ffff;\n for (let i = 0; i < 16385; i++) {\n reg = ((((reg >> 5) ^ reg) & 0xff) << 9) + (reg >> 8);\n poly17[i] = (reg >> 1);\n }\n}\n\nexport class POKEY {\n regs = new Uint8Array(16);\n readregs = new Uint8Array(16);\n divnirq = new Uint32Array(4);\n divnmax = new Uint32Array(4);\n pot_inputs = new Uint8Array(8);\n basemult = 0;\n pot_scanline = 0;\n random_scanline_counter = 0;\n kbcode = 0;\n DELAYED_SERIN_IRQ = 0;\n DELAYED_SEROUT_IRQ = 0;\n DELAYED_XMTDONE_IRQ = 0;\n\n constructor(\n public irq: () => void,\n public antic_xpos: () => number,\n ) {\n this.init();\n }\n\n saveState() {\n return safe_extend(0, {}, this);\n }\n loadState(s) {\n safe_extend(0, this, s);\n }\n\n init() {\n /* Initialise Serial Port Interrupts */\n //DELAYED_SERIN_IRQ = 0;\n //DELAYED_SEROUT_IRQ = 0;\n //DELAYED_XMTDONE_IRQ = 0;\n this.readregs.fill(0xff);\n this.readregs[SKSTAT] = 0xef;\n //SERIN = 0x00;\t/* or 0xff ? */\n //IRQEN = 0x00;\n //SKCTL = 0x00;\n this.basemult = DIV_64;\n this.pot_inputs.fill(128);\n initPolyTables();\n }\n\n\n read(addr: number): number {\n let byte = this.readregs[addr];\n addr &= 0xf;\n switch (addr) {\n case 0: case 1: case 2: case 3:\n case 4: case 5: case 6: case 7:\n byte = this.pot_inputs[addr];\n return (byte < this.pot_scanline) ? byte : this.pot_scanline;\n case ALLPOT:\n for (let i = 0; i < 8; i++) {\n if (this.pot_inputs[i] <= this.pot_scanline)\n byte &= ~(1 << i);\t\t// reset bit if pot value known\n }\n break;\n case KBCODE:\n return this.kbcode;\n case SKSTAT:\n byte = SKSTAT + (this.CASSETTE_IOLineStatus() << 4);\n break;\n case RANDOM:\n if ((this.regs[SKCTL] & 0x03) != 0) {\n let i = this.random_scanline_counter + this.antic_xpos();\n if (this.regs[AUDCTL] & POLY9)\n byte = poly9[i % POLY9_SIZE];\n else {\n i %= POLY17_SIZE;\n let ptr = i >> 3;\n i &= 7;\n byte = (poly17[ptr] >> i) + (poly17[ptr + 1] << (8 - i));\n }\n }\n break;\n }\n return byte & 0xff;\n }\n\n write(addr: number, byte: number): void {\n addr &= 0x0f;\n this.regs[addr] = byte;\n switch (addr) {\n case AUDCTL:\n /* determine the base multiplier for the 'div by n' calculations */\n if (byte & CLOCK_15)\n this.basemult = DIV_15;\n else\n this.basemult = DIV_64;\n this.update_counter((1 << CHAN1) | (1 << CHAN2) | (1 << CHAN3) | (1 << CHAN4));\n break;\n case AUDF1:\n this.update_counter((this.regs[AUDCTL] & CH1_CH2) ? ((1 << CHAN2) | (1 << CHAN1)) : (1 << CHAN1));\n break;\n case AUDF2:\n this.update_counter(1 << CHAN2);\n break;\n case AUDF3:\n this.update_counter((this.regs[AUDCTL] & CH3_CH4) ? ((1 << CHAN4) | (1 << CHAN3)) : (1 << CHAN3));\n break;\n case AUDF4:\n this.update_counter(1 << CHAN4);\n break;\n case IRQEN:\n this.readregs[IRQST] |= ~byte & 0xf7;\t/* Reset disabled IRQs except XMTDONE */\n let mask = ~this.readregs[IRQST] & this.regs[IRQEN];\n if (mask) {\n this.generateIRQ(this.readregs[IRQST]);\n }\n break;\n case SKRES:\n this.readregs[SKSTAT] |= 0xe0;\n break;\n case POTGO:\n if (!(this.regs[SKCTL] & 4))\n this.pot_scanline = 0;\t/* slow pot mode */\n break;\n case SEROUT:\n if ((this.regs[SKCTL] & 0x70) == 0x20 && this.siocheck()) {\n this.SIO_PutByte(byte);\n }\n // check if cassette 2-tone mode has been enabled \n if ((this.regs[SKCTL] & 0x08) == 0x00) {\n // intelligent device\n this.DELAYED_SEROUT_IRQ = SIO_SEROUT_INTERVAL;\n this.readregs[IRQST] |= 0x08;\n this.DELAYED_XMTDONE_IRQ = SIO_XMTDONE_INTERVAL;\n }\n else {\n // cassette \n // some savers patch the cassette baud rate, so we evaluate it here \n // scanlines per second*10 bit*audiofrequency/(1.79 MHz/2) \n this.DELAYED_SEROUT_IRQ = 312 * 50 * 10 * (this.regs[AUDF3] + this.regs[AUDF4] * 0x100) / 895000;\n // safety check \n if (this.DELAYED_SEROUT_IRQ >= 3) {\n this.readregs[IRQST] |= 0x08;\n this.DELAYED_XMTDONE_IRQ = 2 * this.DELAYED_SEROUT_IRQ - 2;\n }\n else {\n this.DELAYED_SEROUT_IRQ = 0;\n this.DELAYED_XMTDONE_IRQ = 0;\n }\n };\n break;\n case STIMER:\n this.divnirq[CHAN1] = this.divnmax[CHAN1];\n this.divnirq[CHAN2] = this.divnmax[CHAN2];\n this.divnirq[CHAN3] = this.divnmax[CHAN3];\n this.divnirq[CHAN4] = this.divnmax[CHAN4];\n //POKEYSND_Update(STIMER, byte, 0, SOUND_GAIN);\n break;\n case SKCTL:\n //VOICEBOX_SKCTLPutByte(byte);\n //POKEYSND_Update(SKCTL, byte, 0, SOUND_GAIN);\n if (byte & 4)\n this.pot_scanline = 228;\t/* fast pot mode - return results immediately */\n if ((byte & 0x03) == 0) {\n /* POKEY reset. */\n /* Stop serial IO. */\n this.DELAYED_SERIN_IRQ = 0;\n this.DELAYED_SEROUT_IRQ = 0;\n this.DELAYED_XMTDONE_IRQ = 0;\n // TODO: CASSETTE_ResetPOKEY();\n /* TODO other registers should also be reset. */\n }\n break;\n }\n this.snd_update(addr);\n //POKEYSND_Update(AUDC1, byte, 0, SOUND_GAIN);\n }\n\n /*****************************************************************************/\n /* Module: Update_Counter() */\n /* Purpose: To process the latest control values stored in the AUDF, AUDC, */\n /* and AUDCTL registers. It pre-calculates as much information as */\n /* possible for better performance. This routine has been added */\n /* here again as I need the precise frequency for the pokey timers */\n /* again. The pokey emulation is therefore somewhat sub-optimal */\n /* since the actual pokey emulation should grab the frequency values */\n /* directly from here instead of calculating them again. */\n /* */\n /* Author: Ron Fries,Thomas Richter */\n /* Date: March 27, 1998 */\n /* */\n /* Inputs: chan_mask: Channel mask, one bit per channel. */\n /* The channels that need to be updated */\n /* */\n /* Outputs: Adjusts local globals - no return value */\n /* */\n /*****************************************************************************/\n\n update_counter(chan_mask: number): void {\n\n /************************************************************/\n /* As defined in the manual, the exact Div_n_cnt values are */\n /* different depending on the frequency and resolution: */\n /* 64 kHz or 15 kHz - AUDF + 1 */\n /* 1 MHz, 8-bit - AUDF + 4 */\n /* 1 MHz, 16-bit - AUDF[CHAN1]+256*AUDF[CHAN2] + 7 */\n /************************************************************/\n\n /* only reset the channels that have changed */\n\n if (chan_mask & (1 << CHAN1)) {\n /* process channel 1 frequency */\n if (this.regs[AUDCTL] & CH1_179)\n this.divnmax[CHAN1] = this.regs[AUDF1 + CHAN1] + 4;\n else\n this.divnmax[CHAN1] = (this.regs[AUDF1 + CHAN1] + 1) * this.basemult;\n if (this.divnmax[CHAN1] < ANTIC_LINE_C)\n this.divnmax[CHAN1] = ANTIC_LINE_C;\n }\n\n if (chan_mask & (1 << CHAN2)) {\n /* process channel 2 frequency */\n if (this.regs[AUDCTL] & CH1_CH2) {\n if (this.regs[AUDCTL] & CH1_179)\n this.divnmax[CHAN2] = this.regs[AUDF1 + CHAN2] * 256 + this.regs[AUDF1 + CHAN1] + 7;\n else\n this.divnmax[CHAN2] = (this.regs[AUDF1 + CHAN2] * 256 + this.regs[AUDF1 + CHAN1] + 1) * this.basemult;\n }\n else\n this.divnmax[CHAN2] = (this.regs[AUDF1 + CHAN2] + 1) * this.basemult;\n if (this.divnmax[CHAN2] < ANTIC_LINE_C)\n this.divnmax[CHAN2] = ANTIC_LINE_C;\n }\n\n if (chan_mask & (1 << CHAN4)) {\n /* process channel 4 frequency */\n if (this.regs[AUDCTL] & CH3_CH4) {\n if (this.regs[AUDCTL] & CH3_179)\n this.divnmax[CHAN4] = this.regs[AUDF1 + CHAN4] * 256 + this.regs[AUDF1 + CHAN3] + 7;\n else\n this.divnmax[CHAN4] = (this.regs[AUDF1 + CHAN4] * 256 + this.regs[AUDF1 + CHAN3] + 1) * this.basemult;\n }\n else\n this.divnmax[CHAN4] = (this.regs[AUDF1 + CHAN4] + 1) * this.basemult;\n if (this.divnmax[CHAN4] < ANTIC_LINE_C)\n this.divnmax[CHAN4] = ANTIC_LINE_C;\n }\n\n //console.log(chan_mask, this.divnmax);\n }\n\n snd_update(addr: number) {\n\n }\n\n advanceScanline() {\n /***************************************************************************\n ** Generate POKEY Timer IRQs if required **\n ** called on a per-scanline basis, not very precise, but good enough **\n ** for most applications **\n ***************************************************************************/\n\n\n /* on nonpatched i/o-operation, enable the cassette timing */\n /*\n if (!ESC_enable_sio_patch) {\n if (CASSETTE_AddScanLine())\n DELAYED_SERIN_IRQ = 1;\n }\n */\n\n if ((this.regs[SKCTL] & 0x03) == 0)\n /* Don't process timers when POKEY is in reset mode. */\n return;\n\n if (this.pot_scanline < 228)\n this.pot_scanline++;\n\n this.random_scanline_counter += ANTIC_LINE_C;\n this.random_scanline_counter %= (this.regs[AUDCTL] & POLY9) ? POLY9_SIZE : POLY17_SIZE;\n\n if (this.DELAYED_SERIN_IRQ > 0) {\n if (--this.DELAYED_SERIN_IRQ == 0) {\n // Load a byte to SERIN - even when the IRQ is disabled. \n this.readregs[SERIN] = this.SIO_GetByte();\n this.generateIRQ(0x20);\n }\n }\n\n if (this.DELAYED_SEROUT_IRQ > 0) {\n if (--this.DELAYED_SEROUT_IRQ == 0) {\n this.generateIRQ(0x10);\n }\n }\n\n if (this.DELAYED_XMTDONE_IRQ > 0)\n if (--this.DELAYED_XMTDONE_IRQ == 0) {\n this.generateIRQ(0x08);\n }\n\n this.advanceIRQTimer(CHAN1, 0x1);\n this.advanceIRQTimer(CHAN2, 0x2);\n this.advanceIRQTimer(CHAN4, 0x4);\n }\n\n advanceIRQTimer(chan: number, mask: number) {\n if ((this.divnirq[chan] -= ANTIC_LINE_C) < 0) {\n this.divnirq[chan] += this.divnmax[chan];\n this.generateIRQ(mask);\n //console.log('irq', chan, this.divnirq[chan], this.divnmax[chan])\n }\n }\n\n generateIRQ(mask: number) {\n if (this.regs[IRQEN] & mask) {\n this.irq();\n this.readregs[IRQST] &= ~mask;\n }\n }\n\n static stateToLongString(state): string {\n let s = ''\n s += \"Write Registers:\\n\";\n s += dumpRAM(state.regs, 0, 16);\n s += \"Read Registers:\\n\";\n s += dumpRAM(state.readregs, 0, 16);\n return s;\n }\n\n CASSETTE_IOLineStatus() {\n return 0;\n }\n\n siocheck() {\n return (((this.regs[AUDF1 + CHAN3] == 0x28 || this.regs[AUDF1 + CHAN3] == 0x10\n || this.regs[AUDF1 + CHAN3] == 0x08 || this.regs[AUDF1 + CHAN3] == 0x0a)\n && this.regs[AUDF1 + CHAN4] == 0x00) // intelligent peripherals speeds\n || (this.regs[SKCTL] & 0x78) == 0x28) // cassette save mode\n && (this.regs[AUDCTL] & 0x28) == 0x28;\n }\n SIO_PutByte(byte: number) {\n // TODO\n console.log(\"SIO put byte\", byte);\n }\n SIO_GetByte() {\n return 0; // TODO\n }\n\n}\n\n\n//const SOUND_GAIN 4\n/*\nvoid Frame(void)\n{\n random_scanline_counter %= (this.regs[AUDCTL] & POLY9) ? POLY9_SIZE : POLY17_SIZE;\n}\n*/\n\n\n\n", "import { newPOKEYAudio, TssChannelAdapter } from \"../common/audio\";\nimport { MOS6502 } from \"../common/cpu/MOS6502\";\nimport { AcceptsPaddleInput, BasicScanlineMachine } from \"../common/devices\";\nimport { KeyFlags, Keys, makeKeycodeMap, newAddressDecoder, newKeyboardHandler } from \"../common/emu\";\nimport { hex } from \"../common/util\";\nimport { ANTIC, MODE_LINES, MODE_SHIFT } from \"./chips/antic\";\nimport { CONSOL, GTIA, TRIG0 } from \"./chips/gtia\";\nimport { POKEY } from \"./chips/pokey\";\n\nconst ATARI8_KEYMATRIX_INTL_NOSHIFT = [\n Keys.VK_L, Keys.VK_J, Keys.VK_SEMICOLON, Keys.VK_F4, Keys.VK_F5, Keys.VK_K, Keys.VK_BACK_SLASH, Keys.VK_TILDE,\n Keys.VK_O, null, Keys.VK_P, Keys.VK_U, Keys.VK_ENTER, Keys.VK_I, Keys.VK_MINUS2, Keys.VK_EQUALS2,\n Keys.VK_V, Keys.VK_F7, Keys.VK_C, Keys.VK_F6, Keys.VK_F4, Keys.VK_B, Keys.VK_X, Keys.VK_Z,\n Keys.VK_4, null, Keys.VK_3, Keys.VK_6, Keys.VK_ESCAPE, Keys.VK_5, Keys.VK_2, Keys.VK_1,\n Keys.VK_COMMA, Keys.VK_SPACE, Keys.VK_PERIOD, Keys.VK_N, null, Keys.VK_M, Keys.VK_SLASH, null/*invert*/,\n Keys.VK_R, null, Keys.VK_E, Keys.VK_Y, Keys.VK_TAB, Keys.VK_T, Keys.VK_W, Keys.VK_Q,\n Keys.VK_9, null, Keys.VK_0, Keys.VK_7, Keys.VK_BACK_SPACE, Keys.VK_8, null, null,\n Keys.VK_F, Keys.VK_H, Keys.VK_D, null, Keys.VK_CAPS_LOCK, Keys.VK_G, Keys.VK_S, Keys.VK_A,\n];\n\n//TODO\nvar ATARI8_KEYCODE_MAP = makeKeycodeMap([\n [Keys.UP, 0, 0x1],\n [Keys.DOWN, 0, 0x2],\n [Keys.LEFT, 0, 0x4],\n [Keys.RIGHT, 0, 0x8],\n [{ c: 16, n: \"Shift\", plyr: 0, button: 0 }, 2, 0x1],\n /*\n [Keys.P2_UP, 0, 0x10],\n [Keys.P2_DOWN, 0, 0x20],\n [Keys.P2_LEFT, 0, 0x40],\n [Keys.P2_RIGHT, 0, 0x80],\n [Keys.P2_A, 3, 0x1],\n */\n [Keys.VK_F1, 3, 0x1], // START\n [Keys.VK_F2, 3, 0x2], // SELECT\n [Keys.VK_F3, 3, 0x4], // OPTION\n]);\n\n\nexport class Atari800 extends BasicScanlineMachine implements AcceptsPaddleInput {\n\n // http://www.ataripreservation.org/websites/freddy.offenga/megazine/ISSUE5-PALNTSC.html\n cpuFrequency = 1789773;\n numTotalScanlines = 262;\n cpuCyclesPerLine = 114;\n canvasWidth = 336;\n numVisibleScanlines = 224;\n aspectRatio = this.canvasWidth / this.numVisibleScanlines * 0.857;\n firstVisibleScanline = 16;\n firstVisibleClock = (44 - 6) * 2; // ... to 215 * 2\n defaultROMSize = 0x8000;\n overscan = true;\n audioOversample = 2;\n sampleRate = this.numTotalScanlines * 60 * this.audioOversample;\n run_address = -1;\n\n cpu: MOS6502;\n ram: Uint8Array;\n bios: Uint8Array;\n bus;\n audio_pokey;\n audioadapter;\n antic: ANTIC;\n gtia: GTIA;\n irq_pokey: POKEY;\n inputs = new Uint8Array(4);\n linergb = new Uint32Array(this.canvasWidth);\n lastdmabyte = 0;\n keycode = 0;\n cart_80 = false;\n cart_a0 = false;\n xexdata = null;\n keyboard_active = true;\n d500 = new Uint8Array(0x100);\n // TODO: save/load vars\n\n constructor() {\n super();\n this.cpu = new MOS6502();\n this.ram = new Uint8Array(0x10000);\n this.bios = new Uint8Array(0x2800);\n this.bus = this.newBus();\n this.connectCPUMemoryBus(this.bus);\n // create support chips\n this.antic = new ANTIC(this.readDMA.bind(this), this.antic_nmi.bind(this));\n this.gtia = new GTIA();\n this.irq_pokey = new POKEY(this.pokey_irq.bind(this), () => this.antic.h);\n this.audio_pokey = newPOKEYAudio(1);\n this.audioadapter = new TssChannelAdapter(this.audio_pokey.pokey1, this.audioOversample, this.sampleRate);\n this.handler = newKeyboardHandler(\n this.inputs, ATARI8_KEYCODE_MAP, this.getKeyboardFunction(), true);\n }\n newBus() {\n return {\n read: newAddressDecoder([\n [0x0000, 0x7fff, 0xffff, (a) => { return this.ram[a]; }],\n [0x8000, 0x9fff, 0xffff, (a) => { return this.cart_80 ? this.rom[a - 0x8000] : this.ram[a]; }],\n [0xa000, 0xbfff, 0xffff, (a) => { return this.cart_a0 ? this.rom[a - 0x8000] : this.ram[a]; }],\n [0xd000, 0xd0ff, 0x1f, (a) => { return this.gtia.readReg(a); }],\n [0xd200, 0xd2ff, 0xf, (a) => { return this.readPokey(a); }],\n [0xd300, 0xd3ff, 0xf, (a) => { return this.readPIA(a); }],\n [0xd400, 0xd4ff, 0xf, (a) => { return this.antic.readReg(a); }],\n [0xd500, 0xd5ff, 0xff, (a) => { return this.d500[a]; }],\n [0xd800, 0xffff, 0xffff, (a) => { return this.bios[a - 0xd800]; }],\n ]),\n write: newAddressDecoder([\n [0x0000, 0xbffa, 0xffff, (a, v) => { this.ram[a] = v; }],\n [0xbffb, 0xbfff, 0xffff, (a, v) => { this.ram[a] = v; this.initCartA(); }],\n [0xd000, 0xd0ff, 0x1f, (a, v) => { this.gtia.setReg(a, v); }],\n [0xd200, 0xd2ff, 0xf, (a, v) => { this.writePokey(a, v); }],\n [0xd400, 0xd4ff, 0xf, (a, v) => { this.antic.setReg(a, v); }],\n [0xd500, 0xd5ff, 0xff, (a, v) => { this.writeMapper(a, v); }],\n ]),\n };\n }\n\n loadBIOS(bios: Uint8Array) {\n this.bios.set(bios);\n }\n\n reset() {\n super.reset();\n this.antic.reset();\n this.gtia.reset();\n this.keycode = 0;\n //if (this.xexdata) this.cart_a0 = true; // TODO\n }\n\n read(a) {\n // TODO: lastdmabyte?\n return this.bus.read(a);\n }\n // used by ANTIC\n readDMA(a) {\n let v = this.bus.read(a);\n this.probe.logDMARead(a, v);\n this.lastdmabyte = v;\n return v;\n }\n readConst(a) {\n return a < 0xd000 || a >= 0xd500 ? this.bus.read(a) : 0xff;\n }\n write(a, v) {\n this.bus.write(a, v);\n }\n readPokey(a: number) {\n switch (a & 0xf) {\n case 9: // KBCODE\n return this.keycode & 0xff;\n case 15: // SKSTAT\n return ((~this.keycode >> 6) & 0x4) | ((~this.keycode >> 3) & 0x8) | 0x12;\n default:\n return this.irq_pokey.read(a);\n }\n }\n readPIA(a: number) {\n if (a == 0 || a == 1) { return ~this.inputs[a]; }\n }\n writePokey(a, v) {\n this.audio_pokey.pokey1.setRegister(a, v);\n this.irq_pokey.write(a, v);\n }\n\n startScanline() {\n // TODO: if (this.antic.h != 0) throw new Error(this.antic.h+\"\");\n //if (this.cpu.isHalted()) throw new EmuHalt(\"CPU HALTED\");\n // set GTIA switch inputs\n this.gtia.sync();\n // TODO: trigger latching mode\n for (let i = 0; i < 4; i++)\n this.gtia.readregs[TRIG0 + i] = (~this.inputs[2] >> i) & 1;\n // console switches\n this.gtia.readregs[CONSOL] = ~this.inputs[3] & 0x7;\n // advance POKEY audio\n this.audio && this.audioadapter.generate(this.audio);\n // advance POKEY IRQ timers\n this.irq_pokey.advanceScanline();\n }\n\n drawScanline() {\n // TODO\n let y = this.antic.v - this.firstVisibleScanline;\n if (y >= 0 && y < this.numVisibleScanlines) {\n this.pixels.set(this.linergb, y * this.canvasWidth);\n }\n }\n\n advanceCPU(): number {\n // update ANTIC\n if (this.antic.clockPulse()) {\n // ANTIC DMA cycle, update GTIA\n if (this.antic.h < 8)\n this.gtia.updateGfx(this.antic.h - 1, this.antic.v, this.lastdmabyte); // HALT pin\n if (this.antic.isWSYNC())\n this.probe.logWait(0);\n this.probe.logClocks(1);\n } else {\n super.advanceCPU();\n }\n // update GTIA\n // get X coordinate within scanline\n let xofs = this.antic.h * 4 - this.firstVisibleClock;\n // GTIA tick functions\n let gtiatick1 = () => {\n this.gtia.clockPulse1();\n this.linergb[xofs++] = this.gtia.rgb;\n }\n let gtiatick2 = () => {\n this.gtia.clockPulse2();\n this.linergb[xofs++] = this.gtia.rgb;\n }\n // tick 4 GTIA clocks for each CPU/ANTIC cycle\n this.gtia.clockPulse4();\n // correct for HSCROL -- bias antic +2, bias gtia -1\n if ((this.antic.dliop & 0x10) && (this.antic.regs[4] & 1)) {\n xofs += 2;\n this.gtia.setBias(-1);\n } else {\n this.gtia.setBias(0);\n }\n let bp = MODE_SHIFT[this.antic.mode];\n let odd = this.antic.h & 1;\n if (bp < 8 || odd) { this.gtia.an = this.antic.shiftout(); }\n gtiatick1();\n if (bp == 1) { this.gtia.an = this.antic.shiftout(); }\n gtiatick2();\n if (bp <= 2) { this.gtia.an = this.antic.shiftout(); }\n gtiatick1();\n if (bp == 1) { this.gtia.an = this.antic.shiftout(); }\n gtiatick2();\n return 1;\n }\n\n loadState(state: any) {\n this.loadControlsState(state);\n this.cpu.loadState(state.c);\n this.ram.set(state.ram);\n this.antic.loadState(state.antic);\n this.gtia.loadState(state.gtia);\n this.irq_pokey.loadState(state.pokey);\n this.lastdmabyte = state.lastdmabyte;\n this.cart_80 = state.cart_80;\n this.cart_a0 = state.cart_a0;\n }\n saveState() {\n return {\n c: this.cpu.saveState(),\n ram: this.ram.slice(0),\n antic: this.antic.saveState(),\n gtia: this.gtia.saveState(),\n pokey: this.irq_pokey.saveState(),\n inputs: this.inputs.slice(0),\n lastdmabyte: this.lastdmabyte,\n keycode: this.keycode,\n cart_80: this.cart_80,\n cart_a0: this.cart_a0,\n };\n }\n loadControlsState(state) {\n this.inputs.set(state.inputs);\n this.keycode = state.keycode;\n }\n saveControlsState() {\n return {\n inputs: this.inputs.slice(0),\n keycode: this.keycode,\n };\n }\n getRasterY() {\n return this.antic.v;\n }\n getRasterX() {\n return this.antic.h;\n }\n getRasterCanvasPosition() {\n return {\n x: this.antic.h * 4 - this.firstVisibleClock,\n y: this.antic.v - this.firstVisibleScanline,\n }\n }\n getDebugCategories() {\n return ['CPU', 'Stack', 'ANTIC', 'GTIA', 'POKEY'];\n }\n getDebugInfo(category, state) {\n switch (category) {\n case 'ANTIC': return ANTIC.stateToLongString(state.antic);\n case 'GTIA': return GTIA.stateToLongString(state.gtia);\n case 'POKEY': return POKEY.stateToLongString(state.pokey);\n }\n }\n getKeyboardFunction() {\n return (o, key, code, flags) => {\n if (!this.keyboard_active) return false;\n if (flags & (KeyFlags.KeyDown | KeyFlags.KeyUp)) {\n //console.log(o, key, code, flags, hex(this.keycode));\n var keymap = ATARI8_KEYMATRIX_INTL_NOSHIFT;\n if (key == Keys.VK_F9.c) {\n this.irq_pokey.generateIRQ(0x80); // break IRQ\n return true;\n }\n for (var i = 0; i < keymap.length; i++) {\n if (keymap[i] && keymap[i].c == key) {\n this.keycode = i;\n if (flags & KeyFlags.Shift) { this.keycode |= 0x40; }\n if (flags & KeyFlags.Ctrl) { this.keycode |= 0x80; }\n if (flags & KeyFlags.KeyDown) {\n this.keycode |= 0x100;\n this.irq_pokey.generateIRQ(0x40); // key pressed IRQ\n return true;\n }\n }\n }\n };\n }\n }\n pokey_irq() {\n this.cpu.IRQ();\n this.probe.logInterrupt(2);\n }\n antic_nmi() {\n this.cpu.NMI();\n this.probe.logInterrupt(1);\n }\n\n loadROM(rom: Uint8Array, title: string) {\n if ((rom[0] == 0xff && rom[1] == 0xff) && !title.endsWith('.rom')) {\n // XEX file, chill out and wait for BIOS hook\n this.xexdata = rom;\n } else {\n this.loadCartridge(rom);\n }\n }\n\n loadCartridge(rom: Uint8Array) {\n // TODO: https://github.com/dmlloyd/atari800/blob/master/DOC/cart.txt\n // strip off header\n if (rom[0] == 0x43 && rom[1] == 0x41 && rom[2] == 0x52 && rom[3] == 0x54) {\n rom = rom.slice(16);\n }\n if (rom.length != 0x1000 && rom.length != 0x2000 && rom.length != 0x4000 && rom.length != 0x8000)\n throw new Error(\"Sorry, this platform can only load 4/8/16/32 KB cartridges at the moment.\");\n // TODO: support other than 8 KB carts\n // support 4/8/16/32 KB carts\n let rom2 = new Uint8Array(0x8000);\n for (let i = 0; i <= rom2.length - rom.length; i += rom.length) {\n rom2.set(rom, i);\n }\n this.run_address = rom2[0x7ffe] + rom2[0x7fff]*256;\n this.cart_a0 = true; // TODO\n this.cart_80 = rom.length == 0x4000;\n super.loadROM(rom2);\n }\n\n writeMapper(addr: number, value: number) {\n // TODO\n if (addr == 0xff) {\n if (value == 0x80) this.cart_80 = false;\n if (value == 0xa0) this.cart_a0 = false;\n }\n }\n\n loadXEX(rom: Uint8Array) {\n let ofs = 2;\n let stub = this.d500;\n let stubofs = 0; // stub routine \n var runaddr = -1;\n // load segments into RAM\n while (ofs < rom.length) {\n let start = rom[ofs + 0] + rom[ofs + 1] * 256;\n let end = rom[ofs + 2] + rom[ofs + 3] * 256;\n console.log('XEX', hex(ofs), hex(start), hex(end));\n ofs += 4;\n for (let i = start; i <= end; i++) {\n this.ram[i] = rom[ofs++];\n }\n if (start == 0x2e0 && end == 0x2e1) {\n runaddr = this.ram[0x2e0] + this.ram[0x2e1] * 256;\n console.log('XEX run', hex(runaddr));\n }\n if (start == 0x2e2 && end == 0x2e3) {\n var initaddr = this.ram[0x2e2] + this.ram[0x2e3] * 256;\n console.log('XEX init', hex(initaddr));\n stub[stubofs++] = 0x20;\n stub[stubofs++] = initaddr & 0xff;\n stub[stubofs++] = initaddr >> 8;\n }\n if (ofs > rom.length) throw new Error(\"Bad .XEX file format\");\n }\n if (runaddr >= 0) {\n // build stub routine at 0xd500\n stub[stubofs++] = 0xa9; // lda #$a0\n stub[stubofs++] = 0xa0;\n stub[stubofs++] = 0x8d; // sta $d5ff (disable cart)\n stub[stubofs++] = 0xff;\n stub[stubofs++] = 0xd5;\n stub[stubofs++] = 0x4c; // jmp runaddr\n stub[stubofs++] = runaddr & 0xff;\n stub[stubofs++] = runaddr >> 8;\n // set DOSVEC to 0xd500\n this.ram[0xa] = 0x00;\n this.ram[0xb] = 0xd5;\n this.run_address = 0xd500;\n }\n }\n\n initCartA() {\n if (this.cpu.getPC() == 0xf17f && this.xexdata) {\n this.loadXEX(this.xexdata);\n }\n }\n\n setPaddleInput(controller: number, value: number): void {\n this.irq_pokey.pot_inputs[controller] = 255 - value;\n }\n\n getDebugDisplayList() {\n let pc = this.antic.getDlistAddr();\n const nextInsn = () => {\n let b = this.read(pc);\n pc = ((pc + 1) & 0x3ff) | (pc & ~0x3ff);\n return b;\n }\n let dlist = [];\n let y = 0;\n for (let i=0; i<256 && y<240; i++) {\n let pc0 = pc;\n let op = nextInsn(); // get mode\n let mode = op & 0xf;\n let debugmsg = \"\" // \"op=\" + hex(op);\n let jmp = false;\n let lines;\n if (mode == 0) {\n lines = (((op >> 4) & 7) + 1);\n debugmsg += \" blank=\" + lines;\n } else {\n lines = MODE_LINES[mode];\n debugmsg += \" mode=\" + hex(mode);\n debugmsg += \" lines=\" + lines;\n jmp = (op & ~0x40) == 0x01; // JMP insn?\n let lms = (op & 0x40) != 0 && (op & 0xf) != 0; // LMS insn?\n if (op & 0x10) { debugmsg += \" HSCROL\"; }\n if (op & 0x20) { debugmsg += \" VSCROL\"; }\n if (op & 0x80) { debugmsg += \" DLI\"; }\n if (jmp && (op & 0x40)) { debugmsg += \" JVB\"; }\n else if (jmp) debugmsg += \" JMP\";\n else if (lms) debugmsg += \" LMS\";\n if (jmp || lms) {\n let dlarg_lo = nextInsn();\n let dlarg_hi = nextInsn();\n debugmsg += \" $\" + hex(dlarg_hi) + \"\" + hex(dlarg_lo);\n }\n }\n dlist.push(\"$\"+hex(pc0) + \" y=\" + y + \" \" + debugmsg);\n if (jmp) break;\n y += lines;\n }\n return dlist;\n }\n\n}\n\nexport class Atari5200 extends Atari800 {\n newBus() {\n return {\n read: newAddressDecoder([\n [0x0000, 0x3fff, 0xffff, (a) => { return this.ram[a]; }],\n [0x4000, 0xbfff, 0xffff, (a) => { return this.rom ? this.rom[a - 0x4000] : 0; }],\n [0xc000, 0xcfff, 0x1f, (a) => { return this.gtia.readReg(a); }],\n [0xd400, 0xd4ff, 0xf, (a) => { return this.antic.readReg(a); }],\n [0xe800, 0xefff, 0xf, (a) => { return this.readPokey(a); }],\n [0xf800, 0xffff, 0x7ff, (a) => { return this.bios[a]; }],\n ]),\n write: newAddressDecoder([\n [0x0000, 0x3fff, 0xffff, (a, v) => { this.ram[a] = v; }],\n [0xc000, 0xcfff, 0x1f, (a, v) => { this.gtia.setReg(a, v); }],\n [0xd400, 0xd4ff, 0xf, (a, v) => { this.antic.setReg(a, v); }],\n [0xe800, 0xefff, 0xf, (a, v) => { this.writePokey(a, v); }],\n ]),\n };\n }\n}\n\n", "\nimport { Platform, getOpcodeMetadata_6502, getToolForFilename_6502, Base6502MachinePlatform, Preset } from \"../common/baseplatform\";\nimport { PLATFORMS } from \"../common/emu\";\nimport { BaseMAME6502Platform } from \"../common/mameplatform\";\nimport { Atari5200, Atari800 } from \"../machine/atari8\";\n\nvar Atari8_PRESETS : Preset[] = [\n {id:'hello.dasm', name:'Hello World', category:'Assembly'},\n {id:'hellopm.dasm', name:'Hello Sprites'},\n {id:'helloconio.c', name:'Text Mode', category:'C'},\n {id:'siegegame.c', name:'Siege Game'},\n {id:'hellodlist.c', name:'Display List'},\n {id:'libdemo.c', name:'Common Library'},\n];\n\nvar Atari800_PRESETS = Atari8_PRESETS.concat([\n //{id:'testmusic.c', name:'POKEY Music'},\n {id:'scrolldemo.c', name:'Scrolling'},\n {id:'hello.cpp', name:'Hello Oscar64', category:'C++ (Oscar64)'},\n {id:'sieve.bas', name:'Benchmark', category:'FastBasic'},\n {id:'pmtest.bas', name:'Sprites Test'},\n {id:'dli.bas', name:'DLI Test'},\n {id:'joyas.bas', name:'Match-3 Game'},\n]);\n\nconst Atari800_MemoryMap = { main:[\n {name:'RAM',start:0x0,size:0xc000,type:'ram'},\n {name:'Left Cartridge ROM',start:0xa000,size:0x2000,type:'rom'},\n {name:'GTIA',start:0xd000,size:0x20,type:'io'},\n {name:'POKEY',start:0xd200,size:0x10,type:'io'},\n {name:'PIA',start:0xd300,size:0x04,type:'io'},\n {name:'ANTIC',start:0xd400,size:0x10,type:'io'},\n {name:'Cartridge Control Line',start:0xd600,size:0x100,type:'io'},\n {name:'ROM',start:0xd800,size:0x800,type:'rom'},\n {name:'Character Set',start:0xe000,size:0x400,type:'rom'},\n {name:'ROM',start:0xe400,size:0x1c00,type:'rom'},\n] }\n\nconst Atari5200_MemoryMap = { main:[\n {name:'RAM',start:0x0,size:0x4000,type:'ram'},\n {name:'Cartridge ROM',start:0x4000,size:0x8000,type:'rom'},\n {name:'GTIA',start:0xc000,size:0x20,type:'io'},\n {name:'ANTIC',start:0xd400,size:0x10,type:'io'},\n {name:'POKEY',start:0xe800,size:0x10,type:'io'},\n {name:'ATARI Character Set',start:0xf800,size:0x400,type:'rom'},\n {name:'ROM',start:0xfc00,size:0x400,type:'rom'},\n] }\n\nfunction getToolForFilename_Atari8(fn:string) {\n if (fn.endsWith(\".bas\") || fn.endsWith(\".fb\") || fn.endsWith(\".fbi\")) return \"fastbasic\";\n else return getToolForFilename_6502(fn);\n}\n\nclass Atari800Platform extends Base6502MachinePlatform {\n newMachine() { return new Atari800(); }\n getPresets() { return Atari800_PRESETS; }\n getDefaultExtensions() { return [...super.getDefaultExtensions(), \".bas\"]; }\n getToolForFilename = getToolForFilename_Atari8;\n readAddress(a) { return this.machine.readConst(a); }\n getMemoryMap() { return Atari800_MemoryMap; }\n showHelp = atari8_showHelp;\n getROMExtension = atari8_getROMExtension;\n \n async start() {\n let bios = await this.loadKernel();\n await super.start();\n this.machine.loadBIOS(bios);\n }\n biosPath = 'res/altirra/kernel.rom';\n async loadKernel() {\n var biosResponse = await fetch(this.biosPath);\n if (biosResponse.status == 200 || (biosResponse as any as Blob).size) {\n var biosBinary = await biosResponse.arrayBuffer();\n return new Uint8Array(biosBinary);\n } else throw new Error('could not load BIOS file');\n }\n getDebugTree() {\n let tree = super.getDebugTree();\n tree['display_list'] = this.machine.getDebugDisplayList();\n return tree;\n }\n}\n\nclass Atari5200Platform extends Atari800Platform {\n getPresets() { return Atari8_PRESETS; }\n newMachine() { return new Atari5200(); }\n getMemoryMap() { return Atari5200_MemoryMap; }\n biosPath = 'res/altirra/superkernel.rom';\n}\n\n\n/// MAME support\n\nabstract class Atari8MAMEPlatform extends BaseMAME6502Platform {\n getPresets() { return Atari8_PRESETS; }\n getToolForFilename = getToolForFilename_Atari8;\n getOpcodeMetadata = getOpcodeMetadata_6502;\n getDefaultExtensions() { return [\".c\", \".bas\", \".cpp\", \".acme\", \".xa\", \".ca65\", \".dasm\", \".ecs\", \".wiz\"]; }\n showHelp = atari8_showHelp;\n}\n\nclass Atari800MAMEPlatform extends Atari8MAMEPlatform implements Platform {\n // MAME loads cartridge ROMs; oscar64's atari target emits an XEX image, so it\n // only works on the native platform.\n getPresets() { return Atari800_PRESETS.filter(p => !p.id.endsWith('.cpp')); }\n loadROM(title, data) {\n if (!this.started) {\n this.startModule(this.mainElement, {\n jsfile:'mame8bitws.js',\n biosfile:'a800xl.zip',\n cfgfile:'a800xl.cfg',\n driver:'a800xl',\n width:336*2,\n height:225*2,\n romfn:'/emulator/cart.rom',\n romdata:new Uint8Array(data),\n romsize:0x2000,\n preInit:function(_self) {\n },\n });\n } else {\n this.loadROMFile(data);\n this.loadRegion(\":cartleft:cart:rom\", data);\n }\n }\n start() {\n }\n getMemoryMap = function() { return Atari800_MemoryMap };\n}\n\nclass Atari5200MAMEPlatform extends Atari8MAMEPlatform implements Platform {\n loadROM(title, data) {\n if (!this.started) {\n this.startModule(this.mainElement, {\n jsfile:'mame8bitws.js',\n biosfile:'a5200/5200.rom',\n cfgfile:'a5200.cfg',\n driver:'a5200',\n width:336*2,\n height:225*2,\n romfn:'/emulator/cart.rom',\n romdata:new Uint8Array(data),\n romsize:0x8000,\n preInit:function(_self) {\n },\n });\n } else {\n this.loadROMFile(data);\n this.loadRegion(\":cartleft:cart:rom\", data);\n }\n }\n start() {\n }\n getMemoryMap() { return Atari5200_MemoryMap; }\n}\n\nfunction atari8_getROMExtension(rom: Uint8Array) {\n if (rom == null) return \".bin\";\n if (rom[0] == 0xff && rom[1] == 0xff) return \".xex\";\n else return \".rom\";\n}\n\nfunction atari8_showHelp() {\n return \"https://8bitworkshop.com/docs/platforms/atari8/\";\n}\n\n///\n\nPLATFORMS['atari8-800.xlmame'] = Atari800MAMEPlatform\nPLATFORMS['atari8-5200.mame'] = Atari5200MAMEPlatform\nPLATFORMS['atari8-800'] = Atari800Platform\nPLATFORMS['atari8-5200'] = Atari5200Platform\n"], - "mappings": "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", - "names": ["PF_LEFT", "PF_RIGHT", "DMACTL", "CHACTL", "DLISTL", "DLISTH", "HSCROL", "VSCROL", "PMBASE", "CHBASE", "WSYNC", "VCOUNT", "PENH", "PENV", "NMIEN", "NMIRES", "NMIST", "ANTIC_LEFT", "ANTIC_RIGHT", "LAST_DMA_H", "MODE_LINES", "MODE_PERIOD", "MODE_YPERIOD", "MODE_SHIFT", "ANTIC", "readfn", "nmifn", "NMIEN", "NMIST", "PENH", "PENV", "DMACTL", "safe_extend", "s", "state", "lpad", "hex", "dumpRAM", "a", "v", "WSYNC", "NMIRES", "VCOUNT", "DLISTL", "DLISTH", "effwidth", "hscroll", "HSCROL", "PF_LEFT", "PF_RIGHT", "vscrol", "VSCROL", "mask", "pc", "b", "did_dma", "op", "candma", "section", "oneline", "pmaddr", "PMBASE", "mode", "ch", "y", "addrofs", "chbase", "CHBASE", "addr", "chactl", "CHACTL", "mode3lc", "AUDF1", "AUDF2", "AUDF3", "AUDF4", "AUDCTL", "STIMER", "SKRES", "POTGO", "SEROUT", "IRQEN", "SKCTL", "ALLPOT", "KBCODE", "RANDOM", "SERIN", "IRQST", "SKSTAT", "POLY9", "CH1_179", "CH3_179", "CH1_CH2", "CH3_CH4", "CLOCK_15", "DIV_64", "DIV_15", "POLY9_SIZE", "POLY17_SIZE", "CHAN1", "CHAN2", "CHAN3", "CHAN4", "ANTIC_LINE_C", "SIO_XMTDONE_INTERVAL", "SIO_SEROUT_INTERVAL", "poly9", "poly17", "initPolyTables", "reg", "i", "POKEY", "irq", "antic_xpos", "safe_extend", "s", "SKSTAT", "DIV_64", "addr", "byte", "ALLPOT", "KBCODE", "RANDOM", "SKCTL", "AUDCTL", "POLY9", "POLY9_SIZE", "POLY17_SIZE", "ptr", "CLOCK_15", "DIV_15", "CHAN1", "CHAN2", "CHAN3", "CHAN4", "AUDF1", "CH1_CH2", "AUDF2", "AUDF3", "CH3_CH4", "AUDF4", "IRQEN", "IRQST", "SKRES", "POTGO", "SEROUT", "SIO_SEROUT_INTERVAL", "SIO_XMTDONE_INTERVAL", "STIMER", "chan_mask", "CH1_179", "ANTIC_LINE_C", "CH3_179", "SERIN", "chan", "mask", "state", "dumpRAM", "ATARI8_KEYMATRIX_INTL_NOSHIFT", "Keys", "ATARI8_KEYCODE_MAP", "makeKeycodeMap", "Atari800", "BasicScanlineMachine", "MOS6502", "ANTIC", "GTIA", "POKEY", "newPOKEYAudio", "TssChannelAdapter", "newKeyboardHandler", "newAddressDecoder", "a", "v", "bios", "i", "TRIG0", "CONSOL", "y", "xofs", "gtiatick1", "gtiatick2", "bp", "MODE_SHIFT", "odd", "state", "category", "o", "key", "code", "flags", "keymap", "rom", "title", "rom2", "addr", "value", "ofs", "stub", "stubofs", "runaddr", "start", "end", "hex", "initaddr", "controller", "pc", "nextInsn", "b", "dlist", "pc0", "op", "mode", "debugmsg", "jmp", "lines", "MODE_LINES", "lms", "dlarg_lo", "dlarg_hi", "Atari5200", "Atari8_PRESETS", "Atari800_PRESETS", "Atari800_MemoryMap", "Atari5200_MemoryMap", "getToolForFilename_Atari8", "fn", "getToolForFilename_6502", "Atari800Platform", "Base6502MachinePlatform", "atari8_showHelp", "atari8_getROMExtension", "Atari800", "a", "bios", "biosResponse", "biosBinary", "tree", "Atari5200Platform", "Atari5200", "Atari8MAMEPlatform", "BaseMAME6502Platform", "getOpcodeMetadata_6502", "Atari800MAMEPlatform", "p", "title", "data", "_self", "Atari5200MAMEPlatform", "rom", "PLATFORMS"] + "sources": ["../src/machine/chips/antic.ts", "../src/machine/chips/pokey.ts", "../src/machine/atari8disk.ts", "../src/machine/atari8.ts", "../src/platform/atari8.ts"], + "sourcesContent": ["import { dumpRAM } from \"../../common/emu\";\nimport { hex, lpad, safe_extend } from \"../../common/util\";\n\n// ANTIC\n// https://www.atarimax.com/jindroush.atari.org/atanttim.html\n// http://www.virtualdub.org/blog/pivot/entry.php?id=243\n// http://www.beipmu.com/Antic_Timings.txt\n// https://user.xmission.com/~trevin/atari/antic_regs.html\n// https://user.xmission.com/~trevin/atari/antic_insns.html\n// http://www.atarimuseum.com/videogames/consoles/5200/conv_to_5200.html\n// https://www.virtualdub.org/downloads/Altirra%20Hardware%20Reference%20Manual.pdf\n\nconst PF_LEFT = [0, 25, 17, 9];\nconst PF_RIGHT = [0, 25 + 64, 17 + 80, 9 + 96];\n\nconst DMACTL = 0;\nconst CHACTL = 1;\nconst DLISTL = 2;\nconst DLISTH = 3;\nconst HSCROL = 4;\nconst VSCROL = 5;\nconst PMBASE = 7;\nconst CHBASE = 9;\nconst WSYNC = 10;\nconst VCOUNT = 11;\nconst PENH = 12;\nconst PENV = 13;\nconst NMIEN = 14;\nconst NMIRES = 15;\nconst NMIST = 15;\n\nconst PFNONE = 0;\nconst PFNARROW = 1;\nconst PFNORMAL = 2;\nconst PFWIDE = 3;\n\nconst NMIST_CYCLE = 12;\nconst NMI_CYCLE = 24;\nconst WSYNC_CYCLE = 212;\n\nconst ANTIC_LEFT = 17 - 4; // gtia 34, 4 cycle delay\nconst ANTIC_RIGHT = 110 - 4; // gtia 221, 4 cycle delay\nconst LAST_DMA_H = 105; // last DMA cycle\n\nexport const MODE_LINES = [0, 0, 8, 10, 8, 16, 8, 16, 8, 4, 4, 2, 1, 2, 1, 1];\n// how many bits before DMA clock repeats?\nconst MODE_PERIOD = [0, 0, 2, 2, 2, 2, 4, 4, 8, 8, 4, 4, 4, 2, 2, 2];\nconst MODE_YPERIOD = [0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 2, 1, 0, 0, 0, 0];\n//const MODE_BPP = [0, 0, 1, 1, 2, 2, 1, 1, 2, 1, 2, 1, 1, 2, 2, 1];\n// how many color clocks / pixel * 2\nexport const MODE_SHIFT = [0, 0, 1, 1, 2, 2, 2, 2, 8, 4, 4, 2, 2, 2, 2, 1];\n\nexport class ANTIC {\n read: (address: number) => number;\t// bus read function\n nmi: () => void; // generate NMI\n\n regs = new Uint8Array(0x10);\t\t\t\t// registers\n\n // Initialized so they are always own properties and therefore always\n // round-trip through saveState()/loadState() -- safe_extend() copies only\n // the keys present in the source, so a field that is merely declared is\n // absent from a save state and keeps its old value on load, which leaks\n // state backwards across a rewind. -1 behaves exactly as undefined did in\n // every comparison below (h is never negative, and h >= 2 && h <= 1 is\n // never true), so this does not change emulation.\n left: number = -1;\n right: number = -1;\t\t\t\t// left/right clocks for mode\n\n dma_enabled: boolean = false;\n dliop: number = 0; // dli operation\n mode: number = 0;\t\t\t// current mode\n jmp = false; // TODO\n lms = false; // TODO\n dlarg_lo: number = 0;\n dlarg_hi: number = 0;\n period: number = 0;\t\t// current mode period bitmask\n scanaddr: number = 0; // Scan Address (via LMS)\n startaddr: number = 0;\t// Start of line Address\n pfbyte: number = 0;\t\t// playfield byte fetched\n ch: number = 0;\t\t\t\t// char read\n linesleft: number = 0; // # of lines left in mode\n yofs: number = 0;\t\t\t// yofs fine\n isfirstline: boolean = false;\n v: number = 0;\t\t\t\t\t// vertical scanline #\n h: number = 0;\t\t\t\t\t// horizontal color clock\n\n linebuf = new Uint8Array(48);\n dmaclock: number = 0;\n dmaidx: number = 0;\n output: number = 0;\n dramrefresh = false;\n in_vscroll = 0;\n\n constructor(readfn, nmifn) {\n this.read = readfn; // bus read function\n this.nmi = nmifn; // NMI function\n }\n reset() {\n this.regs.fill(0);\n this.regs[NMIEN] = 0x00;\n this.regs[NMIST] = 0x7f;\n this.regs[PENH] = 0x00;\n this.regs[PENV] = 0xff;\n this.setReg(DMACTL, 0x0);\n this.h = this.v = 0;\n this.startaddr = this.scanaddr = 0;\n this.dmaclock = 0;\n }\n saveState() {\n return safe_extend(0, {}, this);\n }\n loadState(s) {\n safe_extend(0, this, s);\n this.setReg(DMACTL, s.regs[DMACTL]);\n }\n static stateToLongString(state): string {\n let s = \"\";\n s += \"H: \" + lpad(state.h, 3) + \" V: \" + lpad(state.v, 3) + \"\\n\";\n s += \"DLIOp: \" + hex(state.dliop, 2) + \" Lines: \" + state.yofs + \"/\" + state.linesleft;\n s += \" DMA \" + (state.dma_enabled ? \"ON \" : \"off\")\n if (state.dma_enabled) s += \" idx \" + state.dmaidx + \" clk \" + hex(state.dmaclock) \n s += \"\\n\"\n s += \"Addr: \" + hex(state.scanaddr, 4) + \"\\n\";\n s += dumpRAM(state.regs, 0, 16).replace('$00', 'Regs');\n return s;\n }\n setReg(a: number, v: number) {\n switch (a) {\n case WSYNC:\n this.regs[WSYNC] = 0xff;\n return; // this is readonly (we reset it)\n case NMIRES:\n this.regs[NMIST] = 0x1f;\n return; // this is readonly, don't mess with it\n }\n this.regs[a] = v;\n }\n readReg(a: number) {\n switch (a) {\n case NMIST:\n return this.regs[a];\n case VCOUNT:\n return this.v >> 1;\n default:\n return 0xff;\n }\n }\n processDLIEntry() {\n if (this.mode == 0) { // N Blank Lines\n this.linesleft = ((this.dliop >> 4) & 7) + 1;\n this.dmaclock = 0;\n } else {\n this.linesleft = MODE_LINES[this.mode];\n this.period = MODE_PERIOD[this.mode];\n if (this.jmp) {\n this.regs[DLISTL] = this.dlarg_lo;\n this.regs[DLISTH] = this.dlarg_hi;\n this.mode = this.period = 0;\n // JVB (Jump and wait for Vertical Blank)\n if (this.dliop & 0x40) {\n this.linesleft = 1; //(248 - this.v) & 0xff; // TODO?\n this.dma_enabled = false;\n }\n this.dmaclock = 0;\n } else if (this.lms) {\n this.scanaddr = this.dlarg_lo + (this.dlarg_hi << 8);\n //console.log('scanaddr', hex(this.scanaddr));\n }\n this.startaddr = this.scanaddr;\n // horiz scroll\n let effwidth = this.regs[DMACTL] & 3;\n let hscroll = (this.dliop & 0x10) ? (this.regs[HSCROL] & 15) >> 1 : 0;\n if ((this.dliop & 0x10) && effwidth < 3) effwidth++;\n this.left = PF_LEFT[effwidth] + hscroll;\n this.right = PF_RIGHT[effwidth] + hscroll;\n // vertical scroll\n let vscrol = this.regs[VSCROL] & 0xf;\n if ((this.dliop & 0x20) ^ this.in_vscroll) {\n if (this.in_vscroll) {\n this.linesleft = vscrol+1; // exiting\n } else {\n this.linesleft -= vscrol; // entering\n this.yofs += vscrol;\n }\n this.linesleft &= 0xf;\n this.in_vscroll ^= 0x20;\n }\n }\n }\n\n nextLine() {\n if (this.linesleft > 0) {\n this.linesleft--;\n this.yofs++;\n this.isfirstline = false;\n if (this.mode >= 8 && this.linesleft) {\n this.scanaddr = this.startaddr; // reset line addr\n }\n }\n }\n\n triggerNMI(mask: number) {\n this.regs[NMIST] = mask | 0x1f;\n if (this.regs[NMIEN] & mask) {\n this.nmi();\n }\n }\n\n getDlistAddr() {\n return this.regs[DLISTL] + (this.regs[DLISTH] << 8);\n }\n\n nextInsn(): number {\n let pc = this.getDlistAddr();\n let b = this.read(pc);\n //console.log('nextInsn', hex(pc), hex(b), this.v);\n pc = ((pc + 1) & 0x3ff) | (pc & ~0x3ff);\n this.regs[DLISTL] = pc & 0xff;\n this.regs[DLISTH] = pc >> 8;\n return b;\n }\n\n nextScreen(): number {\n let b = this.read(this.scanaddr);\n this.incScanAddr();\n return b;\n }\n incScanAddr() {\n this.scanaddr = ((this.scanaddr + 1) & 0xfff) | (this.scanaddr & ~0xfff);\n }\n\n dlDMAEnabled() { return this.regs[DMACTL] & 0b100000; }\n\n isVisibleScanline() {\n return this.v >= 8 && this.v < 248;\n }\n isPlayfieldDMAEnabled() {\n return this.dma_enabled && !this.linesleft;\n }\n isPlayerDMAEnabled() {\n return this.regs[DMACTL] & 0b1000;\n }\n isMissileDMAEnabled() {\n return this.regs[DMACTL] & 0b1100;\n }\n isWSYNC() {\n return this.regs[WSYNC] != 0;\n }\n\n clockPulse(): boolean {\n let did_dma = this.isWSYNC();\n if (!this.isVisibleScanline()) {\n this.doVBlank();\n } else {\n switch (this.h) {\n case 0:\n if (this.isMissileDMAEnabled()) {\n this.doPlayerMissileDMA(3);\n did_dma = true;\n }\n break;\n case 1:\n if (this.isPlayfieldDMAEnabled()) {\n let op = this.nextInsn(); // get mode\n // TODO: too many booleans\n this.jmp = (op & ~0x40) == 0x01; // JMP insn?\n this.lms = (op & 0x40) != 0 && (op & 0xf) != 0; // LMS insn?\n this.mode = op & 0xf;\n this.dliop = op;\n this.yofs = 0;\n this.isfirstline = true;\n did_dma = true;\n }\n break;\n case 2: case 3: case 4: case 5:\n if (this.isPlayerDMAEnabled()) {\n this.doPlayerMissileDMA(this.h + 2);\n did_dma = true;\n }\n break;\n case 6:\n case 7:\n if (this.isPlayfieldDMAEnabled() && this.isfirstline && (this.jmp || this.lms)) {\n if (this.h == 6) this.dlarg_lo = this.nextInsn();\n if (this.h == 7) this.dlarg_hi = this.nextInsn();\n did_dma = true;\n }\n break;\n case 8:\n // TODO? is this at cycle 8?\n if (this.isfirstline) {\n this.processDLIEntry();\n }\n if (this.dliop & 0x80) { // TODO: what if DLI disabled?\n if (this.linesleft == 1) {\n this.triggerNMI(0x80); // DLI interrupt\n }\n }\n break;\n case 9:\n break;\n case 111:\n if (this.dma_enabled) this.nextLine();\n ++this.v;\n break;\n }\n this.output = 0; // background color (TODO: only for blank lines)\n if (this.mode >= 2 && this.period) {\n let candma = this.h <= LAST_DMA_H;\n this.dmaclock = (this.dmaclock << 1) & 0x1ff;\n if (this.dmaclock & (1 << this.period)) {\n this.dmaclock |= 1;\n }\n if (this.h == this.left) { this.dmaclock |= 1; this.dmaidx = 0; }\n if (this.h == this.right) { this.dmaclock &= ~1; this.dmaidx++; }\n if (this.dmaclock & 1) {\n if (this.mode < 8 && this.isfirstline) { // only read chars on 1st line\n if (candma) {\n this.linebuf[this.dmaidx] = this.nextScreen(); // read char name\n } else {\n this.incScanAddr();\n }\n did_dma = candma;\n }\n this.dmaidx++;\n } else if (this.dmaclock & 8) {\n this.ch = this.linebuf[this.dmaidx - 4 / this.period]; // latch char\n if (candma) {\n this.readBitmapData(); // read bitmap\n } else {\n if (this.mode >= 8) this.incScanAddr();\n }\n did_dma = candma;\n }\n this.output = this.h >= this.left + 3 && this.h <= this.right + 2 ? 4 : 0;\n }\n }\n if (this.h < ANTIC_LEFT || this.h > ANTIC_RIGHT) this.output = 2;\n this.incHorizCounter();\n if (!did_dma && this.dramrefresh) {\n this.read(0); // to log a VRAM_READ event\n this.dramrefresh = false;\n did_dma = true;\n }\n return did_dma;\n }\n incHorizCounter() {\n switch (this.h) {\n case 25: case 25 + 4 * 1: case 25 + 4 * 2: case 25 + 4 * 3: case 25 + 4 * 4:\n case 25 + 4 * 5: case 25 + 4 * 6: case 25 + 4 * 7: case 25 + 4 * 8:\n this.dramrefresh = true;\n break;\n case 102:\n this.regs[WSYNC] = 0; // TODO: dram refresh delay to 106?\n break;\n case 113:\n this.h = 0;\n return\n }\n ++this.h;\n }\n doVBlank() {\n this.linesleft = this.mode = this.period = 0;\n if (this.h == 111) { this.v++; }\n if (this.v == 248 && this.h == 0) { this.triggerNMI(0x40); } // VBI\n if (this.v == 262 && this.h == 112) { this.v = 0; }\n if (this.v == 7 && this.h == 113) { \n this.dma_enabled = this.dlDMAEnabled() != 0;\n }\n this.output = 2; // blank\n this.dmaclock = 0;\n }\n\n doPlayerMissileDMA(section: number) {\n let oneline = this.regs[DMACTL] & 0x10;\n let pmaddr = this.regs[PMBASE] << 8;\n if (oneline) {\n pmaddr &= 0xf800;\n pmaddr |= section << 8;\n pmaddr |= this.v & 0xff;\n } else {\n pmaddr &= 0xfc00;\n pmaddr |= section << 7;\n pmaddr |= this.v >> 1;\n }\n this.read(pmaddr);\n }\n\n readBitmapData() {\n const mode = this.mode;\n if (mode < 8) {\t// character mode\n let ch = this.ch;\n let y = this.yofs >> MODE_YPERIOD[this.mode];\n let addrofs = y & 7;\n let chbase = this.regs[CHBASE];\n // modes 6 & 7\n if ((mode & 0xe) == 6) { // or 7\n ch &= 0x3f;\n chbase &= 0xfe;\n } else {\n ch &= 0x7f;\n chbase &= 0xfc;\n }\n let addr = (ch << 3) + (chbase << 8);\n // modes 2 & 3\n if ((mode & 0xe) == 2) { // or 3\n let chactl = this.regs[CHACTL];\n let mode3lc = mode == 3 && (ch & 0x60) == 0x60;\n if (chactl & 4)\n this.pfbyte = this.read(addr + (addrofs ^ 7)); // mirror\n else\n this.pfbyte = this.read(addr + addrofs);\n if (mode3lc && y < 2) { this.pfbyte = 0; }\n if (!mode3lc && y > 7) { this.pfbyte = 0; }\n if (this.ch & 0x80) {\n if (chactl & 1)\n this.pfbyte = 0x0; // blank\n if (chactl & 2)\n this.pfbyte ^= 0xff; // invert\n }\n } else {\n this.pfbyte = this.read(addr + addrofs);\n }\n } else {\t// map mode\n this.pfbyte = this.nextScreen();\n }\n }\n\n shiftout() {\n if (this.output == 4) { // visible pixel?\n switch (this.mode) {\n case 2: case 3:\n case 15:\n {\n let v = (this.pfbyte >> 7) & 1;\n this.pfbyte <<= 1;\n return v ? 8 : 6;\n }\n case 6: case 7:\n {\n let v = (this.pfbyte >> 7) & 1;\n this.pfbyte <<= 1;\n return v ? (this.ch >> 6) + 4 : 0;\n }\n case 9: case 11: case 12:\n {\n let v = (this.pfbyte >> 7) & 1;\n this.pfbyte <<= 1;\n return v ? 4 : 0;\n }\n case 4: case 5:\n {\n let v = (this.pfbyte >> 6) & 3;\n this.pfbyte <<= 2;\n if (this.ch & 0x80)\n return [0, 4, 5, 7][v];\n else\n return [0, 4, 5, 6][v];\n }\n case 8: case 10:\n case 13: case 14:\n {\n let v = (this.pfbyte >> 6) & 3;\n this.pfbyte <<= 2;\n return [0, 4, 5, 6][v];\n }\n }\n }\n return this.output;\n }\n\n}\n", "/*\n * pokey.c - POKEY sound chip emulation\n *\n * Copyright (C) 1995-1998 David Firth\n * Copyright (C) 1998-2008 Atari800 development team (see DOC/CREDITS)\n *\n * This file is part of the Atari800 emulator project which emulates\n * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.\n *\n * Atari800 is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * Atari800 is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with Atari800; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n*/\n\nimport { dumpRAM } from \"../../common/emu\"\nimport { hex, lpad, safe_extend } from \"../../common/util\"\n\nconst AUDF1 = 0x00\nconst AUDC1 = 0x01\nconst AUDF2 = 0x02\nconst AUDC2 = 0x03\nconst AUDF3 = 0x04\nconst AUDC3 = 0x05\nconst AUDF4 = 0x06\nconst AUDC4 = 0x07\nconst AUDCTL = 0x08\nconst STIMER = 0x09\nconst SKRES = 0x0a\nconst POTGO = 0x0b\nconst SEROUT = 0x0d\nconst IRQEN = 0x0e\nconst SKCTL = 0x0f\n\nconst POT0 = 0x00\nconst POT1 = 0x01\nconst POT2 = 0x02\nconst POT3 = 0x03\nconst POT4 = 0x04\nconst POT5 = 0x05\nconst POT6 = 0x06\nconst POT7 = 0x07\nconst ALLPOT = 0x08\nconst KBCODE = 0x09\nconst RANDOM = 0x0a\nconst SERIN = 0x0d\nconst IRQST = 0x0e\nconst SKSTAT = 0x0f\n\n/* definitions for AUDCx (D201, D203, D205, D207) */\nconst NOTPOLY5 = 0x80 /* selects POLY5 or direct CLOCK */\nconst POLY4 = 0x40 /* selects POLY4 or POLY17 */\nconst PURETONE = 0x20 /* selects POLY4/17 or PURE tone */\nconst VOL_ONLY = 0x10 /* selects VOLUME OUTPUT ONLY */\nconst VOLUME_MASK = 0x0f /* volume mask */\n\n/* definitions for AUDCTL (D208) */\nconst POLY9 = 0x80 /* selects POLY9 or POLY17 */\nconst CH1_179 = 0x40 /* selects 1.78979 MHz for Ch 1 */\nconst CH3_179 = 0x20 /* selects 1.78979 MHz for Ch 3 */\nconst CH1_CH2 = 0x10 /* clocks channel 1 w/channel 2 */\nconst CH3_CH4 = 0x08 /* clocks channel 3 w/channel 4 */\nconst CH1_FILTER = 0x04 /* selects channel 1 high pass filter */\nconst CH2_FILTER = 0x02 /* selects channel 2 high pass filter */\nconst CLOCK_15 = 0x01 /* selects 15.6999kHz or 63.9210kHz */\n\n/* for accuracy, the 64kHz and 15kHz clocks are exact divisions of\n the 1.79MHz clock */\nconst DIV_64 = 28 /* divisor for 1.79MHz clock to 64 kHz */\nconst DIV_15 = 114 /* divisor for 1.79MHz clock to 15 kHz */\n\n/* the size (in entries) of the 4 polynomial tables */\nconst POLY4_SIZE = 0x000f\nconst POLY5_SIZE = 0x001f\nconst POLY9_SIZE = 0x01ff\nconst POLY17_SIZE = 0x0001ffff\n\nconst CHAN1 = 0\nconst CHAN2 = 1\nconst CHAN3 = 2\nconst CHAN4 = 3\n\nconst ANTIC_LINE_C = 114\n\n/* Some defines about the serial I/O timing. Currently fixed! */\nconst SIO_XMTDONE_INTERVAL = 15\nconst SIO_SERIN_INTERVAL = 8\nconst SIO_SEROUT_INTERVAL = 8\nconst SIO_ACK_INTERVAL = 36\n\nvar poly9: Uint8Array;\nvar poly17: Uint8Array;\n\nfunction initPolyTables() {\n poly9 = new Uint8Array(511);\n poly17 = new Uint8Array(16385);\n /* initialise poly9_lookup */\n let reg = 0x1ff;\n for (let i = 0; i < 511; i++) {\n reg = ((((reg >> 5) ^ reg) & 1) << 8) + (reg >> 1);\n poly9[i] = reg;\n }\n /* initialise poly17_lookup */\n reg = 0x1ffff;\n for (let i = 0; i < 16385; i++) {\n reg = ((((reg >> 5) ^ reg) & 0xff) << 9) + (reg >> 8);\n poly17[i] = (reg >> 1);\n }\n}\n\nexport class POKEY {\n regs = new Uint8Array(16);\n readregs = new Uint8Array(16);\n divnirq = new Uint32Array(4);\n divnmax = new Uint32Array(4);\n pot_inputs = new Uint8Array(8);\n basemult = 0;\n pot_scanline = 0;\n random_scanline_counter = 0;\n kbcode = 0;\n DELAYED_SERIN_IRQ = 0;\n DELAYED_SEROUT_IRQ = 0;\n DELAYED_XMTDONE_IRQ = 0;\n\n constructor(\n public irq: () => void,\n public antic_xpos: () => number,\n ) {\n this.init();\n }\n\n saveState() {\n return safe_extend(0, {}, this);\n }\n loadState(s) {\n safe_extend(0, this, s);\n }\n\n init() {\n /* Initialise Serial Port Interrupts */\n //DELAYED_SERIN_IRQ = 0;\n //DELAYED_SEROUT_IRQ = 0;\n //DELAYED_XMTDONE_IRQ = 0;\n this.readregs.fill(0xff);\n this.readregs[SKSTAT] = 0xef;\n //SERIN = 0x00;\t/* or 0xff ? */\n //IRQEN = 0x00;\n //SKCTL = 0x00;\n this.basemult = DIV_64;\n this.pot_inputs.fill(128);\n initPolyTables();\n }\n\n\n read(addr: number): number {\n let byte = this.readregs[addr];\n addr &= 0xf;\n switch (addr) {\n case 0: case 1: case 2: case 3:\n case 4: case 5: case 6: case 7:\n byte = this.pot_inputs[addr];\n return (byte < this.pot_scanline) ? byte : this.pot_scanline;\n case ALLPOT:\n for (let i = 0; i < 8; i++) {\n if (this.pot_inputs[i] <= this.pot_scanline)\n byte &= ~(1 << i);\t\t// reset bit if pot value known\n }\n break;\n case KBCODE:\n return this.kbcode;\n case SKSTAT:\n byte = SKSTAT + (this.CASSETTE_IOLineStatus() << 4);\n break;\n case RANDOM:\n if ((this.regs[SKCTL] & 0x03) != 0) {\n let i = this.random_scanline_counter + this.antic_xpos();\n if (this.regs[AUDCTL] & POLY9)\n byte = poly9[i % POLY9_SIZE];\n else {\n i %= POLY17_SIZE;\n let ptr = i >> 3;\n i &= 7;\n byte = (poly17[ptr] >> i) + (poly17[ptr + 1] << (8 - i));\n }\n }\n break;\n }\n return byte & 0xff;\n }\n\n write(addr: number, byte: number): void {\n addr &= 0x0f;\n this.regs[addr] = byte;\n switch (addr) {\n case AUDCTL:\n /* determine the base multiplier for the 'div by n' calculations */\n if (byte & CLOCK_15)\n this.basemult = DIV_15;\n else\n this.basemult = DIV_64;\n this.update_counter((1 << CHAN1) | (1 << CHAN2) | (1 << CHAN3) | (1 << CHAN4));\n break;\n case AUDF1:\n this.update_counter((this.regs[AUDCTL] & CH1_CH2) ? ((1 << CHAN2) | (1 << CHAN1)) : (1 << CHAN1));\n break;\n case AUDF2:\n this.update_counter(1 << CHAN2);\n break;\n case AUDF3:\n this.update_counter((this.regs[AUDCTL] & CH3_CH4) ? ((1 << CHAN4) | (1 << CHAN3)) : (1 << CHAN3));\n break;\n case AUDF4:\n this.update_counter(1 << CHAN4);\n break;\n case IRQEN:\n this.readregs[IRQST] |= ~byte & 0xf7;\t/* Reset disabled IRQs except XMTDONE */\n let mask = ~this.readregs[IRQST] & this.regs[IRQEN];\n if (mask) {\n this.generateIRQ(this.readregs[IRQST]);\n }\n break;\n case SKRES:\n this.readregs[SKSTAT] |= 0xe0;\n break;\n case POTGO:\n if (!(this.regs[SKCTL] & 4))\n this.pot_scanline = 0;\t/* slow pot mode */\n break;\n case SEROUT:\n if ((this.regs[SKCTL] & 0x70) == 0x20 && this.siocheck()) {\n this.SIO_PutByte(byte);\n }\n // check if cassette 2-tone mode has been enabled \n if ((this.regs[SKCTL] & 0x08) == 0x00) {\n // intelligent device\n this.DELAYED_SEROUT_IRQ = SIO_SEROUT_INTERVAL;\n this.readregs[IRQST] |= 0x08;\n this.DELAYED_XMTDONE_IRQ = SIO_XMTDONE_INTERVAL;\n }\n else {\n // cassette \n // some savers patch the cassette baud rate, so we evaluate it here \n // scanlines per second*10 bit*audiofrequency/(1.79 MHz/2) \n this.DELAYED_SEROUT_IRQ = 312 * 50 * 10 * (this.regs[AUDF3] + this.regs[AUDF4] * 0x100) / 895000;\n // safety check \n if (this.DELAYED_SEROUT_IRQ >= 3) {\n this.readregs[IRQST] |= 0x08;\n this.DELAYED_XMTDONE_IRQ = 2 * this.DELAYED_SEROUT_IRQ - 2;\n }\n else {\n this.DELAYED_SEROUT_IRQ = 0;\n this.DELAYED_XMTDONE_IRQ = 0;\n }\n };\n break;\n case STIMER:\n this.divnirq[CHAN1] = this.divnmax[CHAN1];\n this.divnirq[CHAN2] = this.divnmax[CHAN2];\n this.divnirq[CHAN3] = this.divnmax[CHAN3];\n this.divnirq[CHAN4] = this.divnmax[CHAN4];\n //POKEYSND_Update(STIMER, byte, 0, SOUND_GAIN);\n break;\n case SKCTL:\n //VOICEBOX_SKCTLPutByte(byte);\n //POKEYSND_Update(SKCTL, byte, 0, SOUND_GAIN);\n if (byte & 4)\n this.pot_scanline = 228;\t/* fast pot mode - return results immediately */\n if ((byte & 0x03) == 0) {\n /* POKEY reset. */\n /* Stop serial IO. */\n this.DELAYED_SERIN_IRQ = 0;\n this.DELAYED_SEROUT_IRQ = 0;\n this.DELAYED_XMTDONE_IRQ = 0;\n // TODO: CASSETTE_ResetPOKEY();\n /* TODO other registers should also be reset. */\n }\n break;\n }\n this.snd_update(addr);\n //POKEYSND_Update(AUDC1, byte, 0, SOUND_GAIN);\n }\n\n /*****************************************************************************/\n /* Module: Update_Counter() */\n /* Purpose: To process the latest control values stored in the AUDF, AUDC, */\n /* and AUDCTL registers. It pre-calculates as much information as */\n /* possible for better performance. This routine has been added */\n /* here again as I need the precise frequency for the pokey timers */\n /* again. The pokey emulation is therefore somewhat sub-optimal */\n /* since the actual pokey emulation should grab the frequency values */\n /* directly from here instead of calculating them again. */\n /* */\n /* Author: Ron Fries,Thomas Richter */\n /* Date: March 27, 1998 */\n /* */\n /* Inputs: chan_mask: Channel mask, one bit per channel. */\n /* The channels that need to be updated */\n /* */\n /* Outputs: Adjusts local globals - no return value */\n /* */\n /*****************************************************************************/\n\n update_counter(chan_mask: number): void {\n\n /************************************************************/\n /* As defined in the manual, the exact Div_n_cnt values are */\n /* different depending on the frequency and resolution: */\n /* 64 kHz or 15 kHz - AUDF + 1 */\n /* 1 MHz, 8-bit - AUDF + 4 */\n /* 1 MHz, 16-bit - AUDF[CHAN1]+256*AUDF[CHAN2] + 7 */\n /************************************************************/\n\n /* only reset the channels that have changed */\n\n if (chan_mask & (1 << CHAN1)) {\n /* process channel 1 frequency */\n if (this.regs[AUDCTL] & CH1_179)\n this.divnmax[CHAN1] = this.regs[AUDF1 + CHAN1] + 4;\n else\n this.divnmax[CHAN1] = (this.regs[AUDF1 + CHAN1] + 1) * this.basemult;\n if (this.divnmax[CHAN1] < ANTIC_LINE_C)\n this.divnmax[CHAN1] = ANTIC_LINE_C;\n }\n\n if (chan_mask & (1 << CHAN2)) {\n /* process channel 2 frequency */\n if (this.regs[AUDCTL] & CH1_CH2) {\n if (this.regs[AUDCTL] & CH1_179)\n this.divnmax[CHAN2] = this.regs[AUDF1 + CHAN2] * 256 + this.regs[AUDF1 + CHAN1] + 7;\n else\n this.divnmax[CHAN2] = (this.regs[AUDF1 + CHAN2] * 256 + this.regs[AUDF1 + CHAN1] + 1) * this.basemult;\n }\n else\n this.divnmax[CHAN2] = (this.regs[AUDF1 + CHAN2] + 1) * this.basemult;\n if (this.divnmax[CHAN2] < ANTIC_LINE_C)\n this.divnmax[CHAN2] = ANTIC_LINE_C;\n }\n\n if (chan_mask & (1 << CHAN4)) {\n /* process channel 4 frequency */\n if (this.regs[AUDCTL] & CH3_CH4) {\n if (this.regs[AUDCTL] & CH3_179)\n this.divnmax[CHAN4] = this.regs[AUDF1 + CHAN4] * 256 + this.regs[AUDF1 + CHAN3] + 7;\n else\n this.divnmax[CHAN4] = (this.regs[AUDF1 + CHAN4] * 256 + this.regs[AUDF1 + CHAN3] + 1) * this.basemult;\n }\n else\n this.divnmax[CHAN4] = (this.regs[AUDF1 + CHAN4] + 1) * this.basemult;\n if (this.divnmax[CHAN4] < ANTIC_LINE_C)\n this.divnmax[CHAN4] = ANTIC_LINE_C;\n }\n\n //console.log(chan_mask, this.divnmax);\n }\n\n snd_update(addr: number) {\n\n }\n\n advanceScanline() {\n /***************************************************************************\n ** Generate POKEY Timer IRQs if required **\n ** called on a per-scanline basis, not very precise, but good enough **\n ** for most applications **\n ***************************************************************************/\n\n\n /* on nonpatched i/o-operation, enable the cassette timing */\n /*\n if (!ESC_enable_sio_patch) {\n if (CASSETTE_AddScanLine())\n DELAYED_SERIN_IRQ = 1;\n }\n */\n\n if ((this.regs[SKCTL] & 0x03) == 0)\n /* Don't process timers when POKEY is in reset mode. */\n return;\n\n if (this.pot_scanline < 228)\n this.pot_scanline++;\n\n this.random_scanline_counter += ANTIC_LINE_C;\n this.random_scanline_counter %= (this.regs[AUDCTL] & POLY9) ? POLY9_SIZE : POLY17_SIZE;\n\n if (this.DELAYED_SERIN_IRQ > 0) {\n if (--this.DELAYED_SERIN_IRQ == 0) {\n // Load a byte to SERIN - even when the IRQ is disabled. \n this.readregs[SERIN] = this.SIO_GetByte();\n this.generateIRQ(0x20);\n }\n }\n\n if (this.DELAYED_SEROUT_IRQ > 0) {\n if (--this.DELAYED_SEROUT_IRQ == 0) {\n this.generateIRQ(0x10);\n }\n }\n\n if (this.DELAYED_XMTDONE_IRQ > 0)\n if (--this.DELAYED_XMTDONE_IRQ == 0) {\n this.generateIRQ(0x08);\n }\n\n this.advanceIRQTimer(CHAN1, 0x1);\n this.advanceIRQTimer(CHAN2, 0x2);\n this.advanceIRQTimer(CHAN4, 0x4);\n }\n\n advanceIRQTimer(chan: number, mask: number) {\n if ((this.divnirq[chan] -= ANTIC_LINE_C) < 0) {\n this.divnirq[chan] += this.divnmax[chan];\n this.generateIRQ(mask);\n //console.log('irq', chan, this.divnirq[chan], this.divnmax[chan])\n }\n }\n\n generateIRQ(mask: number) {\n if (this.regs[IRQEN] & mask) {\n this.irq();\n this.readregs[IRQST] &= ~mask;\n }\n }\n\n static stateToLongString(state): string {\n let s = ''\n s += \"Write Registers:\\n\";\n s += dumpRAM(state.regs, 0, 16);\n s += \"Read Registers:\\n\";\n s += dumpRAM(state.readregs, 0, 16);\n return s;\n }\n\n CASSETTE_IOLineStatus() {\n return 0;\n }\n\n siocheck() {\n return (((this.regs[AUDF1 + CHAN3] == 0x28 || this.regs[AUDF1 + CHAN3] == 0x10\n || this.regs[AUDF1 + CHAN3] == 0x08 || this.regs[AUDF1 + CHAN3] == 0x0a)\n && this.regs[AUDF1 + CHAN4] == 0x00) // intelligent peripherals speeds\n || (this.regs[SKCTL] & 0x78) == 0x28) // cassette save mode\n && (this.regs[AUDCTL] & 0x28) == 0x28;\n }\n SIO_PutByte(byte: number) {\n // TODO\n console.log(\"SIO put byte\", byte);\n }\n SIO_GetByte() {\n return 0; // TODO\n }\n\n}\n\n\n//const SOUND_GAIN 4\n/*\nvoid Frame(void)\n{\n random_scanline_counter %= (this.regs[AUDCTL] & POLY9) ? POLY9_SIZE : POLY17_SIZE;\n}\n*/\n\n\n\n", "// Minimal Atari 8-bit disk image support.\n//\n// Parses ATR images (and raw 128-byte-sector .xfd/.dsk images) and exposes\n// sector reads/writes. Sector numbers are 1-based to match SIO/DAUX1/DAUX2.\n//\n// ATR layout: http://www.atarimax.com/jindroush.atari.org/afmtdos.html\n// 0 word magic $0296\n// 2 word paragraph count (16-byte units, includes the 16-byte header)\n// 4 word sector size (128 or 256)\n// 6 dword CRC (unused)\n// 10 dword unused\n// 16 sector data\n\nconst ATR_MAGIC = 0x0296;\n\nexport class DiskImage {\n data: Uint8Array;\n sectorsize: number;\n numsectors: number;\n\n constructor(data: Uint8Array) {\n this.data = data;\n if (data.length >= 16 && (data[0] | (data[1] << 8)) == ATR_MAGIC) {\n const paragraphs = data[2] | (data[3] << 8);\n const total = paragraphs * 16;\n const sectorsize = data[4] | (data[5] << 8);\n // enhanced-density images declare 256-byte sectors, but the first three\n // (boot) sectors are still 128 bytes\n this.sectorsize = sectorsize == 256 ? 256 : 128;\n if (this.sectorsize == 256) {\n this.numsectors = 3 + Math.floor((total - 16 - 3 * 128) / 256);\n } else {\n this.numsectors = Math.floor((total - 16) / 128);\n }\n } else {\n // raw image, assume single density\n this.sectorsize = 128;\n this.numsectors = Math.floor(data.length / 128);\n }\n }\n\n private sectorOffset(sector: number): number {\n // sector is 0-based here\n if (this.sectorsize == 256) {\n if (sector < 3) return 16 + sector * 128;\n return 16 + 3 * 128 + (sector - 3) * 256;\n }\n return 16 + sector * 128;\n }\n\n /** Read up to len bytes from 1-based sector, returns null if out of range. */\n readSector(sector: number, len: number): Uint8Array {\n const idx = sector - 1;\n if (idx < 0 || idx >= this.numsectors) return null;\n const ofs = this.sectorOffset(idx);\n return this.data.subarray(ofs, Math.min(ofs + len, this.data.length));\n }\n\n /** Write len bytes into 1-based sector; returns false if out of range. */\n writeSector(sector: number, src: Uint8Array, len: number): boolean {\n const idx = sector - 1;\n if (idx < 0 || idx >= this.numsectors) return false;\n const ofs = this.sectorOffset(idx);\n this.data.set(src.subarray(0, Math.min(len, this.data.length - ofs)), ofs);\n return true;\n }\n}\n", "import { newPOKEYAudio, TssChannelAdapter } from \"../common/audio\";\nimport { MOS6502 } from \"../common/cpu/MOS6502\";\nimport { AcceptsPaddleInput, BasicScanlineMachine } from \"../common/devices\";\nimport { KeyFlags, Keys, makeKeycodeMap, newAddressDecoder, newKeyboardHandler } from \"../common/emu\";\nimport { hex } from \"../common/util\";\nimport { ANTIC, MODE_LINES, MODE_SHIFT } from \"./chips/antic\";\nimport { CONSOL, GTIA, TRIG0 } from \"./chips/gtia\";\nimport { POKEY } from \"./chips/pokey\";\nimport { DiskImage } from \"./atari8disk\";\n\n// High-level SIO patch: redirect the OS SIOV vector to a stub in the $D5xx\n// mapper area. The stub traps disk-device calls back into JS (via a magic\n// write) and falls through to the real SIO routine for everything else.\n// We stash the stub at $D5A0 so the XEX loader's stub (which starts at $D500)\n// doesn't clobber it.\nconst SIO_STUB_OFFSET = 0xa0; // $D5A0\nconst SIO_HOOK_OFFSET = 0xf0; // $D5F0 - write here to call JS\nconst SIO_STATUS_OFFSET = 0xf1; // $D5F1 - JS puts the SIO status here\n// lda $0300; cmp #$31; bcc notdisk; cmp #$40; bcs notdisk;\n// sta $D5F0; lda $D5F1; tay; rts; notdisk: jmp $E950\nconst SIO_STUB = [\n 0xad, 0x00, 0x03, 0xc9, 0x31, 0x90, 0x0c, 0xc9, 0x40, 0xb0, 0x08,\n 0x8d, 0xf0, 0xd5, 0xad, 0xf1, 0xd5, 0xa8, 0x60, 0x4c, 0x50, 0xe9,\n];\n\nconst ATARI8_KEYMATRIX_INTL_NOSHIFT = [\n Keys.VK_L, Keys.VK_J, Keys.VK_SEMICOLON, Keys.VK_F4, Keys.VK_F5, Keys.VK_K, Keys.VK_BACK_SLASH, Keys.VK_TILDE,\n Keys.VK_O, null, Keys.VK_P, Keys.VK_U, Keys.VK_ENTER, Keys.VK_I, Keys.VK_MINUS2, Keys.VK_EQUALS2,\n Keys.VK_V, Keys.VK_F7, Keys.VK_C, Keys.VK_F6, Keys.VK_F4, Keys.VK_B, Keys.VK_X, Keys.VK_Z,\n Keys.VK_4, null, Keys.VK_3, Keys.VK_6, Keys.VK_ESCAPE, Keys.VK_5, Keys.VK_2, Keys.VK_1,\n Keys.VK_COMMA, Keys.VK_SPACE, Keys.VK_PERIOD, Keys.VK_N, null, Keys.VK_M, Keys.VK_SLASH, null/*invert*/,\n Keys.VK_R, null, Keys.VK_E, Keys.VK_Y, Keys.VK_TAB, Keys.VK_T, Keys.VK_W, Keys.VK_Q,\n Keys.VK_9, null, Keys.VK_0, Keys.VK_7, Keys.VK_BACK_SPACE, Keys.VK_8, null, null,\n Keys.VK_F, Keys.VK_H, Keys.VK_D, null, Keys.VK_CAPS_LOCK, Keys.VK_G, Keys.VK_S, Keys.VK_A,\n];\n\n//TODO\nvar ATARI8_KEYCODE_MAP = makeKeycodeMap([\n [Keys.UP, 0, 0x1],\n [Keys.DOWN, 0, 0x2],\n [Keys.LEFT, 0, 0x4],\n [Keys.RIGHT, 0, 0x8],\n [{ c: 16, n: \"Shift\", plyr: 0, button: 0 }, 2, 0x1],\n /*\n [Keys.P2_UP, 0, 0x10],\n [Keys.P2_DOWN, 0, 0x20],\n [Keys.P2_LEFT, 0, 0x40],\n [Keys.P2_RIGHT, 0, 0x80],\n [Keys.P2_A, 3, 0x1],\n */\n [Keys.VK_F1, 3, 0x1], // START\n [Keys.VK_F2, 3, 0x2], // SELECT\n [Keys.VK_F3, 3, 0x4], // OPTION\n]);\n\n\nexport class Atari800 extends BasicScanlineMachine implements AcceptsPaddleInput {\n\n // http://www.ataripreservation.org/websites/freddy.offenga/megazine/ISSUE5-PALNTSC.html\n cpuFrequency = 1789773;\n numTotalScanlines = 262;\n cpuCyclesPerLine = 114;\n canvasWidth = 336;\n numVisibleScanlines = 224;\n aspectRatio = this.canvasWidth / this.numVisibleScanlines * 0.857;\n firstVisibleScanline = 16;\n firstVisibleClock = (44 - 6) * 2; // ... to 215 * 2\n defaultROMSize = 0x8000;\n overscan = true;\n audioOversample = 2;\n sampleRate = this.numTotalScanlines * 60 * this.audioOversample;\n run_address = -1;\n\n cpu: MOS6502;\n ram: Uint8Array;\n bios: Uint8Array;\n bus;\n audio_pokey;\n audioadapter;\n antic: ANTIC;\n gtia: GTIA;\n irq_pokey: POKEY;\n inputs = new Uint8Array(4);\n linergb = new Uint32Array(this.canvasWidth);\n lastdmabyte = 0;\n keycode = 0;\n cart_80 = false;\n cart_a0 = false;\n xexdata = null;\n disk: DiskImage = null;\n keyboard_active = true;\n d500 = new Uint8Array(0x100);\n // TODO: save/load vars\n\n constructor() {\n super();\n this.cpu = new MOS6502();\n this.ram = new Uint8Array(0x10000);\n this.bios = new Uint8Array(0x2800);\n this.bus = this.newBus();\n this.connectCPUMemoryBus(this.bus);\n // create support chips\n this.antic = new ANTIC(this.readDMA.bind(this), this.antic_nmi.bind(this));\n this.gtia = new GTIA();\n this.irq_pokey = new POKEY(this.pokey_irq.bind(this), () => this.antic.h);\n this.audio_pokey = newPOKEYAudio(1);\n this.audioadapter = new TssChannelAdapter(this.audio_pokey.pokey1, this.audioOversample, this.sampleRate);\n this.handler = newKeyboardHandler(\n this.inputs, ATARI8_KEYCODE_MAP, this.getKeyboardFunction(), true);\n }\n newBus() {\n return {\n read: newAddressDecoder([\n [0x0000, 0x7fff, 0xffff, (a) => { return this.ram[a]; }],\n [0x8000, 0x9fff, 0xffff, (a) => { return this.cart_80 ? this.rom[a - 0x8000] : this.ram[a]; }],\n [0xa000, 0xbfff, 0xffff, (a) => { return this.cart_a0 ? this.rom[a - 0x8000] : this.ram[a]; }],\n [0xd000, 0xd0ff, 0x1f, (a) => { return this.gtia.readReg(a); }],\n [0xd200, 0xd2ff, 0xf, (a) => { return this.readPokey(a); }],\n [0xd300, 0xd3ff, 0xf, (a) => { return this.readPIA(a); }],\n [0xd400, 0xd4ff, 0xf, (a) => { return this.antic.readReg(a); }],\n [0xd500, 0xd5ff, 0xff, (a) => { return this.d500[a]; }],\n [0xd800, 0xffff, 0xffff, (a) => { return this.bios[a - 0xd800]; }],\n ]),\n write: newAddressDecoder([\n [0x0000, 0xbffa, 0xffff, (a, v) => { this.ram[a] = v; }],\n [0xbffb, 0xbfff, 0xffff, (a, v) => { this.ram[a] = v; this.initCartA(); }],\n [0xd000, 0xd0ff, 0x1f, (a, v) => { this.gtia.setReg(a, v); }],\n [0xd200, 0xd2ff, 0xf, (a, v) => { this.writePokey(a, v); }],\n [0xd400, 0xd4ff, 0xf, (a, v) => { this.antic.setReg(a, v); }],\n [0xd500, 0xd5ff, 0xff, (a, v) => { this.writeMapper(a, v); }],\n ]),\n };\n }\n\n loadBIOS(bios: Uint8Array) {\n this.bios.set(bios);\n this.installSIOStub();\n }\n\n installSIOStub() {\n // patch the SIOV jump table entry ($E459: JMP $E950) to enter our stub\n const o = 0xe459 - 0xd800;\n this.bios[o] = 0x4c; // JMP\n this.bios[o + 1] = 0xd500 + SIO_STUB_OFFSET & 0xff;\n this.bios[o + 2] = (0xd500 + SIO_STUB_OFFSET) >> 8;\n this.d500.set(SIO_STUB, SIO_STUB_OFFSET);\n }\n\n reset() {\n super.reset();\n this.antic.reset();\n this.gtia.reset();\n this.keycode = 0;\n //if (this.xexdata) this.cart_a0 = true; // TODO\n }\n\n read(a) {\n // TODO: lastdmabyte?\n return this.bus.read(a);\n }\n // used by ANTIC\n readDMA(a) {\n let v = this.bus.read(a);\n this.probe.logDMARead(a, v);\n this.lastdmabyte = v;\n return v;\n }\n readConst(a) {\n return a < 0xd000 || a >= 0xd500 ? this.bus.read(a) : 0xff;\n }\n write(a, v) {\n this.bus.write(a, v);\n }\n readPokey(a: number) {\n switch (a & 0xf) {\n case 9: // KBCODE\n return this.keycode & 0xff;\n case 15: // SKSTAT\n return ((~this.keycode >> 6) & 0x4) | ((~this.keycode >> 3) & 0x8) | 0x12;\n default:\n return this.irq_pokey.read(a);\n }\n }\n readPIA(a: number) {\n if (a == 0 || a == 1) { return ~this.inputs[a]; }\n }\n writePokey(a, v) {\n this.audio_pokey.pokey1.setRegister(a, v);\n this.irq_pokey.write(a, v);\n }\n\n startScanline() {\n // TODO: if (this.antic.h != 0) throw new Error(this.antic.h+\"\");\n //if (this.cpu.isHalted()) throw new EmuHalt(\"CPU HALTED\");\n // set GTIA switch inputs\n this.gtia.sync();\n // TODO: trigger latching mode\n for (let i = 0; i < 4; i++)\n this.gtia.readregs[TRIG0 + i] = (~this.inputs[2] >> i) & 1;\n // console switches\n this.gtia.readregs[CONSOL] = ~this.inputs[3] & 0x7;\n // advance POKEY audio\n this.audio && this.audioadapter.generate(this.audio);\n // advance POKEY IRQ timers\n this.irq_pokey.advanceScanline();\n }\n\n drawScanline() {\n // TODO\n let y = this.antic.v - this.firstVisibleScanline;\n if (y >= 0 && y < this.numVisibleScanlines) {\n this.pixels.set(this.linergb, y * this.canvasWidth);\n }\n }\n\n advanceCPU(): number {\n // update ANTIC\n if (this.antic.clockPulse()) {\n // ANTIC DMA cycle, update GTIA\n if (this.antic.h < 8)\n this.gtia.updateGfx(this.antic.h - 1, this.antic.v, this.lastdmabyte); // HALT pin\n if (this.antic.isWSYNC())\n this.probe.logWait(0);\n this.probe.logClocks(1);\n } else {\n super.advanceCPU();\n }\n // update GTIA\n // get X coordinate within scanline\n let xofs = this.antic.h * 4 - this.firstVisibleClock;\n // GTIA tick functions\n let gtiatick1 = () => {\n this.gtia.clockPulse1();\n this.linergb[xofs++] = this.gtia.rgb;\n }\n let gtiatick2 = () => {\n this.gtia.clockPulse2();\n this.linergb[xofs++] = this.gtia.rgb;\n }\n // tick 4 GTIA clocks for each CPU/ANTIC cycle\n this.gtia.clockPulse4();\n // correct for HSCROL -- bias antic +2, bias gtia -1\n if ((this.antic.dliop & 0x10) && (this.antic.regs[4] & 1)) {\n xofs += 2;\n this.gtia.setBias(-1);\n } else {\n this.gtia.setBias(0);\n }\n let bp = MODE_SHIFT[this.antic.mode];\n let odd = this.antic.h & 1;\n if (bp < 8 || odd) { this.gtia.an = this.antic.shiftout(); }\n gtiatick1();\n if (bp == 1) { this.gtia.an = this.antic.shiftout(); }\n gtiatick2();\n if (bp <= 2) { this.gtia.an = this.antic.shiftout(); }\n gtiatick1();\n if (bp == 1) { this.gtia.an = this.antic.shiftout(); }\n gtiatick2();\n return 1;\n }\n\n loadState(state: any) {\n this.loadControlsState(state);\n this.cpu.loadState(state.c);\n this.ram.set(state.ram);\n this.antic.loadState(state.antic);\n this.gtia.loadState(state.gtia);\n this.irq_pokey.loadState(state.pokey);\n this.lastdmabyte = state.lastdmabyte;\n this.cart_80 = state.cart_80;\n this.cart_a0 = state.cart_a0;\n }\n saveState() {\n return {\n c: this.cpu.saveState(),\n ram: this.ram.slice(0),\n antic: this.antic.saveState(),\n gtia: this.gtia.saveState(),\n pokey: this.irq_pokey.saveState(),\n inputs: this.inputs.slice(0),\n lastdmabyte: this.lastdmabyte,\n keycode: this.keycode,\n cart_80: this.cart_80,\n cart_a0: this.cart_a0,\n };\n }\n loadControlsState(state) {\n this.inputs.set(state.inputs);\n this.keycode = state.keycode;\n }\n saveControlsState() {\n return {\n inputs: this.inputs.slice(0),\n keycode: this.keycode,\n };\n }\n getRasterY() {\n return this.antic.v;\n }\n getRasterX() {\n return this.antic.h;\n }\n getRasterCanvasPosition() {\n return {\n x: this.antic.h * 4 - this.firstVisibleClock,\n y: this.antic.v - this.firstVisibleScanline,\n }\n }\n getDebugCategories() {\n return ['CPU', 'Stack', 'ANTIC', 'GTIA', 'POKEY'];\n }\n getDebugInfo(category, state) {\n switch (category) {\n case 'ANTIC': return ANTIC.stateToLongString(state.antic);\n case 'GTIA': return GTIA.stateToLongString(state.gtia);\n case 'POKEY': return POKEY.stateToLongString(state.pokey);\n }\n }\n getKeyboardFunction() {\n return (o, key, code, flags) => {\n if (!this.keyboard_active) return false;\n if (flags & (KeyFlags.KeyDown | KeyFlags.KeyUp)) {\n //console.log(o, key, code, flags, hex(this.keycode));\n var keymap = ATARI8_KEYMATRIX_INTL_NOSHIFT;\n if (key == Keys.VK_F9.c) {\n this.irq_pokey.generateIRQ(0x80); // break IRQ\n return true;\n }\n for (var i = 0; i < keymap.length; i++) {\n if (keymap[i] && keymap[i].c == key) {\n this.keycode = i;\n if (flags & KeyFlags.Shift) { this.keycode |= 0x40; }\n if (flags & KeyFlags.Ctrl) { this.keycode |= 0x80; }\n if (flags & KeyFlags.KeyDown) {\n this.keycode |= 0x100;\n this.irq_pokey.generateIRQ(0x40); // key pressed IRQ\n return true;\n }\n }\n }\n };\n }\n }\n pokey_irq() {\n this.cpu.IRQ();\n this.probe.logInterrupt(2);\n }\n antic_nmi() {\n this.cpu.NMI();\n this.probe.logInterrupt(1);\n }\n\n loadROM(rom: Uint8Array, title: string) {\n if (rom[0] == 0x96 && rom[1] == 0x02) {\n // ATR disk image, boot it as drive 1\n this.disk = new DiskImage(rom);\n this.xexdata = null;\n console.log(`ATR disk: ${this.disk.numsectors} sectors x ${this.disk.sectorsize} bytes`);\n } else if ((rom[0] == 0xff && rom[1] == 0xff) && !title.endsWith('.rom')) {\n // XEX file, chill out and wait for BIOS hook\n this.disk = null;\n this.xexdata = rom;\n } else {\n this.disk = null;\n this.loadCartridge(rom);\n }\n }\n\n loadCartridge(rom: Uint8Array) {\n // TODO: https://github.com/dmlloyd/atari800/blob/master/DOC/cart.txt\n // strip off header\n if (rom[0] == 0x43 && rom[1] == 0x41 && rom[2] == 0x52 && rom[3] == 0x54) {\n rom = rom.slice(16);\n }\n if (rom.length != 0x1000 && rom.length != 0x2000 && rom.length != 0x4000 && rom.length != 0x8000)\n throw new Error(\"Sorry, this platform can only load 4/8/16/32 KB cartridges at the moment.\");\n // TODO: support other than 8 KB carts\n // support 4/8/16/32 KB carts\n let rom2 = new Uint8Array(0x8000);\n for (let i = 0; i <= rom2.length - rom.length; i += rom.length) {\n rom2.set(rom, i);\n }\n this.run_address = rom2[0x7ffe] + rom2[0x7fff]*256;\n this.cart_a0 = true; // TODO\n this.cart_80 = rom.length == 0x4000;\n super.loadROM(rom2);\n }\n\n writeMapper(addr: number, value: number) {\n // TODO\n if (addr == 0xff) {\n if (value == 0x80) this.cart_80 = false;\n if (value == 0xa0) this.cart_a0 = false;\n } else if (addr == SIO_HOOK_OFFSET) {\n this.handleSIO();\n }\n }\n\n // Called from the patched SIO stub with the SIO parameter block in page 3.\n handleSIO() {\n const dunit = this.read(0x301);\n const dcomnd = this.read(0x302);\n const dbuf = this.read(0x304) | (this.read(0x305) << 8);\n let dbyt = this.read(0x308) | (this.read(0x309) << 8);\n const sector = this.read(0x30a) | (this.read(0x30b) << 8);\n const disk = dunit == 1 ? this.disk : null;\n let status = 0x01; // SIOSuccess\n if (disk == null) {\n status = 0x8a; // SIOErrorTimeout (no drive in that unit)\n } else {\n switch (dcomnd) {\n case 0x53: { // STATUS\n const dvstat = [0x00, 0xff, 0xe0, 0x00];\n const n = Math.min(dbyt || 4, dvstat.length);\n for (let i = 0; i < n; i++) this.write(dbuf + i, dvstat[i]);\n break;\n }\n case 0x52: { // READ\n if (dbyt == 0) dbyt = disk.sectorsize;\n const bytes = disk.readSector(sector, dbyt);\n if (bytes == null) {\n status = 0x90; // SIOErrorDeviceError\n } else {\n for (let i = 0; i < dbyt; i++)\n this.write(dbuf + i, i < bytes.length ? bytes[i] : 0);\n }\n break;\n }\n case 0x57: // WRITE\n case 0x50: { // PUT\n if (dbyt == 0) dbyt = disk.sectorsize;\n const buf = new Uint8Array(dbyt);\n for (let i = 0; i < dbyt; i++) buf[i] = this.read(dbuf + i);\n if (!disk.writeSector(sector, buf, dbyt)) status = 0x90;\n break;\n }\n default:\n status = 0x8b; // SIOErrorNAK (unsupported command)\n }\n }\n // mirror the OS SIO exit: status goes to DSTATS and the stub reads it back\n this.write(0x303, status);\n this.d500[SIO_STATUS_OFFSET] = status;\n }\n\n loadXEX(rom: Uint8Array) {\n let ofs = 2;\n let stub = this.d500;\n let stubofs = 0; // stub routine \n var runaddr = -1;\n // load segments into RAM\n while (ofs < rom.length) {\n let start = rom[ofs + 0] + rom[ofs + 1] * 256;\n let end = rom[ofs + 2] + rom[ofs + 3] * 256;\n console.log('XEX', hex(ofs), hex(start), hex(end));\n ofs += 4;\n for (let i = start; i <= end; i++) {\n this.ram[i] = rom[ofs++];\n }\n if (start == 0x2e0 && end == 0x2e1) {\n runaddr = this.ram[0x2e0] + this.ram[0x2e1] * 256;\n console.log('XEX run', hex(runaddr));\n }\n if (start == 0x2e2 && end == 0x2e3) {\n var initaddr = this.ram[0x2e2] + this.ram[0x2e3] * 256;\n console.log('XEX init', hex(initaddr));\n stub[stubofs++] = 0x20;\n stub[stubofs++] = initaddr & 0xff;\n stub[stubofs++] = initaddr >> 8;\n }\n if (ofs > rom.length) throw new Error(\"Bad .XEX file format\");\n }\n if (runaddr >= 0) {\n // build stub routine at 0xd500\n stub[stubofs++] = 0xa9; // lda #$a0\n stub[stubofs++] = 0xa0;\n stub[stubofs++] = 0x8d; // sta $d5ff (disable cart)\n stub[stubofs++] = 0xff;\n stub[stubofs++] = 0xd5;\n stub[stubofs++] = 0x4c; // jmp runaddr\n stub[stubofs++] = runaddr & 0xff;\n stub[stubofs++] = runaddr >> 8;\n // set DOSVEC to 0xd500\n this.ram[0xa] = 0x00;\n this.ram[0xb] = 0xd5;\n this.run_address = 0xd500;\n }\n }\n\n initCartA() {\n if (this.cpu.getPC() == 0xf17f && this.xexdata) {\n this.loadXEX(this.xexdata);\n }\n }\n\n setPaddleInput(controller: number, value: number): void {\n this.irq_pokey.pot_inputs[controller] = 255 - value;\n }\n\n getDebugDisplayList() {\n let pc = this.antic.getDlistAddr();\n const nextInsn = () => {\n let b = this.read(pc);\n pc = ((pc + 1) & 0x3ff) | (pc & ~0x3ff);\n return b;\n }\n let dlist = [];\n let y = 0;\n for (let i=0; i<256 && y<240; i++) {\n let pc0 = pc;\n let op = nextInsn(); // get mode\n let mode = op & 0xf;\n let debugmsg = \"\" // \"op=\" + hex(op);\n let jmp = false;\n let lines;\n if (mode == 0) {\n lines = (((op >> 4) & 7) + 1);\n debugmsg += \" blank=\" + lines;\n } else {\n lines = MODE_LINES[mode];\n debugmsg += \" mode=\" + hex(mode);\n debugmsg += \" lines=\" + lines;\n jmp = (op & ~0x40) == 0x01; // JMP insn?\n let lms = (op & 0x40) != 0 && (op & 0xf) != 0; // LMS insn?\n if (op & 0x10) { debugmsg += \" HSCROL\"; }\n if (op & 0x20) { debugmsg += \" VSCROL\"; }\n if (op & 0x80) { debugmsg += \" DLI\"; }\n if (jmp && (op & 0x40)) { debugmsg += \" JVB\"; }\n else if (jmp) debugmsg += \" JMP\";\n else if (lms) debugmsg += \" LMS\";\n if (jmp || lms) {\n let dlarg_lo = nextInsn();\n let dlarg_hi = nextInsn();\n debugmsg += \" $\" + hex(dlarg_hi) + \"\" + hex(dlarg_lo);\n }\n }\n dlist.push(\"$\"+hex(pc0) + \" y=\" + y + \" \" + debugmsg);\n if (jmp) break;\n y += lines;\n }\n return dlist;\n }\n\n}\n\nexport class Atari5200 extends Atari800 {\n newBus() {\n return {\n read: newAddressDecoder([\n [0x0000, 0x3fff, 0xffff, (a) => { return this.ram[a]; }],\n [0x4000, 0xbfff, 0xffff, (a) => { return this.rom ? this.rom[a - 0x4000] : 0; }],\n [0xc000, 0xcfff, 0x1f, (a) => { return this.gtia.readReg(a); }],\n [0xd400, 0xd4ff, 0xf, (a) => { return this.antic.readReg(a); }],\n [0xe800, 0xefff, 0xf, (a) => { return this.readPokey(a); }],\n [0xf800, 0xffff, 0x7ff, (a) => { return this.bios[a]; }],\n ]),\n write: newAddressDecoder([\n [0x0000, 0x3fff, 0xffff, (a, v) => { this.ram[a] = v; }],\n [0xc000, 0xcfff, 0x1f, (a, v) => { this.gtia.setReg(a, v); }],\n [0xd400, 0xd4ff, 0xf, (a, v) => { this.antic.setReg(a, v); }],\n [0xe800, 0xefff, 0xf, (a, v) => { this.writePokey(a, v); }],\n ]),\n };\n }\n}\n\n", "\nimport { Platform, getOpcodeMetadata_6502, getToolForFilename_6502, Base6502MachinePlatform, Preset } from \"../common/baseplatform\";\nimport { PLATFORMS } from \"../common/emu\";\nimport { BaseMAME6502Platform } from \"../common/mameplatform\";\nimport { Atari5200, Atari800 } from \"../machine/atari8\";\n\nvar Atari8_PRESETS : Preset[] = [\n {id:'hello.dasm', name:'Hello World', category:'Assembly'},\n {id:'hellopm.dasm', name:'Hello Sprites'},\n {id:'helloconio.c', name:'Text Mode', category:'C'},\n {id:'siegegame.c', name:'Siege Game'},\n {id:'hellodlist.c', name:'Display List'},\n {id:'libdemo.c', name:'Library Demo'},\n];\n\nvar Atari800_PRESETS = Atari8_PRESETS.concat([\n //{id:'testmusic.c', name:'POKEY Music'},\n {id:'scrolldemo.c', name:'Scrolling'},\n {id:'hello.cpp', name:'Hello Oscar64', category:'Oscar64'},\n {id:'libdemo.cpp', name:'Library Demo Oscar64'},\n {id:'sieve.bas', name:'Benchmark', category:'FastBasic'},\n {id:'pmtest.bas', name:'Sprites Test'},\n {id:'dli.bas', name:'DLI Test'},\n {id:'joyas.bas', name:'Match-3 Game'},\n]);\n\nconst Atari800_MemoryMap = { main:[\n {name:'RAM',start:0x0,size:0xc000,type:'ram'},\n {name:'Left Cartridge ROM',start:0xa000,size:0x2000,type:'rom'},\n {name:'GTIA',start:0xd000,size:0x20,type:'io'},\n {name:'POKEY',start:0xd200,size:0x10,type:'io'},\n {name:'PIA',start:0xd300,size:0x04,type:'io'},\n {name:'ANTIC',start:0xd400,size:0x10,type:'io'},\n {name:'Cartridge Control Line',start:0xd600,size:0x100,type:'io'},\n {name:'ROM',start:0xd800,size:0x800,type:'rom'},\n {name:'Character Set',start:0xe000,size:0x400,type:'rom'},\n {name:'ROM',start:0xe400,size:0x1c00,type:'rom'},\n] }\n\nconst Atari5200_MemoryMap = { main:[\n {name:'RAM',start:0x0,size:0x4000,type:'ram'},\n {name:'Cartridge ROM',start:0x4000,size:0x8000,type:'rom'},\n {name:'GTIA',start:0xc000,size:0x20,type:'io'},\n {name:'ANTIC',start:0xd400,size:0x10,type:'io'},\n {name:'POKEY',start:0xe800,size:0x10,type:'io'},\n {name:'ATARI Character Set',start:0xf800,size:0x400,type:'rom'},\n {name:'ROM',start:0xfc00,size:0x400,type:'rom'},\n] }\n\nfunction getToolForFilename_Atari8(fn:string) {\n if (fn.endsWith(\".bas\") || fn.endsWith(\".fb\") || fn.endsWith(\".fbi\")) return \"fastbasic\";\n else return getToolForFilename_6502(fn);\n}\n\nclass Atari800Platform extends Base6502MachinePlatform {\n newMachine() { return new Atari800(); }\n getPresets() { return Atari800_PRESETS; }\n getDefaultExtensions() { return [...super.getDefaultExtensions(), \".bas\"]; }\n getToolForFilename = getToolForFilename_Atari8;\n readAddress(a) { return this.machine.readConst(a); }\n getMemoryMap() { return Atari800_MemoryMap; }\n showHelp = atari8_showHelp;\n getROMExtension = atari8_getROMExtension;\n \n async start() {\n let bios = await this.loadKernel();\n await super.start();\n this.machine.loadBIOS(bios);\n }\n biosPath = 'res/altirra/kernel.rom';\n async loadKernel() {\n var biosResponse = await fetch(this.biosPath);\n if (biosResponse.status == 200 || (biosResponse as any as Blob).size) {\n var biosBinary = await biosResponse.arrayBuffer();\n return new Uint8Array(biosBinary);\n } else throw new Error('could not load BIOS file');\n }\n getDebugTree() {\n let tree = super.getDebugTree();\n tree['display_list'] = this.machine.getDebugDisplayList();\n return tree;\n }\n}\n\nclass Atari5200Platform extends Atari800Platform {\n getPresets() { return Atari8_PRESETS; }\n newMachine() { return new Atari5200(); }\n getMemoryMap() { return Atari5200_MemoryMap; }\n biosPath = 'res/altirra/superkernel.rom';\n}\n\n\n/// MAME support\n\nabstract class Atari8MAMEPlatform extends BaseMAME6502Platform {\n getPresets() { return Atari8_PRESETS; }\n getToolForFilename = getToolForFilename_Atari8;\n getOpcodeMetadata = getOpcodeMetadata_6502;\n getDefaultExtensions() { return [\".c\", \".bas\", \".cpp\", \".acme\", \".xa\", \".ca65\", \".dasm\", \".ecs\", \".wiz\"]; }\n showHelp = atari8_showHelp;\n}\n\nclass Atari800MAMEPlatform extends Atari8MAMEPlatform implements Platform {\n // MAME loads cartridge ROMs; oscar64's atari target emits an XEX image, so it\n // only works on the native platform.\n getPresets() { return Atari800_PRESETS.filter(p => !p.id.endsWith('.cpp')); }\n loadROM(title, data) {\n if (!this.started) {\n this.startModule(this.mainElement, {\n jsfile:'mame8bitws.js',\n biosfile:'a800xl.zip',\n cfgfile:'a800xl.cfg',\n driver:'a800xl',\n width:336*2,\n height:225*2,\n romfn:'/emulator/cart.rom',\n romdata:new Uint8Array(data),\n romsize:0x2000,\n preInit:function(_self) {\n },\n });\n } else {\n this.loadROMFile(data);\n this.loadRegion(\":cartleft:cart:rom\", data);\n }\n }\n start() {\n }\n getMemoryMap = function() { return Atari800_MemoryMap };\n}\n\nclass Atari5200MAMEPlatform extends Atari8MAMEPlatform implements Platform {\n loadROM(title, data) {\n if (!this.started) {\n this.startModule(this.mainElement, {\n jsfile:'mame8bitws.js',\n biosfile:'a5200/5200.rom',\n cfgfile:'a5200.cfg',\n driver:'a5200',\n width:336*2,\n height:225*2,\n romfn:'/emulator/cart.rom',\n romdata:new Uint8Array(data),\n romsize:0x8000,\n preInit:function(_self) {\n },\n });\n } else {\n this.loadROMFile(data);\n this.loadRegion(\":cartleft:cart:rom\", data);\n }\n }\n start() {\n }\n getMemoryMap() { return Atari5200_MemoryMap; }\n}\n\nfunction atari8_getROMExtension(rom: Uint8Array) {\n if (rom == null) return \".bin\";\n if (rom[0] == 0x96 && rom[1] == 0x02) return \".atr\";\n if (rom[0] == 0xff && rom[1] == 0xff) return \".xex\";\n else return \".rom\";\n}\n\nfunction atari8_showHelp() {\n return \"https://8bitworkshop.com/docs/platforms/atari8/\";\n}\n\n///\n\nPLATFORMS['atari8-800.xlmame'] = Atari800MAMEPlatform\nPLATFORMS['atari8-5200.mame'] = Atari5200MAMEPlatform\nPLATFORMS['atari8-800'] = Atari800Platform\nPLATFORMS['atari8-5200'] = Atari5200Platform\n"], + "mappings": "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", + "names": ["PF_LEFT", "PF_RIGHT", "DMACTL", "CHACTL", "DLISTL", "DLISTH", "HSCROL", "VSCROL", "PMBASE", "CHBASE", "WSYNC", "VCOUNT", "PENH", "PENV", "NMIEN", "NMIRES", "NMIST", "ANTIC_LEFT", "ANTIC_RIGHT", "LAST_DMA_H", "MODE_LINES", "MODE_PERIOD", "MODE_YPERIOD", "MODE_SHIFT", "ANTIC", "readfn", "nmifn", "NMIEN", "NMIST", "PENH", "PENV", "DMACTL", "safe_extend", "s", "state", "lpad", "hex", "dumpRAM", "a", "v", "WSYNC", "NMIRES", "VCOUNT", "DLISTL", "DLISTH", "effwidth", "hscroll", "HSCROL", "PF_LEFT", "PF_RIGHT", "vscrol", "VSCROL", "mask", "pc", "b", "did_dma", "op", "candma", "section", "oneline", "pmaddr", "PMBASE", "mode", "ch", "y", "addrofs", "chbase", "CHBASE", "addr", "chactl", "CHACTL", "mode3lc", "AUDF1", "AUDF2", "AUDF3", "AUDF4", "AUDCTL", "STIMER", "SKRES", "POTGO", "SEROUT", "IRQEN", "SKCTL", "ALLPOT", "KBCODE", "RANDOM", "SERIN", "IRQST", "SKSTAT", "POLY9", "CH1_179", "CH3_179", "CH1_CH2", "CH3_CH4", "CLOCK_15", "DIV_64", "DIV_15", "POLY9_SIZE", "POLY17_SIZE", "CHAN1", "CHAN2", "CHAN3", "CHAN4", "ANTIC_LINE_C", "SIO_XMTDONE_INTERVAL", "SIO_SEROUT_INTERVAL", "poly9", "poly17", "initPolyTables", "reg", "i", "POKEY", "irq", "antic_xpos", "safe_extend", "s", "SKSTAT", "DIV_64", "addr", "byte", "ALLPOT", "KBCODE", "RANDOM", "SKCTL", "AUDCTL", "POLY9", "POLY9_SIZE", "POLY17_SIZE", "ptr", "CLOCK_15", "DIV_15", "CHAN1", "CHAN2", "CHAN3", "CHAN4", "AUDF1", "CH1_CH2", "AUDF2", "AUDF3", "CH3_CH4", "AUDF4", "IRQEN", "IRQST", "SKRES", "POTGO", "SEROUT", "SIO_SEROUT_INTERVAL", "SIO_XMTDONE_INTERVAL", "STIMER", "chan_mask", "CH1_179", "ANTIC_LINE_C", "CH3_179", "SERIN", "chan", "mask", "state", "dumpRAM", "DiskImage", "data", "total", "sectorsize", "sector", "len", "idx", "ofs", "src", "SIO_STUB_OFFSET", "SIO_HOOK_OFFSET", "SIO_STATUS_OFFSET", "SIO_STUB", "ATARI8_KEYMATRIX_INTL_NOSHIFT", "Keys", "ATARI8_KEYCODE_MAP", "makeKeycodeMap", "Atari800", "BasicScanlineMachine", "MOS6502", "ANTIC", "GTIA", "POKEY", "newPOKEYAudio", "TssChannelAdapter", "newKeyboardHandler", "newAddressDecoder", "a", "v", "bios", "i", "TRIG0", "CONSOL", "y", "xofs", "gtiatick1", "gtiatick2", "bp", "MODE_SHIFT", "odd", "state", "category", "o", "key", "code", "flags", "keymap", "rom", "title", "DiskImage", "rom2", "addr", "value", "dunit", "dcomnd", "dbuf", "dbyt", "sector", "disk", "status", "dvstat", "n", "bytes", "buf", "ofs", "stub", "stubofs", "runaddr", "start", "end", "hex", "initaddr", "controller", "pc", "nextInsn", "b", "dlist", "pc0", "op", "mode", "debugmsg", "jmp", "lines", "MODE_LINES", "lms", "dlarg_lo", "dlarg_hi", "Atari5200", "Atari8_PRESETS", "Atari800_PRESETS", "Atari800_MemoryMap", "Atari5200_MemoryMap", "getToolForFilename_Atari8", "fn", "getToolForFilename_6502", "Atari800Platform", "Base6502MachinePlatform", "atari8_showHelp", "atari8_getROMExtension", "Atari800", "a", "bios", "biosResponse", "biosBinary", "tree", "Atari5200Platform", "Atari5200", "Atari8MAMEPlatform", "BaseMAME6502Platform", "getOpcodeMetadata_6502", "Atari800MAMEPlatform", "p", "title", "data", "_self", "Atari5200MAMEPlatform", "rom", "PLATFORMS"] } diff --git a/gen/basic-NCWX6JLL.js b/gen/basic-FXG6Z7YU.js similarity index 99% rename from gen/basic-NCWX6JLL.js rename to gen/basic-FXG6Z7YU.js index 1d07286e..0178ca20 100644 --- a/gen/basic-NCWX6JLL.js +++ b/gen/basic-FXG6Z7YU.js @@ -1,4 +1,4 @@ -import{b as S}from"./chunk-3FIVEUG2.js";import{y as E}from"./chunk-5YJJ4DRA.js";import"./chunk-VLOGW7AJ.js";import"./chunk-T6VURU5O.js";import"./chunk-A7SWO6EA.js";import"./chunk-4R3KPJX7.js";import"./chunk-2XSLQJUY.js";import{B as c,K as f,T as d,U as v,a as g}from"./chunk-TYDI7JZQ.js";import"./chunk-VSC4MAII.js";function k(u){return u.value!=null}function y(u){return u.name!=null}function O(u){return u.op!=null&&u.left!=null&&u.right!=null}function N(u){return u.op!=null&&u.expr!=null}var m=class{randomize(){this.seed(Math.random()*2147483647,Math.random()*2147483647,Math.random()*2147483647,Math.random()*2147483647)}constructor(){(()=>{var r,e,i,n;this.seed=function(s,a,h,o){r=s,e=a,i=h,n=o},this.seedfloat=function(s){this.seed(s,s*4294,s*429496,s*4294967296),this.next(),this.next(),this.next()},this.next=function(){r>>>=0,e>>>=0,i>>>=0,n>>>=0;var s=r+e|0;return r=e^e>>>9,e=i+(i<<3)|0,i=i<<21|i>>>11,n=n+1|0,s=s+n|0,i=i+s|0,(s>>>0)/4294967296}})(),this.seedfloat(-1)}},I=1024*1024,l=class u{constructor(){this.margin=80;this.running=!1;this.exited=!0;this.trace=!1}load(t){let r=null,e=0;if(this.pc2label!=null){let s=this.curpc;for(;s>0&&(r=this.pc2label.get(s))==null;)s--;e=this.curpc-s,console.log("oldpc=",this.curpc,"restart @ label",r,"+",e)}this.program=t,this.opts=t.opts,this.opts.maxArrayElements||(this.opts.maxArrayElements=I),this.allstmts=t.stmts,this.label2pc=t.labels,this.label2dataptr={},this.pc2label=new Map,this.datums=[],this.subroutines={},this.builtins=this.getBuiltinFunctions();for(var i in t.labels){var n=t.labels[i];this.pc2label.set(n,i)}return this.allstmts.forEach((s,a)=>{this.curpc=a+1,this.compileStatement(s),s.command=="DATA"&&(this.label2dataptr[s.$loc.label]=this.datums.length,s.datums.forEach(h=>{this.curpc=s.$loc.offset,this.datums.push(h)}))}),this.label2pc[r]!=null?(this.curpc=this.label2pc[r]+e,!0):(this.curpc=0,!1)}reset(){this.curpc=0,this.dataptr=0,this.clearVars(),this.returnStack=[],this.column=0,this.running=!0,this.exited=!1}clearVars(){this.globals=this.vars={},this.arrays={},this.defs={},this.forLoops={},this.forLoopStack=[],this.whileLoops=[],this.rng=new m,this.opts&&this.opts.staticArrays&&this.allstmts.filter(t=>t.command=="DIM").forEach(t=>{t.args.forEach(r=>this.compileJS(this._DIM(r))())})}saveState(){return $.extend(!0,{},this)}loadState(t){$.extend(!0,this,t)}getBuiltinFunctions(){var t=this.program&&this.opts.validFunctions;t||(t=Object.getOwnPropertyNames(u.prototype).filter(i=>/^[A-Z]{3,}[$]?$/.test(i)));var r={};for(var e of t)this.supportsFunction(e)&&(r[e]=this[e].bind(this));return r}supportsFunction(t){return typeof this[t]=="function"}runtimeError(t){throw this.curpc--,new d(t,this.getCurrentSourceLocation())}dialectError(t){this.runtimeError(`I can't ${t} in this dialect of BASIC.`)}getLineForPC(t){var r=this.allstmts[t];return r&&r.$loc&&r.$loc.line}getLabelForPC(t){var r=this.allstmts[t];return r&&r.$loc&&r.$loc.label}getCurrentSourceLocation(){var t=this.getStatement();return t&&t.$loc}getCurrentLabel(){var t=this.getCurrentSourceLocation();return t&&t.label}getStatement(){return this.allstmts[this.curpc]}step(){if(!this.running)return!1;var t=this.getStatement();return t?(this.trace&&console.log(this.curpc,t,this.vars,Object.keys(this.arrays)),this.curpc++,this.executeStatement(t),this.running):(this.running=!1,this.exited=!0,!1)}compileStatement(t){if(t.$run==null)try{var r=this["do__"+t.command];r==null&&this.runtimeError(`I don't know how to "${t.command}".`);var e=r.bind(this)(t);this.trace&&console.log(e),t.$run=this.compileJS(e)}catch(i){throw e&&console.log(e),i}}compileJS(t){return new Function(t).bind(this)}executeStatement(t){this.compileStatement(t),t.$run()}skipToEOL(){do this.curpc++;while(this.curpc=0?this.curpc=r:this.runtimeError(`I tried to go to the label "${t}" but couldn't find it.`)}newLocalScope(){this.vars=Object.create(this.vars)}popLocalScope(){this.vars!==this.globals&&(this.vars=Object.getPrototypeOf(this.vars))}gosubLabel(t){this.returnStack.length>32767&&this.runtimeError("I did too many GOSUBs without a RETURN."),this.returnStack.push(this.curpc),this.gotoLabel(t)}returnFromGosub(){this.returnStack.length==0&&this.runtimeError("I tried to RETURN, but there wasn't a corresponding GOSUB.");var t=this.returnStack.pop();this.curpc=t,this.popLocalScope()}popReturnStack(){this.returnStack.length==0&&this.runtimeError("I tried to POP, but there wasn't a corresponding GOSUB."),this.returnStack.pop()}valueToString(t,r){var e;if(typeof t=="number"){var i=this.float2str(t,this.opts.printZoneLength-4);return r?i.startsWith("-")?`${i} `:` ${i} `:i}else if(t==` +import{b as S}from"./chunk-3FIVEUG2.js";import{y as E}from"./chunk-XRWSPMRR.js";import"./chunk-VLOGW7AJ.js";import"./chunk-T6VURU5O.js";import"./chunk-A7SWO6EA.js";import"./chunk-C7SFJC4F.js";import"./chunk-2XSLQJUY.js";import{B as c,K as f,T as d,U as v,a as g}from"./chunk-TYDI7JZQ.js";import"./chunk-VSC4MAII.js";function k(u){return u.value!=null}function y(u){return u.name!=null}function O(u){return u.op!=null&&u.left!=null&&u.right!=null}function N(u){return u.op!=null&&u.expr!=null}var m=class{randomize(){this.seed(Math.random()*2147483647,Math.random()*2147483647,Math.random()*2147483647,Math.random()*2147483647)}constructor(){(()=>{var r,e,i,n;this.seed=function(s,a,h,o){r=s,e=a,i=h,n=o},this.seedfloat=function(s){this.seed(s,s*4294,s*429496,s*4294967296),this.next(),this.next(),this.next()},this.next=function(){r>>>=0,e>>>=0,i>>>=0,n>>>=0;var s=r+e|0;return r=e^e>>>9,e=i+(i<<3)|0,i=i<<21|i>>>11,n=n+1|0,s=s+n|0,i=i+s|0,(s>>>0)/4294967296}})(),this.seedfloat(-1)}},I=1024*1024,l=class u{constructor(){this.margin=80;this.running=!1;this.exited=!0;this.trace=!1}load(t){let r=null,e=0;if(this.pc2label!=null){let s=this.curpc;for(;s>0&&(r=this.pc2label.get(s))==null;)s--;e=this.curpc-s,console.log("oldpc=",this.curpc,"restart @ label",r,"+",e)}this.program=t,this.opts=t.opts,this.opts.maxArrayElements||(this.opts.maxArrayElements=I),this.allstmts=t.stmts,this.label2pc=t.labels,this.label2dataptr={},this.pc2label=new Map,this.datums=[],this.subroutines={},this.builtins=this.getBuiltinFunctions();for(var i in t.labels){var n=t.labels[i];this.pc2label.set(n,i)}return this.allstmts.forEach((s,a)=>{this.curpc=a+1,this.compileStatement(s),s.command=="DATA"&&(this.label2dataptr[s.$loc.label]=this.datums.length,s.datums.forEach(h=>{this.curpc=s.$loc.offset,this.datums.push(h)}))}),this.label2pc[r]!=null?(this.curpc=this.label2pc[r]+e,!0):(this.curpc=0,!1)}reset(){this.curpc=0,this.dataptr=0,this.clearVars(),this.returnStack=[],this.column=0,this.running=!0,this.exited=!1}clearVars(){this.globals=this.vars={},this.arrays={},this.defs={},this.forLoops={},this.forLoopStack=[],this.whileLoops=[],this.rng=new m,this.opts&&this.opts.staticArrays&&this.allstmts.filter(t=>t.command=="DIM").forEach(t=>{t.args.forEach(r=>this.compileJS(this._DIM(r))())})}saveState(){return $.extend(!0,{},this)}loadState(t){$.extend(!0,this,t)}getBuiltinFunctions(){var t=this.program&&this.opts.validFunctions;t||(t=Object.getOwnPropertyNames(u.prototype).filter(i=>/^[A-Z]{3,}[$]?$/.test(i)));var r={};for(var e of t)this.supportsFunction(e)&&(r[e]=this[e].bind(this));return r}supportsFunction(t){return typeof this[t]=="function"}runtimeError(t){throw this.curpc--,new d(t,this.getCurrentSourceLocation())}dialectError(t){this.runtimeError(`I can't ${t} in this dialect of BASIC.`)}getLineForPC(t){var r=this.allstmts[t];return r&&r.$loc&&r.$loc.line}getLabelForPC(t){var r=this.allstmts[t];return r&&r.$loc&&r.$loc.label}getCurrentSourceLocation(){var t=this.getStatement();return t&&t.$loc}getCurrentLabel(){var t=this.getCurrentSourceLocation();return t&&t.label}getStatement(){return this.allstmts[this.curpc]}step(){if(!this.running)return!1;var t=this.getStatement();return t?(this.trace&&console.log(this.curpc,t,this.vars,Object.keys(this.arrays)),this.curpc++,this.executeStatement(t),this.running):(this.running=!1,this.exited=!0,!1)}compileStatement(t){if(t.$run==null)try{var r=this["do__"+t.command];r==null&&this.runtimeError(`I don't know how to "${t.command}".`);var e=r.bind(this)(t);this.trace&&console.log(e),t.$run=this.compileJS(e)}catch(i){throw e&&console.log(e),i}}compileJS(t){return new Function(t).bind(this)}executeStatement(t){this.compileStatement(t),t.$run()}skipToEOL(){do this.curpc++;while(this.curpc=0?this.curpc=r:this.runtimeError(`I tried to go to the label "${t}" but couldn't find it.`)}newLocalScope(){this.vars=Object.create(this.vars)}popLocalScope(){this.vars!==this.globals&&(this.vars=Object.getPrototypeOf(this.vars))}gosubLabel(t){this.returnStack.length>32767&&this.runtimeError("I did too many GOSUBs without a RETURN."),this.returnStack.push(this.curpc),this.gotoLabel(t)}returnFromGosub(){this.returnStack.length==0&&this.runtimeError("I tried to RETURN, but there wasn't a corresponding GOSUB.");var t=this.returnStack.pop();this.curpc=t,this.popLocalScope()}popReturnStack(){this.returnStack.length==0&&this.runtimeError("I tried to POP, but there wasn't a corresponding GOSUB."),this.returnStack.pop()}valueToString(t,r){var e;if(typeof t=="number"){var i=this.float2str(t,this.opts.printZoneLength-4);return r?i.startsWith("-")?`${i} `:` ${i} `:i}else if(t==` `)this.column=0,e=t;else if(t==" "){var n=this.opts.printZoneLength,s=Math.floor(this.column/n),a=(s+1)*this.opts.printZoneLength;a+n>this.margin?(this.column=0,e=` `):e=this.TAB(a)}else e=`${t}`;return e}float2str(t,r){var e=t.toString().toUpperCase();if(r>0){for(var i=r;e.length>r;)e=t.toPrecision(i--);e.startsWith("0.")?e=e.substr(1):e.startsWith("-0.")&&(e="-"+e.substr(2))}return e}printExpr(t){var r=this.valueToString(t,this.opts.numericPadding);this.column+=r.length,this.print(r)}print(t){console.log(t)}async input(t,r){return{line:"",vals:[]}}resume(){}expr2js(t,r){if(r||(r={}),k(t))return JSON.stringify(t.value);if(y(t)){if(!t.args&&r.locals&&r.locals.indexOf(t.name)>=0)return t.name;{r.isconst&&this.runtimeError("I expected a constant value here.");var e="",i=JSON.stringify(t.name);let h=t.args?t.args.map(o=>this.expr2js(o,r)).join(", "):[];return t.name.startsWith("FN")?e+=`this.getDef(${i})(${h})`:this.builtins[t.name]?(this.checkFuncArgs(t,this.builtins[t.name]),e+=`this.builtins.${t.name}(${h})`):t.args?this.opts.arraysContainChars&&t.name.endsWith("$")?e+=`this.getStringSlice(this.vars.${t.name}, ${h})`:e+=`this.arrayGet(${i}, ${h})`:e+=`this.vars.${t.name}`,r.novalid?e:`this.checkValue(${e}, ${i})`}}else if(O(t)){var n=this.expr2js(t.left,r),s=this.expr2js(t.right,r);return`this.${t.op}(${n}, ${s})`}else if(N(t)){var a=this.expr2js(t.expr,r);return`this.${t.op}(${a})`}}assign2js(t,r){r||(r={});var e="";return(t.name.startsWith("FN")||this.builtins[t.name])&&this.runtimeError("I can't call a function here."),t.args?this.opts.arraysContainChars&&t.name.endsWith("$")?this.runtimeError("I can't set array slices via this command yet."):e+=this.array2js(t,r):e=`this.globals.${t.name}`,e}array2js(t,r){var e=JSON.stringify(t.name),i=t.args||[];return this.expr2js(t,{novalid:!0})+`;this.getArray(${e}, ${i.length})`+i.map(n=>"[this.ROUND("+this.expr2js(n,r)+")]").join("")}checkFuncArgs(t,r){var e=t.args?t.args.length:0;t.name=="RND"&&e==0||t.name=="MID$"&&e==2||t.name=="INSTR"&&e==2||r.length!=e&&this.runtimeError(`I expected ${r.length} arguments for the ${t.name} function, but I got ${e}.`)}startForLoop(t,r,e,i,n){var s=this.curpc-1,a=this.pc2label.get(s);i||(i=1),this.vars[t]=r,this.trace&&console.log(`FOR ${t} = ${r} TO ${e} STEP ${i}`);var h=()=>i>=0?this.vars[t]>e:this.vars[t]o==t)),this.forLoopStack.push(t),this.forLoops[t]={$next:o=>{o&&t!=o&&this.runtimeError(`I executed NEXT "${o}", but the last FOR was for "${t}".`),this.vars[t]+=i;var b=h();b?(this.forLoopStack.pop(),delete this.forLoops[t]):this.curpc=(a!=null&&this.label2pc[a]||s)+1,this.trace&&console.log(`NEXT ${t}: ${this.vars[t]} TO ${e} STEP ${i} DONE=${b}`)}}}nextForLoop(t){var r=this.forLoops[t||this.opts.optionalNextVar&&this.forLoopStack[this.forLoopStack.length-1]];r||this.runtimeError("I couldn't find a matching FOR for this NEXT."),r.$next(t)}whileLoop(t){t?this.whileLoops.push(this.curpc-1):this.skipToAfterWend()}nextWhileLoop(){var t=this.whileLoops.pop();t==null?this.runtimeError("I couldn't find a matching WHILE for this WEND."):this.curpc=t}assign(t,r,e){return e&&t.endsWith("$")?this.checkValue(this.convert(t,r),t):t.endsWith("$")?this.convertToString(r,t):this.convertToNumber(r,t)}convert(t,r){return t.endsWith("$")?r==null?"":r.toString():typeof r=="number"?r:parseFloat(r+"")}convertToString(t,r){if(typeof t!="string")this.runtimeError(`I can't convert ${t} to a string.`);else return t}convertToNumber(t,r){if(typeof t!="number")this.runtimeError(`I can't convert ${t} to a number.`);else return this.checkNum(t)}dimArray(t,...r){if(r=r.map(Math.round),this.arrays[t]!=null){if(this.opts.staticArrays)return;this.runtimeError(`I already dimensioned this array (${t}) earlier.`)}var e=this.getTotalArrayLength(r);e>this.opts.maxArrayElements&&this.runtimeError("I can't create an array with this many elements.");var i=t.endsWith("$"),n=i?Array:Float64Array;if(r.length==1)this.arrays[t]=new n(r[0]+1);else if(r.length==2){this.arrays[t]=new Array(r[0]+1);for(var s=0;s=i.length&&this.runtimeError(`I tried to lookup ${t}(${r}) but it exceeded the dimensions of the array.`),i=i[r[n]]}return c(i)&&this.runtimeError(`I tried to lookup ${t}(${r}) but used too few dimensions.`),i}modifyStringSlice(t,r,e,i){return t=t||"",this.checkString(t),this.checkString(r),i||(i=e),e=this.ROUND(e),i=this.ROUND(i),e<1&&this.dialectError("accept a string slice index less than 1"),ir.length)&&this.runtimeError(`I needed a number between 1 and ${r.length}, but I got ${t}.`),t<1||t>r.length?0:t}onGotoLabel(t,...r){t=this.checkOnGoto(t,r),t&&this.gotoLabel(r[t-1])}onGosubLabel(t,...r){t=this.checkOnGoto(t,r),t&&this.gosubLabel(r[t-1])}nextDatum(){return this.dataptr>=this.datums.length&&this.runtimeError("I tried to READ, but ran out of data."),this.datums[this.dataptr++].value}do__PRINT(t){var r="";for(var e of t.args){var i=this.expr2js(e),n=i.name;r+=`this.printExpr(this.checkValue(${i}, ${JSON.stringify(n)}));`}return r}preInput(){this.running=!1,this.curpc--}postInput(t){t&&this.curpc++,this.running=!0,this.resume()}do__INPUT(t){var r=t.prompt!=null?this.expr2js(t.prompt):'""',e=t.elapsed!=null?this.assign2js(t.elapsed):"let ___",i="";return t.args.forEach((n,s)=>{var a=this.assign2js(n);i+=` var value = this.convert(${JSON.stringify(n.name)}, response.vals[${s}]); @@ -34,4 +34,4 @@ import{b as S}from"./chunk-3FIVEUG2.js";import{y as E}from"./chunk-5YJJ4DRA.js"; `),this.tty.addtext("*** END OF PROGRAM ***",1),this.tty.showPrintHead(!1)}loadROM(t,r){var e=this.runtime.exited;this.program=r;var i=this.runtime.load(r);this.tty.uppercaseOnly=!0,(!this.hotReload||e||!i)&&this.reset()}getROMExtension(){return".json"}reset(){this.tty.clear(),this.runtime.reset(),this.clock=0,this.transcript=[]}pause(){this.timer.stop()}resume(){this.isBlocked()||(this.animcount=0,this.timer.start())}isBlocked(){return this.tty.waitingfor!=null||this.runtime.exited}isRunning(){return this.timer.isRunning()}getDefaultExtensions(){return[".bas"]}getToolForFilename(){return"basic"}getPresets(){return L}getPC(){return this.runtime.curpc}getSP(){return 4096-this.runtime.returnStack.length}isStable(){return!0}getCPUState(){return{PC:this.getPC(),SP:this.getSP()}}saveState(){return{c:this.getCPUState(),rt:this.runtime.saveState()}}loadState(t){this.runtime.loadState(t)}getDebugTree(){return{CurrentLine:this.runtime.getCurrentLabel(),Variables:this.runtime.vars,Arrays:this.runtime.arrays,Functions:this.runtime.defs,ForLoops:this.runtime.forLoops,WhileLoops:this.runtime.whileLoops,ReturnStack:this.runtime.returnStack,NextDatum:this.runtime.datums[this.runtime.dataptr],Clock:this.clock,Options:this.runtime.opts,Internals:this.runtime}}inspect(t){let r=this.runtime.vars[t];if(r!=null)return`${t} = ${r}`}showHelp(){return"https://8bitworkshop.com/docs/platforms/basic/"}getDebugCategories(){return["Variables"]}getDebugInfo(t,r){if(t==="Variables")return this.varsToLongString()}varsToLongString(){var t="",r=Object.keys(this.runtime.vars);r.sort();for(var e of r){var i=this.runtime.vars[e],n=JSON.stringify(i);n.length>24&&(n=`${n.substr(0,24)}...(${n.length})`),t+=g(e,3)+" = "+n+` `}return t}setupDebug(t){this.onBreakpointHit=t}clearDebug(){this.onBreakpointHit=null,this.debugTrap=null}checkDebugTrap(){return this.debugTrap&&this.debugTrap()?(this.pause(),this.break(),!0):!1}break(){this.onBreakpointHit&&this.onBreakpointHit(this.saveState())}step(){var t=this.clock;this.debugTrap=()=>this.clock>t,this.resume()}stepOver(){var t=this.runtime.getStatement();if(t&&(t.command=="GOSUB"||t.command=="ONGOSUB")){var r=this.getPC()+1;this.runEval(()=>this.getPC()==r)}else this.step()}hasCustomStepOver(){return!0}runUntilReturn(){var t=this.getSP();this.runEval(()=>this.getSP()>t)}runEval(t){this.debugTrap=()=>t(this.getCPUState()),this.resume()}restartAtPC(t){return t=Math.round(t),t>=0&&t element; its readystatechange event will be fired asynchronously once it is inserted\n // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.\n var scriptEl = global.document.createElement('script');\n scriptEl.onreadystatechange = function () {\n nextTick();\n\n scriptEl.onreadystatechange = null;\n scriptEl.parentNode.removeChild(scriptEl);\n scriptEl = null;\n };\n global.document.documentElement.appendChild(scriptEl);\n };\n } else {\n scheduleDrain = function () {\n setTimeout(nextTick, 0);\n };\n }\n}\n\nvar draining;\nvar queue = [];\n//named nextTick for less confusing stack traces\nfunction nextTick() {\n draining = true;\n var i, oldQueue;\n var len = queue.length;\n while (len) {\n oldQueue = queue;\n queue = [];\n i = -1;\n while (++i < len) {\n oldQueue[i]();\n }\n len = queue.length;\n }\n draining = false;\n}\n\nmodule.exports = immediate;\nfunction immediate(task) {\n if (queue.push(task) === 1 && !draining) {\n scheduleDrain();\n }\n}\n\n}).call(this,typeof global !== \"undefined\" ? global : typeof self !== \"undefined\" ? self : typeof window !== \"undefined\" ? window : {})\n},{}],2:[function(_dereq_,module,exports){\n'use strict';\nvar immediate = _dereq_(1);\n\n/* istanbul ignore next */\nfunction INTERNAL() {}\n\nvar handlers = {};\n\nvar REJECTED = ['REJECTED'];\nvar FULFILLED = ['FULFILLED'];\nvar PENDING = ['PENDING'];\n\nmodule.exports = Promise;\n\nfunction Promise(resolver) {\n if (typeof resolver !== 'function') {\n throw new TypeError('resolver must be a function');\n }\n this.state = PENDING;\n this.queue = [];\n this.outcome = void 0;\n if (resolver !== INTERNAL) {\n safelyResolveThenable(this, resolver);\n }\n}\n\nPromise.prototype[\"catch\"] = function (onRejected) {\n return this.then(null, onRejected);\n};\nPromise.prototype.then = function (onFulfilled, onRejected) {\n if (typeof onFulfilled !== 'function' && this.state === FULFILLED ||\n typeof onRejected !== 'function' && this.state === REJECTED) {\n return this;\n }\n var promise = new this.constructor(INTERNAL);\n if (this.state !== PENDING) {\n var resolver = this.state === FULFILLED ? onFulfilled : onRejected;\n unwrap(promise, resolver, this.outcome);\n } else {\n this.queue.push(new QueueItem(promise, onFulfilled, onRejected));\n }\n\n return promise;\n};\nfunction QueueItem(promise, onFulfilled, onRejected) {\n this.promise = promise;\n if (typeof onFulfilled === 'function') {\n this.onFulfilled = onFulfilled;\n this.callFulfilled = this.otherCallFulfilled;\n }\n if (typeof onRejected === 'function') {\n this.onRejected = onRejected;\n this.callRejected = this.otherCallRejected;\n }\n}\nQueueItem.prototype.callFulfilled = function (value) {\n handlers.resolve(this.promise, value);\n};\nQueueItem.prototype.otherCallFulfilled = function (value) {\n unwrap(this.promise, this.onFulfilled, value);\n};\nQueueItem.prototype.callRejected = function (value) {\n handlers.reject(this.promise, value);\n};\nQueueItem.prototype.otherCallRejected = function (value) {\n unwrap(this.promise, this.onRejected, value);\n};\n\nfunction unwrap(promise, func, value) {\n immediate(function () {\n var returnValue;\n try {\n returnValue = func(value);\n } catch (e) {\n return handlers.reject(promise, e);\n }\n if (returnValue === promise) {\n handlers.reject(promise, new TypeError('Cannot resolve promise with itself'));\n } else {\n handlers.resolve(promise, returnValue);\n }\n });\n}\n\nhandlers.resolve = function (self, value) {\n var result = tryCatch(getThen, value);\n if (result.status === 'error') {\n return handlers.reject(self, result.value);\n }\n var thenable = result.value;\n\n if (thenable) {\n safelyResolveThenable(self, thenable);\n } else {\n self.state = FULFILLED;\n self.outcome = value;\n var i = -1;\n var len = self.queue.length;\n while (++i < len) {\n self.queue[i].callFulfilled(value);\n }\n }\n return self;\n};\nhandlers.reject = function (self, error) {\n self.state = REJECTED;\n self.outcome = error;\n var i = -1;\n var len = self.queue.length;\n while (++i < len) {\n self.queue[i].callRejected(error);\n }\n return self;\n};\n\nfunction getThen(obj) {\n // Make sure we only access the accessor once as required by the spec\n var then = obj && obj.then;\n if (obj && (typeof obj === 'object' || typeof obj === 'function') && typeof then === 'function') {\n return function appyThen() {\n then.apply(obj, arguments);\n };\n }\n}\n\nfunction safelyResolveThenable(self, thenable) {\n // Either fulfill, reject or reject with error\n var called = false;\n function onError(value) {\n if (called) {\n return;\n }\n called = true;\n handlers.reject(self, value);\n }\n\n function onSuccess(value) {\n if (called) {\n return;\n }\n called = true;\n handlers.resolve(self, value);\n }\n\n function tryToUnwrap() {\n thenable(onSuccess, onError);\n }\n\n var result = tryCatch(tryToUnwrap);\n if (result.status === 'error') {\n onError(result.value);\n }\n}\n\nfunction tryCatch(func, value) {\n var out = {};\n try {\n out.value = func(value);\n out.status = 'success';\n } catch (e) {\n out.status = 'error';\n out.value = e;\n }\n return out;\n}\n\nPromise.resolve = resolve;\nfunction resolve(value) {\n if (value instanceof this) {\n return value;\n }\n return handlers.resolve(new this(INTERNAL), value);\n}\n\nPromise.reject = reject;\nfunction reject(reason) {\n var promise = new this(INTERNAL);\n return handlers.reject(promise, reason);\n}\n\nPromise.all = all;\nfunction all(iterable) {\n var self = this;\n if (Object.prototype.toString.call(iterable) !== '[object Array]') {\n return this.reject(new TypeError('must be an array'));\n }\n\n var len = iterable.length;\n var called = false;\n if (!len) {\n return this.resolve([]);\n }\n\n var values = new Array(len);\n var resolved = 0;\n var i = -1;\n var promise = new this(INTERNAL);\n\n while (++i < len) {\n allResolver(iterable[i], i);\n }\n return promise;\n function allResolver(value, i) {\n self.resolve(value).then(resolveFromAll, function (error) {\n if (!called) {\n called = true;\n handlers.reject(promise, error);\n }\n });\n function resolveFromAll(outValue) {\n values[i] = outValue;\n if (++resolved === len && !called) {\n called = true;\n handlers.resolve(promise, values);\n }\n }\n }\n}\n\nPromise.race = race;\nfunction race(iterable) {\n var self = this;\n if (Object.prototype.toString.call(iterable) !== '[object Array]') {\n return this.reject(new TypeError('must be an array'));\n }\n\n var len = iterable.length;\n var called = false;\n if (!len) {\n return this.resolve([]);\n }\n\n var i = -1;\n var promise = new this(INTERNAL);\n\n while (++i < len) {\n resolver(iterable[i]);\n }\n return promise;\n function resolver(value) {\n self.resolve(value).then(function (response) {\n if (!called) {\n called = true;\n handlers.resolve(promise, response);\n }\n }, function (error) {\n if (!called) {\n called = true;\n handlers.reject(promise, error);\n }\n });\n }\n}\n\n},{\"1\":1}],3:[function(_dereq_,module,exports){\n(function (global){\n'use strict';\nif (typeof global.Promise !== 'function') {\n global.Promise = _dereq_(2);\n}\n\n}).call(this,typeof global !== \"undefined\" ? global : typeof self !== \"undefined\" ? self : typeof window !== \"undefined\" ? window : {})\n},{\"2\":2}],4:[function(_dereq_,module,exports){\n'use strict';\n\nvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction getIDB() {\n /* global indexedDB,webkitIndexedDB,mozIndexedDB,OIndexedDB,msIndexedDB */\n try {\n if (typeof indexedDB !== 'undefined') {\n return indexedDB;\n }\n if (typeof webkitIndexedDB !== 'undefined') {\n return webkitIndexedDB;\n }\n if (typeof mozIndexedDB !== 'undefined') {\n return mozIndexedDB;\n }\n if (typeof OIndexedDB !== 'undefined') {\n return OIndexedDB;\n }\n if (typeof msIndexedDB !== 'undefined') {\n return msIndexedDB;\n }\n } catch (e) {\n return;\n }\n}\n\nvar idb = getIDB();\n\nfunction isIndexedDBValid() {\n try {\n // Initialize IndexedDB; fall back to vendor-prefixed versions\n // if needed.\n if (!idb || !idb.open) {\n return false;\n }\n // We mimic PouchDB here;\n //\n // We test for openDatabase because IE Mobile identifies itself\n // as Safari. Oh the lulz...\n var isSafari = typeof openDatabase !== 'undefined' && /(Safari|iPhone|iPad|iPod)/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent) && !/BlackBerry/.test(navigator.platform);\n\n var hasFetch = typeof fetch === 'function' && fetch.toString().indexOf('[native code') !== -1;\n\n // Safari <10.1 does not meet our requirements for IDB support\n // (see: https://github.com/pouchdb/pouchdb/issues/5572).\n // Safari 10.1 shipped with fetch, we can use that to detect it.\n // Note: this creates issues with `window.fetch` polyfills and\n // overrides; see:\n // https://github.com/localForage/localForage/issues/856\n return (!isSafari || hasFetch) && typeof indexedDB !== 'undefined' &&\n // some outdated implementations of IDB that appear on Samsung\n // and HTC Android devices <4.4 are missing IDBKeyRange\n // See: https://github.com/mozilla/localForage/issues/128\n // See: https://github.com/mozilla/localForage/issues/272\n typeof IDBKeyRange !== 'undefined';\n } catch (e) {\n return false;\n }\n}\n\n// Abstracts constructing a Blob object, so it also works in older\n// browsers that don't support the native Blob constructor. (i.e.\n// old QtWebKit versions, at least).\n// Abstracts constructing a Blob object, so it also works in older\n// browsers that don't support the native Blob constructor. (i.e.\n// old QtWebKit versions, at least).\nfunction createBlob(parts, properties) {\n /* global BlobBuilder,MSBlobBuilder,MozBlobBuilder,WebKitBlobBuilder */\n parts = parts || [];\n properties = properties || {};\n try {\n return new Blob(parts, properties);\n } catch (e) {\n if (e.name !== 'TypeError') {\n throw e;\n }\n var Builder = typeof BlobBuilder !== 'undefined' ? BlobBuilder : typeof MSBlobBuilder !== 'undefined' ? MSBlobBuilder : typeof MozBlobBuilder !== 'undefined' ? MozBlobBuilder : WebKitBlobBuilder;\n var builder = new Builder();\n for (var i = 0; i < parts.length; i += 1) {\n builder.append(parts[i]);\n }\n return builder.getBlob(properties.type);\n }\n}\n\n// This is CommonJS because lie is an external dependency, so Rollup\n// can just ignore it.\nif (typeof Promise === 'undefined') {\n // In the \"nopromises\" build this will just throw if you don't have\n // a global promise object, but it would throw anyway later.\n _dereq_(3);\n}\nvar Promise$1 = Promise;\n\nfunction executeCallback(promise, callback) {\n if (callback) {\n promise.then(function (result) {\n callback(null, result);\n }, function (error) {\n callback(error);\n });\n }\n}\n\nfunction executeTwoCallbacks(promise, callback, errorCallback) {\n if (typeof callback === 'function') {\n promise.then(callback);\n }\n\n if (typeof errorCallback === 'function') {\n promise[\"catch\"](errorCallback);\n }\n}\n\nfunction normalizeKey(key) {\n // Cast the key to a string, as that's all we can set as a key.\n if (typeof key !== 'string') {\n console.warn(key + ' used as a key, but it is not a string.');\n key = String(key);\n }\n\n return key;\n}\n\nfunction getCallback() {\n if (arguments.length && typeof arguments[arguments.length - 1] === 'function') {\n return arguments[arguments.length - 1];\n }\n}\n\n// Some code originally from async_storage.js in\n// [Gaia](https://github.com/mozilla-b2g/gaia).\n\nvar DETECT_BLOB_SUPPORT_STORE = 'local-forage-detect-blob-support';\nvar supportsBlobs = void 0;\nvar dbContexts = {};\nvar toString = Object.prototype.toString;\n\n// Transaction Modes\nvar READ_ONLY = 'readonly';\nvar READ_WRITE = 'readwrite';\n\n// Transform a binary string to an array buffer, because otherwise\n// weird stuff happens when you try to work with the binary string directly.\n// It is known.\n// From http://stackoverflow.com/questions/14967647/ (continues on next line)\n// encode-decode-image-with-base64-breaks-image (2013-04-21)\nfunction _binStringToArrayBuffer(bin) {\n var length = bin.length;\n var buf = new ArrayBuffer(length);\n var arr = new Uint8Array(buf);\n for (var i = 0; i < length; i++) {\n arr[i] = bin.charCodeAt(i);\n }\n return buf;\n}\n\n//\n// Blobs are not supported in all versions of IndexedDB, notably\n// Chrome <37 and Android <5. In those versions, storing a blob will throw.\n//\n// Various other blob bugs exist in Chrome v37-42 (inclusive).\n// Detecting them is expensive and confusing to users, and Chrome 37-42\n// is at very low usage worldwide, so we do a hacky userAgent check instead.\n//\n// content-type bug: https://code.google.com/p/chromium/issues/detail?id=408120\n// 404 bug: https://code.google.com/p/chromium/issues/detail?id=447916\n// FileReader bug: https://code.google.com/p/chromium/issues/detail?id=447836\n//\n// Code borrowed from PouchDB. See:\n// https://github.com/pouchdb/pouchdb/blob/master/packages/node_modules/pouchdb-adapter-idb/src/blobSupport.js\n//\nfunction _checkBlobSupportWithoutCaching(idb) {\n return new Promise$1(function (resolve) {\n var txn = idb.transaction(DETECT_BLOB_SUPPORT_STORE, READ_WRITE);\n var blob = createBlob(['']);\n txn.objectStore(DETECT_BLOB_SUPPORT_STORE).put(blob, 'key');\n\n txn.onabort = function (e) {\n // If the transaction aborts now its due to not being able to\n // write to the database, likely due to the disk being full\n e.preventDefault();\n e.stopPropagation();\n resolve(false);\n };\n\n txn.oncomplete = function () {\n var matchedChrome = navigator.userAgent.match(/Chrome\\/(\\d+)/);\n var matchedEdge = navigator.userAgent.match(/Edge\\//);\n // MS Edge pretends to be Chrome 42:\n // https://msdn.microsoft.com/en-us/library/hh869301%28v=vs.85%29.aspx\n resolve(matchedEdge || !matchedChrome || parseInt(matchedChrome[1], 10) >= 43);\n };\n })[\"catch\"](function () {\n return false; // error, so assume unsupported\n });\n}\n\nfunction _checkBlobSupport(idb) {\n if (typeof supportsBlobs === 'boolean') {\n return Promise$1.resolve(supportsBlobs);\n }\n return _checkBlobSupportWithoutCaching(idb).then(function (value) {\n supportsBlobs = value;\n return supportsBlobs;\n });\n}\n\nfunction _deferReadiness(dbInfo) {\n var dbContext = dbContexts[dbInfo.name];\n\n // Create a deferred object representing the current database operation.\n var deferredOperation = {};\n\n deferredOperation.promise = new Promise$1(function (resolve, reject) {\n deferredOperation.resolve = resolve;\n deferredOperation.reject = reject;\n });\n\n // Enqueue the deferred operation.\n dbContext.deferredOperations.push(deferredOperation);\n\n // Chain its promise to the database readiness.\n if (!dbContext.dbReady) {\n dbContext.dbReady = deferredOperation.promise;\n } else {\n dbContext.dbReady = dbContext.dbReady.then(function () {\n return deferredOperation.promise;\n });\n }\n}\n\nfunction _advanceReadiness(dbInfo) {\n var dbContext = dbContexts[dbInfo.name];\n\n // Dequeue a deferred operation.\n var deferredOperation = dbContext.deferredOperations.pop();\n\n // Resolve its promise (which is part of the database readiness\n // chain of promises).\n if (deferredOperation) {\n deferredOperation.resolve();\n return deferredOperation.promise;\n }\n}\n\nfunction _rejectReadiness(dbInfo, err) {\n var dbContext = dbContexts[dbInfo.name];\n\n // Dequeue a deferred operation.\n var deferredOperation = dbContext.deferredOperations.pop();\n\n // Reject its promise (which is part of the database readiness\n // chain of promises).\n if (deferredOperation) {\n deferredOperation.reject(err);\n return deferredOperation.promise;\n }\n}\n\nfunction _getConnection(dbInfo, upgradeNeeded) {\n return new Promise$1(function (resolve, reject) {\n dbContexts[dbInfo.name] = dbContexts[dbInfo.name] || createDbContext();\n\n if (dbInfo.db) {\n if (upgradeNeeded) {\n _deferReadiness(dbInfo);\n dbInfo.db.close();\n } else {\n return resolve(dbInfo.db);\n }\n }\n\n var dbArgs = [dbInfo.name];\n\n if (upgradeNeeded) {\n dbArgs.push(dbInfo.version);\n }\n\n var openreq = idb.open.apply(idb, dbArgs);\n\n if (upgradeNeeded) {\n openreq.onupgradeneeded = function (e) {\n var db = openreq.result;\n try {\n db.createObjectStore(dbInfo.storeName);\n if (e.oldVersion <= 1) {\n // Added when support for blob shims was added\n db.createObjectStore(DETECT_BLOB_SUPPORT_STORE);\n }\n } catch (ex) {\n if (ex.name === 'ConstraintError') {\n console.warn('The database \"' + dbInfo.name + '\"' + ' has been upgraded from version ' + e.oldVersion + ' to version ' + e.newVersion + ', but the storage \"' + dbInfo.storeName + '\" already exists.');\n } else {\n throw ex;\n }\n }\n };\n }\n\n openreq.onerror = function (e) {\n e.preventDefault();\n reject(openreq.error);\n };\n\n openreq.onsuccess = function () {\n var db = openreq.result;\n db.onversionchange = function (e) {\n // Triggered when the database is modified (e.g. adding an objectStore) or\n // deleted (even when initiated by other sessions in different tabs).\n // Closing the connection here prevents those operations from being blocked.\n // If the database is accessed again later by this instance, the connection\n // will be reopened or the database recreated as needed.\n e.target.close();\n };\n resolve(db);\n _advanceReadiness(dbInfo);\n };\n });\n}\n\nfunction _getOriginalConnection(dbInfo) {\n return _getConnection(dbInfo, false);\n}\n\nfunction _getUpgradedConnection(dbInfo) {\n return _getConnection(dbInfo, true);\n}\n\nfunction _isUpgradeNeeded(dbInfo, defaultVersion) {\n if (!dbInfo.db) {\n return true;\n }\n\n var isNewStore = !dbInfo.db.objectStoreNames.contains(dbInfo.storeName);\n var isDowngrade = dbInfo.version < dbInfo.db.version;\n var isUpgrade = dbInfo.version > dbInfo.db.version;\n\n if (isDowngrade) {\n // If the version is not the default one\n // then warn for impossible downgrade.\n if (dbInfo.version !== defaultVersion) {\n console.warn('The database \"' + dbInfo.name + '\"' + \" can't be downgraded from version \" + dbInfo.db.version + ' to version ' + dbInfo.version + '.');\n }\n // Align the versions to prevent errors.\n dbInfo.version = dbInfo.db.version;\n }\n\n if (isUpgrade || isNewStore) {\n // If the store is new then increment the version (if needed).\n // This will trigger an \"upgradeneeded\" event which is required\n // for creating a store.\n if (isNewStore) {\n var incVersion = dbInfo.db.version + 1;\n if (incVersion > dbInfo.version) {\n dbInfo.version = incVersion;\n }\n }\n\n return true;\n }\n\n return false;\n}\n\n// encode a blob for indexeddb engines that don't support blobs\nfunction _encodeBlob(blob) {\n return new Promise$1(function (resolve, reject) {\n var reader = new FileReader();\n reader.onerror = reject;\n reader.onloadend = function (e) {\n var base64 = btoa(e.target.result || '');\n resolve({\n __local_forage_encoded_blob: true,\n data: base64,\n type: blob.type\n });\n };\n reader.readAsBinaryString(blob);\n });\n}\n\n// decode an encoded blob\nfunction _decodeBlob(encodedBlob) {\n var arrayBuff = _binStringToArrayBuffer(atob(encodedBlob.data));\n return createBlob([arrayBuff], { type: encodedBlob.type });\n}\n\n// is this one of our fancy encoded blobs?\nfunction _isEncodedBlob(value) {\n return value && value.__local_forage_encoded_blob;\n}\n\n// Specialize the default `ready()` function by making it dependent\n// on the current database operations. Thus, the driver will be actually\n// ready when it's been initialized (default) *and* there are no pending\n// operations on the database (initiated by some other instances).\nfunction _fullyReady(callback) {\n var self = this;\n\n var promise = self._initReady().then(function () {\n var dbContext = dbContexts[self._dbInfo.name];\n\n if (dbContext && dbContext.dbReady) {\n return dbContext.dbReady;\n }\n });\n\n executeTwoCallbacks(promise, callback, callback);\n return promise;\n}\n\n// Try to establish a new db connection to replace the\n// current one which is broken (i.e. experiencing\n// InvalidStateError while creating a transaction).\nfunction _tryReconnect(dbInfo) {\n _deferReadiness(dbInfo);\n\n var dbContext = dbContexts[dbInfo.name];\n var forages = dbContext.forages;\n\n for (var i = 0; i < forages.length; i++) {\n var forage = forages[i];\n if (forage._dbInfo.db) {\n forage._dbInfo.db.close();\n forage._dbInfo.db = null;\n }\n }\n dbInfo.db = null;\n\n return _getOriginalConnection(dbInfo).then(function (db) {\n dbInfo.db = db;\n if (_isUpgradeNeeded(dbInfo)) {\n // Reopen the database for upgrading.\n return _getUpgradedConnection(dbInfo);\n }\n return db;\n }).then(function (db) {\n // store the latest db reference\n // in case the db was upgraded\n dbInfo.db = dbContext.db = db;\n for (var i = 0; i < forages.length; i++) {\n forages[i]._dbInfo.db = db;\n }\n })[\"catch\"](function (err) {\n _rejectReadiness(dbInfo, err);\n throw err;\n });\n}\n\n// FF doesn't like Promises (micro-tasks) and IDDB store operations,\n// so we have to do it with callbacks\nfunction createTransaction(dbInfo, mode, callback, retries) {\n if (retries === undefined) {\n retries = 1;\n }\n\n try {\n var tx = dbInfo.db.transaction(dbInfo.storeName, mode);\n callback(null, tx);\n } catch (err) {\n if (retries > 0 && (!dbInfo.db || err.name === 'InvalidStateError' || err.name === 'NotFoundError')) {\n return Promise$1.resolve().then(function () {\n if (!dbInfo.db || err.name === 'NotFoundError' && !dbInfo.db.objectStoreNames.contains(dbInfo.storeName) && dbInfo.version <= dbInfo.db.version) {\n // increase the db version, to create the new ObjectStore\n if (dbInfo.db) {\n dbInfo.version = dbInfo.db.version + 1;\n }\n // Reopen the database for upgrading.\n return _getUpgradedConnection(dbInfo);\n }\n }).then(function () {\n return _tryReconnect(dbInfo).then(function () {\n createTransaction(dbInfo, mode, callback, retries - 1);\n });\n })[\"catch\"](callback);\n }\n\n callback(err);\n }\n}\n\nfunction createDbContext() {\n return {\n // Running localForages sharing a database.\n forages: [],\n // Shared database.\n db: null,\n // Database readiness (promise).\n dbReady: null,\n // Deferred operations on the database.\n deferredOperations: []\n };\n}\n\n// Open the IndexedDB database (automatically creates one if one didn't\n// previously exist), using any options set in the config.\nfunction _initStorage(options) {\n var self = this;\n var dbInfo = {\n db: null\n };\n\n if (options) {\n for (var i in options) {\n dbInfo[i] = options[i];\n }\n }\n\n // Get the current context of the database;\n var dbContext = dbContexts[dbInfo.name];\n\n // ...or create a new context.\n if (!dbContext) {\n dbContext = createDbContext();\n // Register the new context in the global container.\n dbContexts[dbInfo.name] = dbContext;\n }\n\n // Register itself as a running localForage in the current context.\n dbContext.forages.push(self);\n\n // Replace the default `ready()` function with the specialized one.\n if (!self._initReady) {\n self._initReady = self.ready;\n self.ready = _fullyReady;\n }\n\n // Create an array of initialization states of the related localForages.\n var initPromises = [];\n\n function ignoreErrors() {\n // Don't handle errors here,\n // just makes sure related localForages aren't pending.\n return Promise$1.resolve();\n }\n\n for (var j = 0; j < dbContext.forages.length; j++) {\n var forage = dbContext.forages[j];\n if (forage !== self) {\n // Don't wait for itself...\n initPromises.push(forage._initReady()[\"catch\"](ignoreErrors));\n }\n }\n\n // Take a snapshot of the related localForages.\n var forages = dbContext.forages.slice(0);\n\n // Initialize the connection process only when\n // all the related localForages aren't pending.\n return Promise$1.all(initPromises).then(function () {\n dbInfo.db = dbContext.db;\n // Get the connection or open a new one without upgrade.\n return _getOriginalConnection(dbInfo);\n }).then(function (db) {\n dbInfo.db = db;\n if (_isUpgradeNeeded(dbInfo, self._defaultConfig.version)) {\n // Reopen the database for upgrading.\n return _getUpgradedConnection(dbInfo);\n }\n return db;\n }).then(function (db) {\n dbInfo.db = dbContext.db = db;\n self._dbInfo = dbInfo;\n // Share the final connection amongst related localForages.\n for (var k = 0; k < forages.length; k++) {\n var forage = forages[k];\n if (forage !== self) {\n // Self is already up-to-date.\n forage._dbInfo.db = dbInfo.db;\n forage._dbInfo.version = dbInfo.version;\n }\n }\n });\n}\n\nfunction getItem(key, callback) {\n var self = this;\n\n key = normalizeKey(key);\n\n var promise = new Promise$1(function (resolve, reject) {\n self.ready().then(function () {\n createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {\n if (err) {\n return reject(err);\n }\n\n try {\n var store = transaction.objectStore(self._dbInfo.storeName);\n var req = store.get(key);\n\n req.onsuccess = function () {\n var value = req.result;\n if (value === undefined) {\n value = null;\n }\n if (_isEncodedBlob(value)) {\n value = _decodeBlob(value);\n }\n resolve(value);\n };\n\n req.onerror = function () {\n reject(req.error);\n };\n } catch (e) {\n reject(e);\n }\n });\n })[\"catch\"](reject);\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\n// Iterate over all items stored in database.\nfunction iterate(iterator, callback) {\n var self = this;\n\n var promise = new Promise$1(function (resolve, reject) {\n self.ready().then(function () {\n createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {\n if (err) {\n return reject(err);\n }\n\n try {\n var store = transaction.objectStore(self._dbInfo.storeName);\n var req = store.openCursor();\n var iterationNumber = 1;\n\n req.onsuccess = function () {\n var cursor = req.result;\n\n if (cursor) {\n var value = cursor.value;\n if (_isEncodedBlob(value)) {\n value = _decodeBlob(value);\n }\n var result = iterator(value, cursor.key, iterationNumber++);\n\n // when the iterator callback returns any\n // (non-`undefined`) value, then we stop\n // the iteration immediately\n if (result !== void 0) {\n resolve(result);\n } else {\n cursor[\"continue\"]();\n }\n } else {\n resolve();\n }\n };\n\n req.onerror = function () {\n reject(req.error);\n };\n } catch (e) {\n reject(e);\n }\n });\n })[\"catch\"](reject);\n });\n\n executeCallback(promise, callback);\n\n return promise;\n}\n\nfunction setItem(key, value, callback) {\n var self = this;\n\n key = normalizeKey(key);\n\n var promise = new Promise$1(function (resolve, reject) {\n var dbInfo;\n self.ready().then(function () {\n dbInfo = self._dbInfo;\n if (toString.call(value) === '[object Blob]') {\n return _checkBlobSupport(dbInfo.db).then(function (blobSupport) {\n if (blobSupport) {\n return value;\n }\n return _encodeBlob(value);\n });\n }\n return value;\n }).then(function (value) {\n createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) {\n if (err) {\n return reject(err);\n }\n\n try {\n var store = transaction.objectStore(self._dbInfo.storeName);\n\n // The reason we don't _save_ null is because IE 10 does\n // not support saving the `null` type in IndexedDB. How\n // ironic, given the bug below!\n // See: https://github.com/mozilla/localForage/issues/161\n if (value === null) {\n value = undefined;\n }\n\n var req = store.put(value, key);\n\n transaction.oncomplete = function () {\n // Cast to undefined so the value passed to\n // callback/promise is the same as what one would get out\n // of `getItem()` later. This leads to some weirdness\n // (setItem('foo', undefined) will return `null`), but\n // it's not my fault localStorage is our baseline and that\n // it's weird.\n if (value === undefined) {\n value = null;\n }\n\n resolve(value);\n };\n transaction.onabort = transaction.onerror = function () {\n var err = req.error ? req.error : req.transaction.error;\n reject(err);\n };\n } catch (e) {\n reject(e);\n }\n });\n })[\"catch\"](reject);\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\nfunction removeItem(key, callback) {\n var self = this;\n\n key = normalizeKey(key);\n\n var promise = new Promise$1(function (resolve, reject) {\n self.ready().then(function () {\n createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) {\n if (err) {\n return reject(err);\n }\n\n try {\n var store = transaction.objectStore(self._dbInfo.storeName);\n // We use a Grunt task to make this safe for IE and some\n // versions of Android (including those used by Cordova).\n // Normally IE won't like `.delete()` and will insist on\n // using `['delete']()`, but we have a build step that\n // fixes this for us now.\n var req = store[\"delete\"](key);\n transaction.oncomplete = function () {\n resolve();\n };\n\n transaction.onerror = function () {\n reject(req.error);\n };\n\n // The request will be also be aborted if we've exceeded our storage\n // space.\n transaction.onabort = function () {\n var err = req.error ? req.error : req.transaction.error;\n reject(err);\n };\n } catch (e) {\n reject(e);\n }\n });\n })[\"catch\"](reject);\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\nfunction clear(callback) {\n var self = this;\n\n var promise = new Promise$1(function (resolve, reject) {\n self.ready().then(function () {\n createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) {\n if (err) {\n return reject(err);\n }\n\n try {\n var store = transaction.objectStore(self._dbInfo.storeName);\n var req = store.clear();\n\n transaction.oncomplete = function () {\n resolve();\n };\n\n transaction.onabort = transaction.onerror = function () {\n var err = req.error ? req.error : req.transaction.error;\n reject(err);\n };\n } catch (e) {\n reject(e);\n }\n });\n })[\"catch\"](reject);\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\nfunction length(callback) {\n var self = this;\n\n var promise = new Promise$1(function (resolve, reject) {\n self.ready().then(function () {\n createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {\n if (err) {\n return reject(err);\n }\n\n try {\n var store = transaction.objectStore(self._dbInfo.storeName);\n var req = store.count();\n\n req.onsuccess = function () {\n resolve(req.result);\n };\n\n req.onerror = function () {\n reject(req.error);\n };\n } catch (e) {\n reject(e);\n }\n });\n })[\"catch\"](reject);\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\nfunction key(n, callback) {\n var self = this;\n\n var promise = new Promise$1(function (resolve, reject) {\n if (n < 0) {\n resolve(null);\n\n return;\n }\n\n self.ready().then(function () {\n createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {\n if (err) {\n return reject(err);\n }\n\n try {\n var store = transaction.objectStore(self._dbInfo.storeName);\n var advanced = false;\n var req = store.openKeyCursor();\n\n req.onsuccess = function () {\n var cursor = req.result;\n if (!cursor) {\n // this means there weren't enough keys\n resolve(null);\n\n return;\n }\n\n if (n === 0) {\n // We have the first key, return it if that's what they\n // wanted.\n resolve(cursor.key);\n } else {\n if (!advanced) {\n // Otherwise, ask the cursor to skip ahead n\n // records.\n advanced = true;\n cursor.advance(n);\n } else {\n // When we get here, we've got the nth key.\n resolve(cursor.key);\n }\n }\n };\n\n req.onerror = function () {\n reject(req.error);\n };\n } catch (e) {\n reject(e);\n }\n });\n })[\"catch\"](reject);\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\nfunction keys(callback) {\n var self = this;\n\n var promise = new Promise$1(function (resolve, reject) {\n self.ready().then(function () {\n createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {\n if (err) {\n return reject(err);\n }\n\n try {\n var store = transaction.objectStore(self._dbInfo.storeName);\n var req = store.openKeyCursor();\n var keys = [];\n\n req.onsuccess = function () {\n var cursor = req.result;\n\n if (!cursor) {\n resolve(keys);\n return;\n }\n\n keys.push(cursor.key);\n cursor[\"continue\"]();\n };\n\n req.onerror = function () {\n reject(req.error);\n };\n } catch (e) {\n reject(e);\n }\n });\n })[\"catch\"](reject);\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\nfunction dropInstance(options, callback) {\n callback = getCallback.apply(this, arguments);\n\n var currentConfig = this.config();\n options = typeof options !== 'function' && options || {};\n if (!options.name) {\n options.name = options.name || currentConfig.name;\n options.storeName = options.storeName || currentConfig.storeName;\n }\n\n var self = this;\n var promise;\n if (!options.name) {\n promise = Promise$1.reject('Invalid arguments');\n } else {\n var isCurrentDb = options.name === currentConfig.name && self._dbInfo.db;\n\n var dbPromise = isCurrentDb ? Promise$1.resolve(self._dbInfo.db) : _getOriginalConnection(options).then(function (db) {\n var dbContext = dbContexts[options.name];\n var forages = dbContext.forages;\n dbContext.db = db;\n for (var i = 0; i < forages.length; i++) {\n forages[i]._dbInfo.db = db;\n }\n return db;\n });\n\n if (!options.storeName) {\n promise = dbPromise.then(function (db) {\n _deferReadiness(options);\n\n var dbContext = dbContexts[options.name];\n var forages = dbContext.forages;\n\n db.close();\n for (var i = 0; i < forages.length; i++) {\n var forage = forages[i];\n forage._dbInfo.db = null;\n }\n\n var dropDBPromise = new Promise$1(function (resolve, reject) {\n var req = idb.deleteDatabase(options.name);\n\n req.onerror = function () {\n var db = req.result;\n if (db) {\n db.close();\n }\n reject(req.error);\n };\n\n req.onblocked = function () {\n // Closing all open connections in onversionchange handler should prevent this situation, but if\n // we do get here, it just means the request remains pending - eventually it will succeed or error\n console.warn('dropInstance blocked for database \"' + options.name + '\" until all open connections are closed');\n };\n\n req.onsuccess = function () {\n var db = req.result;\n if (db) {\n db.close();\n }\n resolve(db);\n };\n });\n\n return dropDBPromise.then(function (db) {\n dbContext.db = db;\n for (var i = 0; i < forages.length; i++) {\n var _forage = forages[i];\n _advanceReadiness(_forage._dbInfo);\n }\n })[\"catch\"](function (err) {\n (_rejectReadiness(options, err) || Promise$1.resolve())[\"catch\"](function () {});\n throw err;\n });\n });\n } else {\n promise = dbPromise.then(function (db) {\n if (!db.objectStoreNames.contains(options.storeName)) {\n return;\n }\n\n var newVersion = db.version + 1;\n\n _deferReadiness(options);\n\n var dbContext = dbContexts[options.name];\n var forages = dbContext.forages;\n\n db.close();\n for (var i = 0; i < forages.length; i++) {\n var forage = forages[i];\n forage._dbInfo.db = null;\n forage._dbInfo.version = newVersion;\n }\n\n var dropObjectPromise = new Promise$1(function (resolve, reject) {\n var req = idb.open(options.name, newVersion);\n\n req.onerror = function (err) {\n var db = req.result;\n db.close();\n reject(err);\n };\n\n req.onupgradeneeded = function () {\n var db = req.result;\n db.deleteObjectStore(options.storeName);\n };\n\n req.onsuccess = function () {\n var db = req.result;\n db.close();\n resolve(db);\n };\n });\n\n return dropObjectPromise.then(function (db) {\n dbContext.db = db;\n for (var j = 0; j < forages.length; j++) {\n var _forage2 = forages[j];\n _forage2._dbInfo.db = db;\n _advanceReadiness(_forage2._dbInfo);\n }\n })[\"catch\"](function (err) {\n (_rejectReadiness(options, err) || Promise$1.resolve())[\"catch\"](function () {});\n throw err;\n });\n });\n }\n }\n\n executeCallback(promise, callback);\n return promise;\n}\n\nvar asyncStorage = {\n _driver: 'asyncStorage',\n _initStorage: _initStorage,\n _support: isIndexedDBValid(),\n iterate: iterate,\n getItem: getItem,\n setItem: setItem,\n removeItem: removeItem,\n clear: clear,\n length: length,\n key: key,\n keys: keys,\n dropInstance: dropInstance\n};\n\nfunction isWebSQLValid() {\n return typeof openDatabase === 'function';\n}\n\n// Sadly, the best way to save binary data in WebSQL/localStorage is serializing\n// it to Base64, so this is how we store it to prevent very strange errors with less\n// verbose ways of binary <-> string data storage.\nvar BASE_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\nvar BLOB_TYPE_PREFIX = '~~local_forage_type~';\nvar BLOB_TYPE_PREFIX_REGEX = /^~~local_forage_type~([^~]+)~/;\n\nvar SERIALIZED_MARKER = '__lfsc__:';\nvar SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER.length;\n\n// OMG the serializations!\nvar TYPE_ARRAYBUFFER = 'arbf';\nvar TYPE_BLOB = 'blob';\nvar TYPE_INT8ARRAY = 'si08';\nvar TYPE_UINT8ARRAY = 'ui08';\nvar TYPE_UINT8CLAMPEDARRAY = 'uic8';\nvar TYPE_INT16ARRAY = 'si16';\nvar TYPE_INT32ARRAY = 'si32';\nvar TYPE_UINT16ARRAY = 'ur16';\nvar TYPE_UINT32ARRAY = 'ui32';\nvar TYPE_FLOAT32ARRAY = 'fl32';\nvar TYPE_FLOAT64ARRAY = 'fl64';\nvar TYPE_SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER_LENGTH + TYPE_ARRAYBUFFER.length;\n\nvar toString$1 = Object.prototype.toString;\n\nfunction stringToBuffer(serializedString) {\n // Fill the string into a ArrayBuffer.\n var bufferLength = serializedString.length * 0.75;\n var len = serializedString.length;\n var i;\n var p = 0;\n var encoded1, encoded2, encoded3, encoded4;\n\n if (serializedString[serializedString.length - 1] === '=') {\n bufferLength--;\n if (serializedString[serializedString.length - 2] === '=') {\n bufferLength--;\n }\n }\n\n var buffer = new ArrayBuffer(bufferLength);\n var bytes = new Uint8Array(buffer);\n\n for (i = 0; i < len; i += 4) {\n encoded1 = BASE_CHARS.indexOf(serializedString[i]);\n encoded2 = BASE_CHARS.indexOf(serializedString[i + 1]);\n encoded3 = BASE_CHARS.indexOf(serializedString[i + 2]);\n encoded4 = BASE_CHARS.indexOf(serializedString[i + 3]);\n\n /*jslint bitwise: true */\n bytes[p++] = encoded1 << 2 | encoded2 >> 4;\n bytes[p++] = (encoded2 & 15) << 4 | encoded3 >> 2;\n bytes[p++] = (encoded3 & 3) << 6 | encoded4 & 63;\n }\n return buffer;\n}\n\n// Converts a buffer to a string to store, serialized, in the backend\n// storage library.\nfunction bufferToString(buffer) {\n // base64-arraybuffer\n var bytes = new Uint8Array(buffer);\n var base64String = '';\n var i;\n\n for (i = 0; i < bytes.length; i += 3) {\n /*jslint bitwise: true */\n base64String += BASE_CHARS[bytes[i] >> 2];\n base64String += BASE_CHARS[(bytes[i] & 3) << 4 | bytes[i + 1] >> 4];\n base64String += BASE_CHARS[(bytes[i + 1] & 15) << 2 | bytes[i + 2] >> 6];\n base64String += BASE_CHARS[bytes[i + 2] & 63];\n }\n\n if (bytes.length % 3 === 2) {\n base64String = base64String.substring(0, base64String.length - 1) + '=';\n } else if (bytes.length % 3 === 1) {\n base64String = base64String.substring(0, base64String.length - 2) + '==';\n }\n\n return base64String;\n}\n\n// Serialize a value, afterwards executing a callback (which usually\n// instructs the `setItem()` callback/promise to be executed). This is how\n// we store binary data with localStorage.\nfunction serialize(value, callback) {\n var valueType = '';\n if (value) {\n valueType = toString$1.call(value);\n }\n\n // Cannot use `value instanceof ArrayBuffer` or such here, as these\n // checks fail when running the tests using casper.js...\n //\n // TODO: See why those tests fail and use a better solution.\n if (value && (valueType === '[object ArrayBuffer]' || value.buffer && toString$1.call(value.buffer) === '[object ArrayBuffer]')) {\n // Convert binary arrays to a string and prefix the string with\n // a special marker.\n var buffer;\n var marker = SERIALIZED_MARKER;\n\n if (value instanceof ArrayBuffer) {\n buffer = value;\n marker += TYPE_ARRAYBUFFER;\n } else {\n buffer = value.buffer;\n\n if (valueType === '[object Int8Array]') {\n marker += TYPE_INT8ARRAY;\n } else if (valueType === '[object Uint8Array]') {\n marker += TYPE_UINT8ARRAY;\n } else if (valueType === '[object Uint8ClampedArray]') {\n marker += TYPE_UINT8CLAMPEDARRAY;\n } else if (valueType === '[object Int16Array]') {\n marker += TYPE_INT16ARRAY;\n } else if (valueType === '[object Uint16Array]') {\n marker += TYPE_UINT16ARRAY;\n } else if (valueType === '[object Int32Array]') {\n marker += TYPE_INT32ARRAY;\n } else if (valueType === '[object Uint32Array]') {\n marker += TYPE_UINT32ARRAY;\n } else if (valueType === '[object Float32Array]') {\n marker += TYPE_FLOAT32ARRAY;\n } else if (valueType === '[object Float64Array]') {\n marker += TYPE_FLOAT64ARRAY;\n } else {\n callback(new Error('Failed to get type for BinaryArray'));\n }\n }\n\n callback(marker + bufferToString(buffer));\n } else if (valueType === '[object Blob]') {\n // Conver the blob to a binaryArray and then to a string.\n var fileReader = new FileReader();\n\n fileReader.onload = function () {\n // Backwards-compatible prefix for the blob type.\n var str = BLOB_TYPE_PREFIX + value.type + '~' + bufferToString(this.result);\n\n callback(SERIALIZED_MARKER + TYPE_BLOB + str);\n };\n\n fileReader.readAsArrayBuffer(value);\n } else {\n try {\n callback(JSON.stringify(value));\n } catch (e) {\n console.error(\"Couldn't convert value into a JSON string: \", value);\n\n callback(null, e);\n }\n }\n}\n\n// Deserialize data we've inserted into a value column/field. We place\n// special markers into our strings to mark them as encoded; this isn't\n// as nice as a meta field, but it's the only sane thing we can do whilst\n// keeping localStorage support intact.\n//\n// Oftentimes this will just deserialize JSON content, but if we have a\n// special marker (SERIALIZED_MARKER, defined above), we will extract\n// some kind of arraybuffer/binary data/typed array out of the string.\nfunction deserialize(value) {\n // If we haven't marked this string as being specially serialized (i.e.\n // something other than serialized JSON), we can just return it and be\n // done with it.\n if (value.substring(0, SERIALIZED_MARKER_LENGTH) !== SERIALIZED_MARKER) {\n return JSON.parse(value);\n }\n\n // The following code deals with deserializing some kind of Blob or\n // TypedArray. First we separate out the type of data we're dealing\n // with from the data itself.\n var serializedString = value.substring(TYPE_SERIALIZED_MARKER_LENGTH);\n var type = value.substring(SERIALIZED_MARKER_LENGTH, TYPE_SERIALIZED_MARKER_LENGTH);\n\n var blobType;\n // Backwards-compatible blob type serialization strategy.\n // DBs created with older versions of localForage will simply not have the blob type.\n if (type === TYPE_BLOB && BLOB_TYPE_PREFIX_REGEX.test(serializedString)) {\n var matcher = serializedString.match(BLOB_TYPE_PREFIX_REGEX);\n blobType = matcher[1];\n serializedString = serializedString.substring(matcher[0].length);\n }\n var buffer = stringToBuffer(serializedString);\n\n // Return the right type based on the code/type set during\n // serialization.\n switch (type) {\n case TYPE_ARRAYBUFFER:\n return buffer;\n case TYPE_BLOB:\n return createBlob([buffer], { type: blobType });\n case TYPE_INT8ARRAY:\n return new Int8Array(buffer);\n case TYPE_UINT8ARRAY:\n return new Uint8Array(buffer);\n case TYPE_UINT8CLAMPEDARRAY:\n return new Uint8ClampedArray(buffer);\n case TYPE_INT16ARRAY:\n return new Int16Array(buffer);\n case TYPE_UINT16ARRAY:\n return new Uint16Array(buffer);\n case TYPE_INT32ARRAY:\n return new Int32Array(buffer);\n case TYPE_UINT32ARRAY:\n return new Uint32Array(buffer);\n case TYPE_FLOAT32ARRAY:\n return new Float32Array(buffer);\n case TYPE_FLOAT64ARRAY:\n return new Float64Array(buffer);\n default:\n throw new Error('Unkown type: ' + type);\n }\n}\n\nvar localforageSerializer = {\n serialize: serialize,\n deserialize: deserialize,\n stringToBuffer: stringToBuffer,\n bufferToString: bufferToString\n};\n\n/*\n * Includes code from:\n *\n * base64-arraybuffer\n * https://github.com/niklasvh/base64-arraybuffer\n *\n * Copyright (c) 2012 Niklas von Hertzen\n * Licensed under the MIT license.\n */\n\nfunction createDbTable(t, dbInfo, callback, errorCallback) {\n t.executeSql('CREATE TABLE IF NOT EXISTS ' + dbInfo.storeName + ' ' + '(id INTEGER PRIMARY KEY, key unique, value)', [], callback, errorCallback);\n}\n\n// Open the WebSQL database (automatically creates one if one didn't\n// previously exist), using any options set in the config.\nfunction _initStorage$1(options) {\n var self = this;\n var dbInfo = {\n db: null\n };\n\n if (options) {\n for (var i in options) {\n dbInfo[i] = typeof options[i] !== 'string' ? options[i].toString() : options[i];\n }\n }\n\n var dbInfoPromise = new Promise$1(function (resolve, reject) {\n // Open the database; the openDatabase API will automatically\n // create it for us if it doesn't exist.\n try {\n dbInfo.db = openDatabase(dbInfo.name, String(dbInfo.version), dbInfo.description, dbInfo.size);\n } catch (e) {\n return reject(e);\n }\n\n // Create our key/value table if it doesn't exist.\n dbInfo.db.transaction(function (t) {\n createDbTable(t, dbInfo, function () {\n self._dbInfo = dbInfo;\n resolve();\n }, function (t, error) {\n reject(error);\n });\n }, reject);\n });\n\n dbInfo.serializer = localforageSerializer;\n return dbInfoPromise;\n}\n\nfunction tryExecuteSql(t, dbInfo, sqlStatement, args, callback, errorCallback) {\n t.executeSql(sqlStatement, args, callback, function (t, error) {\n if (error.code === error.SYNTAX_ERR) {\n t.executeSql('SELECT name FROM sqlite_master ' + \"WHERE type='table' AND name = ?\", [dbInfo.storeName], function (t, results) {\n if (!results.rows.length) {\n // if the table is missing (was deleted)\n // re-create it table and retry\n createDbTable(t, dbInfo, function () {\n t.executeSql(sqlStatement, args, callback, errorCallback);\n }, errorCallback);\n } else {\n errorCallback(t, error);\n }\n }, errorCallback);\n } else {\n errorCallback(t, error);\n }\n }, errorCallback);\n}\n\nfunction getItem$1(key, callback) {\n var self = this;\n\n key = normalizeKey(key);\n\n var promise = new Promise$1(function (resolve, reject) {\n self.ready().then(function () {\n var dbInfo = self._dbInfo;\n dbInfo.db.transaction(function (t) {\n tryExecuteSql(t, dbInfo, 'SELECT * FROM ' + dbInfo.storeName + ' WHERE key = ? LIMIT 1', [key], function (t, results) {\n var result = results.rows.length ? results.rows.item(0).value : null;\n\n // Check to see if this is serialized content we need to\n // unpack.\n if (result) {\n result = dbInfo.serializer.deserialize(result);\n }\n\n resolve(result);\n }, function (t, error) {\n reject(error);\n });\n });\n })[\"catch\"](reject);\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\nfunction iterate$1(iterator, callback) {\n var self = this;\n\n var promise = new Promise$1(function (resolve, reject) {\n self.ready().then(function () {\n var dbInfo = self._dbInfo;\n\n dbInfo.db.transaction(function (t) {\n tryExecuteSql(t, dbInfo, 'SELECT * FROM ' + dbInfo.storeName, [], function (t, results) {\n var rows = results.rows;\n var length = rows.length;\n\n for (var i = 0; i < length; i++) {\n var item = rows.item(i);\n var result = item.value;\n\n // Check to see if this is serialized content\n // we need to unpack.\n if (result) {\n result = dbInfo.serializer.deserialize(result);\n }\n\n result = iterator(result, item.key, i + 1);\n\n // void(0) prevents problems with redefinition\n // of `undefined`.\n if (result !== void 0) {\n resolve(result);\n return;\n }\n }\n\n resolve();\n }, function (t, error) {\n reject(error);\n });\n });\n })[\"catch\"](reject);\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\nfunction _setItem(key, value, callback, retriesLeft) {\n var self = this;\n\n key = normalizeKey(key);\n\n var promise = new Promise$1(function (resolve, reject) {\n self.ready().then(function () {\n // The localStorage API doesn't return undefined values in an\n // \"expected\" way, so undefined is always cast to null in all\n // drivers. See: https://github.com/mozilla/localForage/pull/42\n if (value === undefined) {\n value = null;\n }\n\n // Save the original value to pass to the callback.\n var originalValue = value;\n\n var dbInfo = self._dbInfo;\n dbInfo.serializer.serialize(value, function (value, error) {\n if (error) {\n reject(error);\n } else {\n dbInfo.db.transaction(function (t) {\n tryExecuteSql(t, dbInfo, 'INSERT OR REPLACE INTO ' + dbInfo.storeName + ' ' + '(key, value) VALUES (?, ?)', [key, value], function () {\n resolve(originalValue);\n }, function (t, error) {\n reject(error);\n });\n }, function (sqlError) {\n // The transaction failed; check\n // to see if it's a quota error.\n if (sqlError.code === sqlError.QUOTA_ERR) {\n // We reject the callback outright for now, but\n // it's worth trying to re-run the transaction.\n // Even if the user accepts the prompt to use\n // more storage on Safari, this error will\n // be called.\n //\n // Try to re-run the transaction.\n if (retriesLeft > 0) {\n resolve(_setItem.apply(self, [key, originalValue, callback, retriesLeft - 1]));\n return;\n }\n reject(sqlError);\n }\n });\n }\n });\n })[\"catch\"](reject);\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\nfunction setItem$1(key, value, callback) {\n return _setItem.apply(this, [key, value, callback, 1]);\n}\n\nfunction removeItem$1(key, callback) {\n var self = this;\n\n key = normalizeKey(key);\n\n var promise = new Promise$1(function (resolve, reject) {\n self.ready().then(function () {\n var dbInfo = self._dbInfo;\n dbInfo.db.transaction(function (t) {\n tryExecuteSql(t, dbInfo, 'DELETE FROM ' + dbInfo.storeName + ' WHERE key = ?', [key], function () {\n resolve();\n }, function (t, error) {\n reject(error);\n });\n });\n })[\"catch\"](reject);\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\n// Deletes every item in the table.\n// TODO: Find out if this resets the AUTO_INCREMENT number.\nfunction clear$1(callback) {\n var self = this;\n\n var promise = new Promise$1(function (resolve, reject) {\n self.ready().then(function () {\n var dbInfo = self._dbInfo;\n dbInfo.db.transaction(function (t) {\n tryExecuteSql(t, dbInfo, 'DELETE FROM ' + dbInfo.storeName, [], function () {\n resolve();\n }, function (t, error) {\n reject(error);\n });\n });\n })[\"catch\"](reject);\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\n// Does a simple `COUNT(key)` to get the number of items stored in\n// localForage.\nfunction length$1(callback) {\n var self = this;\n\n var promise = new Promise$1(function (resolve, reject) {\n self.ready().then(function () {\n var dbInfo = self._dbInfo;\n dbInfo.db.transaction(function (t) {\n // Ahhh, SQL makes this one soooooo easy.\n tryExecuteSql(t, dbInfo, 'SELECT COUNT(key) as c FROM ' + dbInfo.storeName, [], function (t, results) {\n var result = results.rows.item(0).c;\n resolve(result);\n }, function (t, error) {\n reject(error);\n });\n });\n })[\"catch\"](reject);\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\n// Return the key located at key index X; essentially gets the key from a\n// `WHERE id = ?`. This is the most efficient way I can think to implement\n// this rarely-used (in my experience) part of the API, but it can seem\n// inconsistent, because we do `INSERT OR REPLACE INTO` on `setItem()`, so\n// the ID of each key will change every time it's updated. Perhaps a stored\n// procedure for the `setItem()` SQL would solve this problem?\n// TODO: Don't change ID on `setItem()`.\nfunction key$1(n, callback) {\n var self = this;\n\n var promise = new Promise$1(function (resolve, reject) {\n self.ready().then(function () {\n var dbInfo = self._dbInfo;\n dbInfo.db.transaction(function (t) {\n tryExecuteSql(t, dbInfo, 'SELECT key FROM ' + dbInfo.storeName + ' WHERE id = ? LIMIT 1', [n + 1], function (t, results) {\n var result = results.rows.length ? results.rows.item(0).key : null;\n resolve(result);\n }, function (t, error) {\n reject(error);\n });\n });\n })[\"catch\"](reject);\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\nfunction keys$1(callback) {\n var self = this;\n\n var promise = new Promise$1(function (resolve, reject) {\n self.ready().then(function () {\n var dbInfo = self._dbInfo;\n dbInfo.db.transaction(function (t) {\n tryExecuteSql(t, dbInfo, 'SELECT key FROM ' + dbInfo.storeName, [], function (t, results) {\n var keys = [];\n\n for (var i = 0; i < results.rows.length; i++) {\n keys.push(results.rows.item(i).key);\n }\n\n resolve(keys);\n }, function (t, error) {\n reject(error);\n });\n });\n })[\"catch\"](reject);\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\n// https://www.w3.org/TR/webdatabase/#databases\n// > There is no way to enumerate or delete the databases available for an origin from this API.\nfunction getAllStoreNames(db) {\n return new Promise$1(function (resolve, reject) {\n db.transaction(function (t) {\n t.executeSql('SELECT name FROM sqlite_master ' + \"WHERE type='table' AND name <> '__WebKitDatabaseInfoTable__'\", [], function (t, results) {\n var storeNames = [];\n\n for (var i = 0; i < results.rows.length; i++) {\n storeNames.push(results.rows.item(i).name);\n }\n\n resolve({\n db: db,\n storeNames: storeNames\n });\n }, function (t, error) {\n reject(error);\n });\n }, function (sqlError) {\n reject(sqlError);\n });\n });\n}\n\nfunction dropInstance$1(options, callback) {\n callback = getCallback.apply(this, arguments);\n\n var currentConfig = this.config();\n options = typeof options !== 'function' && options || {};\n if (!options.name) {\n options.name = options.name || currentConfig.name;\n options.storeName = options.storeName || currentConfig.storeName;\n }\n\n var self = this;\n var promise;\n if (!options.name) {\n promise = Promise$1.reject('Invalid arguments');\n } else {\n promise = new Promise$1(function (resolve) {\n var db;\n if (options.name === currentConfig.name) {\n // use the db reference of the current instance\n db = self._dbInfo.db;\n } else {\n db = openDatabase(options.name, '', '', 0);\n }\n\n if (!options.storeName) {\n // drop all database tables\n resolve(getAllStoreNames(db));\n } else {\n resolve({\n db: db,\n storeNames: [options.storeName]\n });\n }\n }).then(function (operationInfo) {\n return new Promise$1(function (resolve, reject) {\n operationInfo.db.transaction(function (t) {\n function dropTable(storeName) {\n return new Promise$1(function (resolve, reject) {\n t.executeSql('DROP TABLE IF EXISTS ' + storeName, [], function () {\n resolve();\n }, function (t, error) {\n reject(error);\n });\n });\n }\n\n var operations = [];\n for (var i = 0, len = operationInfo.storeNames.length; i < len; i++) {\n operations.push(dropTable(operationInfo.storeNames[i]));\n }\n\n Promise$1.all(operations).then(function () {\n resolve();\n })[\"catch\"](function (e) {\n reject(e);\n });\n }, function (sqlError) {\n reject(sqlError);\n });\n });\n });\n }\n\n executeCallback(promise, callback);\n return promise;\n}\n\nvar webSQLStorage = {\n _driver: 'webSQLStorage',\n _initStorage: _initStorage$1,\n _support: isWebSQLValid(),\n iterate: iterate$1,\n getItem: getItem$1,\n setItem: setItem$1,\n removeItem: removeItem$1,\n clear: clear$1,\n length: length$1,\n key: key$1,\n keys: keys$1,\n dropInstance: dropInstance$1\n};\n\nfunction isLocalStorageValid() {\n try {\n return typeof localStorage !== 'undefined' && 'setItem' in localStorage &&\n // in IE8 typeof localStorage.setItem === 'object'\n !!localStorage.setItem;\n } catch (e) {\n return false;\n }\n}\n\nfunction _getKeyPrefix(options, defaultConfig) {\n var keyPrefix = options.name + '/';\n\n if (options.storeName !== defaultConfig.storeName) {\n keyPrefix += options.storeName + '/';\n }\n return keyPrefix;\n}\n\n// Check if localStorage throws when saving an item\nfunction checkIfLocalStorageThrows() {\n var localStorageTestKey = '_localforage_support_test';\n\n try {\n localStorage.setItem(localStorageTestKey, true);\n localStorage.removeItem(localStorageTestKey);\n\n return false;\n } catch (e) {\n return true;\n }\n}\n\n// Check if localStorage is usable and allows to save an item\n// This method checks if localStorage is usable in Safari Private Browsing\n// mode, or in any other case where the available quota for localStorage\n// is 0 and there wasn't any saved items yet.\nfunction _isLocalStorageUsable() {\n return !checkIfLocalStorageThrows() || localStorage.length > 0;\n}\n\n// Config the localStorage backend, using options set in the config.\nfunction _initStorage$2(options) {\n var self = this;\n var dbInfo = {};\n if (options) {\n for (var i in options) {\n dbInfo[i] = options[i];\n }\n }\n\n dbInfo.keyPrefix = _getKeyPrefix(options, self._defaultConfig);\n\n if (!_isLocalStorageUsable()) {\n return Promise$1.reject();\n }\n\n self._dbInfo = dbInfo;\n dbInfo.serializer = localforageSerializer;\n\n return Promise$1.resolve();\n}\n\n// Remove all keys from the datastore, effectively destroying all data in\n// the app's key/value store!\nfunction clear$2(callback) {\n var self = this;\n var promise = self.ready().then(function () {\n var keyPrefix = self._dbInfo.keyPrefix;\n\n for (var i = localStorage.length - 1; i >= 0; i--) {\n var key = localStorage.key(i);\n\n if (key.indexOf(keyPrefix) === 0) {\n localStorage.removeItem(key);\n }\n }\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\n// Retrieve an item from the store. Unlike the original async_storage\n// library in Gaia, we don't modify return values at all. If a key's value\n// is `undefined`, we pass that value to the callback function.\nfunction getItem$2(key, callback) {\n var self = this;\n\n key = normalizeKey(key);\n\n var promise = self.ready().then(function () {\n var dbInfo = self._dbInfo;\n var result = localStorage.getItem(dbInfo.keyPrefix + key);\n\n // If a result was found, parse it from the serialized\n // string into a JS object. If result isn't truthy, the key\n // is likely undefined and we'll pass it straight to the\n // callback.\n if (result) {\n result = dbInfo.serializer.deserialize(result);\n }\n\n return result;\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\n// Iterate over all items in the store.\nfunction iterate$2(iterator, callback) {\n var self = this;\n\n var promise = self.ready().then(function () {\n var dbInfo = self._dbInfo;\n var keyPrefix = dbInfo.keyPrefix;\n var keyPrefixLength = keyPrefix.length;\n var length = localStorage.length;\n\n // We use a dedicated iterator instead of the `i` variable below\n // so other keys we fetch in localStorage aren't counted in\n // the `iterationNumber` argument passed to the `iterate()`\n // callback.\n //\n // See: github.com/mozilla/localForage/pull/435#discussion_r38061530\n var iterationNumber = 1;\n\n for (var i = 0; i < length; i++) {\n var key = localStorage.key(i);\n if (key.indexOf(keyPrefix) !== 0) {\n continue;\n }\n var value = localStorage.getItem(key);\n\n // If a result was found, parse it from the serialized\n // string into a JS object. If result isn't truthy, the\n // key is likely undefined and we'll pass it straight\n // to the iterator.\n if (value) {\n value = dbInfo.serializer.deserialize(value);\n }\n\n value = iterator(value, key.substring(keyPrefixLength), iterationNumber++);\n\n if (value !== void 0) {\n return value;\n }\n }\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\n// Same as localStorage's key() method, except takes a callback.\nfunction key$2(n, callback) {\n var self = this;\n var promise = self.ready().then(function () {\n var dbInfo = self._dbInfo;\n var result;\n try {\n result = localStorage.key(n);\n } catch (error) {\n result = null;\n }\n\n // Remove the prefix from the key, if a key is found.\n if (result) {\n result = result.substring(dbInfo.keyPrefix.length);\n }\n\n return result;\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\nfunction keys$2(callback) {\n var self = this;\n var promise = self.ready().then(function () {\n var dbInfo = self._dbInfo;\n var length = localStorage.length;\n var keys = [];\n\n for (var i = 0; i < length; i++) {\n var itemKey = localStorage.key(i);\n if (itemKey.indexOf(dbInfo.keyPrefix) === 0) {\n keys.push(itemKey.substring(dbInfo.keyPrefix.length));\n }\n }\n\n return keys;\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\n// Supply the number of keys in the datastore to the callback function.\nfunction length$2(callback) {\n var self = this;\n var promise = self.keys().then(function (keys) {\n return keys.length;\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\n// Remove an item from the store, nice and simple.\nfunction removeItem$2(key, callback) {\n var self = this;\n\n key = normalizeKey(key);\n\n var promise = self.ready().then(function () {\n var dbInfo = self._dbInfo;\n localStorage.removeItem(dbInfo.keyPrefix + key);\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\n// Set a key's value and run an optional callback once the value is set.\n// Unlike Gaia's implementation, the callback function is passed the value,\n// in case you want to operate on that value only after you're sure it\n// saved, or something like that.\nfunction setItem$2(key, value, callback) {\n var self = this;\n\n key = normalizeKey(key);\n\n var promise = self.ready().then(function () {\n // Convert undefined values to null.\n // https://github.com/mozilla/localForage/pull/42\n if (value === undefined) {\n value = null;\n }\n\n // Save the original value to pass to the callback.\n var originalValue = value;\n\n return new Promise$1(function (resolve, reject) {\n var dbInfo = self._dbInfo;\n dbInfo.serializer.serialize(value, function (value, error) {\n if (error) {\n reject(error);\n } else {\n try {\n localStorage.setItem(dbInfo.keyPrefix + key, value);\n resolve(originalValue);\n } catch (e) {\n // localStorage capacity exceeded.\n // TODO: Make this a specific error/event.\n if (e.name === 'QuotaExceededError' || e.name === 'NS_ERROR_DOM_QUOTA_REACHED') {\n reject(e);\n }\n reject(e);\n }\n }\n });\n });\n });\n\n executeCallback(promise, callback);\n return promise;\n}\n\nfunction dropInstance$2(options, callback) {\n callback = getCallback.apply(this, arguments);\n\n options = typeof options !== 'function' && options || {};\n if (!options.name) {\n var currentConfig = this.config();\n options.name = options.name || currentConfig.name;\n options.storeName = options.storeName || currentConfig.storeName;\n }\n\n var self = this;\n var promise;\n if (!options.name) {\n promise = Promise$1.reject('Invalid arguments');\n } else {\n promise = new Promise$1(function (resolve) {\n if (!options.storeName) {\n resolve(options.name + '/');\n } else {\n resolve(_getKeyPrefix(options, self._defaultConfig));\n }\n }).then(function (keyPrefix) {\n for (var i = localStorage.length - 1; i >= 0; i--) {\n var key = localStorage.key(i);\n\n if (key.indexOf(keyPrefix) === 0) {\n localStorage.removeItem(key);\n }\n }\n });\n }\n\n executeCallback(promise, callback);\n return promise;\n}\n\nvar localStorageWrapper = {\n _driver: 'localStorageWrapper',\n _initStorage: _initStorage$2,\n _support: isLocalStorageValid(),\n iterate: iterate$2,\n getItem: getItem$2,\n setItem: setItem$2,\n removeItem: removeItem$2,\n clear: clear$2,\n length: length$2,\n key: key$2,\n keys: keys$2,\n dropInstance: dropInstance$2\n};\n\nvar sameValue = function sameValue(x, y) {\n return x === y || typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y);\n};\n\nvar includes = function includes(array, searchElement) {\n var len = array.length;\n var i = 0;\n while (i < len) {\n if (sameValue(array[i], searchElement)) {\n return true;\n }\n i++;\n }\n\n return false;\n};\n\nvar isArray = Array.isArray || function (arg) {\n return Object.prototype.toString.call(arg) === '[object Array]';\n};\n\n// Drivers are stored here when `defineDriver()` is called.\n// They are shared across all instances of localForage.\nvar DefinedDrivers = {};\n\nvar DriverSupport = {};\n\nvar DefaultDrivers = {\n INDEXEDDB: asyncStorage,\n WEBSQL: webSQLStorage,\n LOCALSTORAGE: localStorageWrapper\n};\n\nvar DefaultDriverOrder = [DefaultDrivers.INDEXEDDB._driver, DefaultDrivers.WEBSQL._driver, DefaultDrivers.LOCALSTORAGE._driver];\n\nvar OptionalDriverMethods = ['dropInstance'];\n\nvar LibraryMethods = ['clear', 'getItem', 'iterate', 'key', 'keys', 'length', 'removeItem', 'setItem'].concat(OptionalDriverMethods);\n\nvar DefaultConfig = {\n description: '',\n driver: DefaultDriverOrder.slice(),\n name: 'localforage',\n // Default DB size is _JUST UNDER_ 5MB, as it's the highest size\n // we can use without a prompt.\n size: 4980736,\n storeName: 'keyvaluepairs',\n version: 1.0\n};\n\nfunction callWhenReady(localForageInstance, libraryMethod) {\n localForageInstance[libraryMethod] = function () {\n var _args = arguments;\n return localForageInstance.ready().then(function () {\n return localForageInstance[libraryMethod].apply(localForageInstance, _args);\n });\n };\n}\n\nfunction extend() {\n for (var i = 1; i < arguments.length; i++) {\n var arg = arguments[i];\n\n if (arg) {\n for (var _key in arg) {\n if (arg.hasOwnProperty(_key)) {\n if (isArray(arg[_key])) {\n arguments[0][_key] = arg[_key].slice();\n } else {\n arguments[0][_key] = arg[_key];\n }\n }\n }\n }\n }\n\n return arguments[0];\n}\n\nvar LocalForage = function () {\n function LocalForage(options) {\n _classCallCheck(this, LocalForage);\n\n for (var driverTypeKey in DefaultDrivers) {\n if (DefaultDrivers.hasOwnProperty(driverTypeKey)) {\n var driver = DefaultDrivers[driverTypeKey];\n var driverName = driver._driver;\n this[driverTypeKey] = driverName;\n\n if (!DefinedDrivers[driverName]) {\n // we don't need to wait for the promise,\n // since the default drivers can be defined\n // in a blocking manner\n this.defineDriver(driver);\n }\n }\n }\n\n this._defaultConfig = extend({}, DefaultConfig);\n this._config = extend({}, this._defaultConfig, options);\n this._driverSet = null;\n this._initDriver = null;\n this._ready = false;\n this._dbInfo = null;\n\n this._wrapLibraryMethodsWithReady();\n this.setDriver(this._config.driver)[\"catch\"](function () {});\n }\n\n // Set any config values for localForage; can be called anytime before\n // the first API call (e.g. `getItem`, `setItem`).\n // We loop through options so we don't overwrite existing config\n // values.\n\n\n LocalForage.prototype.config = function config(options) {\n // If the options argument is an object, we use it to set values.\n // Otherwise, we return either a specified config value or all\n // config values.\n if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') {\n // If localforage is ready and fully initialized, we can't set\n // any new configuration values. Instead, we return an error.\n if (this._ready) {\n return new Error(\"Can't call config() after localforage \" + 'has been used.');\n }\n\n for (var i in options) {\n if (i === 'storeName') {\n options[i] = options[i].replace(/\\W/g, '_');\n }\n\n if (i === 'version' && typeof options[i] !== 'number') {\n return new Error('Database version must be a number.');\n }\n\n this._config[i] = options[i];\n }\n\n // after all config options are set and\n // the driver option is used, try setting it\n if ('driver' in options && options.driver) {\n return this.setDriver(this._config.driver);\n }\n\n return true;\n } else if (typeof options === 'string') {\n return this._config[options];\n } else {\n return this._config;\n }\n };\n\n // Used to define a custom driver, shared across all instances of\n // localForage.\n\n\n LocalForage.prototype.defineDriver = function defineDriver(driverObject, callback, errorCallback) {\n var promise = new Promise$1(function (resolve, reject) {\n try {\n var driverName = driverObject._driver;\n var complianceError = new Error('Custom driver not compliant; see ' + 'https://mozilla.github.io/localForage/#definedriver');\n\n // A driver name should be defined and not overlap with the\n // library-defined, default drivers.\n if (!driverObject._driver) {\n reject(complianceError);\n return;\n }\n\n var driverMethods = LibraryMethods.concat('_initStorage');\n for (var i = 0, len = driverMethods.length; i < len; i++) {\n var driverMethodName = driverMethods[i];\n\n // when the property is there,\n // it should be a method even when optional\n var isRequired = !includes(OptionalDriverMethods, driverMethodName);\n if ((isRequired || driverObject[driverMethodName]) && typeof driverObject[driverMethodName] !== 'function') {\n reject(complianceError);\n return;\n }\n }\n\n var configureMissingMethods = function configureMissingMethods() {\n var methodNotImplementedFactory = function methodNotImplementedFactory(methodName) {\n return function () {\n var error = new Error('Method ' + methodName + ' is not implemented by the current driver');\n var promise = Promise$1.reject(error);\n executeCallback(promise, arguments[arguments.length - 1]);\n return promise;\n };\n };\n\n for (var _i = 0, _len = OptionalDriverMethods.length; _i < _len; _i++) {\n var optionalDriverMethod = OptionalDriverMethods[_i];\n if (!driverObject[optionalDriverMethod]) {\n driverObject[optionalDriverMethod] = methodNotImplementedFactory(optionalDriverMethod);\n }\n }\n };\n\n configureMissingMethods();\n\n var setDriverSupport = function setDriverSupport(support) {\n if (DefinedDrivers[driverName]) {\n console.info('Redefining LocalForage driver: ' + driverName);\n }\n DefinedDrivers[driverName] = driverObject;\n DriverSupport[driverName] = support;\n // don't use a then, so that we can define\n // drivers that have simple _support methods\n // in a blocking manner\n resolve();\n };\n\n if ('_support' in driverObject) {\n if (driverObject._support && typeof driverObject._support === 'function') {\n driverObject._support().then(setDriverSupport, reject);\n } else {\n setDriverSupport(!!driverObject._support);\n }\n } else {\n setDriverSupport(true);\n }\n } catch (e) {\n reject(e);\n }\n });\n\n executeTwoCallbacks(promise, callback, errorCallback);\n return promise;\n };\n\n LocalForage.prototype.driver = function driver() {\n return this._driver || null;\n };\n\n LocalForage.prototype.getDriver = function getDriver(driverName, callback, errorCallback) {\n var getDriverPromise = DefinedDrivers[driverName] ? Promise$1.resolve(DefinedDrivers[driverName]) : Promise$1.reject(new Error('Driver not found.'));\n\n executeTwoCallbacks(getDriverPromise, callback, errorCallback);\n return getDriverPromise;\n };\n\n LocalForage.prototype.getSerializer = function getSerializer(callback) {\n var serializerPromise = Promise$1.resolve(localforageSerializer);\n executeTwoCallbacks(serializerPromise, callback);\n return serializerPromise;\n };\n\n LocalForage.prototype.ready = function ready(callback) {\n var self = this;\n\n var promise = self._driverSet.then(function () {\n if (self._ready === null) {\n self._ready = self._initDriver();\n }\n\n return self._ready;\n });\n\n executeTwoCallbacks(promise, callback, callback);\n return promise;\n };\n\n LocalForage.prototype.setDriver = function setDriver(drivers, callback, errorCallback) {\n var self = this;\n\n if (!isArray(drivers)) {\n drivers = [drivers];\n }\n\n var supportedDrivers = this._getSupportedDrivers(drivers);\n\n function setDriverToConfig() {\n self._config.driver = self.driver();\n }\n\n function extendSelfWithDriver(driver) {\n self._extend(driver);\n setDriverToConfig();\n\n self._ready = self._initStorage(self._config);\n return self._ready;\n }\n\n function initDriver(supportedDrivers) {\n return function () {\n var currentDriverIndex = 0;\n\n function driverPromiseLoop() {\n while (currentDriverIndex < supportedDrivers.length) {\n var driverName = supportedDrivers[currentDriverIndex];\n currentDriverIndex++;\n\n self._dbInfo = null;\n self._ready = null;\n\n return self.getDriver(driverName).then(extendSelfWithDriver)[\"catch\"](driverPromiseLoop);\n }\n\n setDriverToConfig();\n var error = new Error('No available storage method found.');\n self._driverSet = Promise$1.reject(error);\n return self._driverSet;\n }\n\n return driverPromiseLoop();\n };\n }\n\n // There might be a driver initialization in progress\n // so wait for it to finish in order to avoid a possible\n // race condition to set _dbInfo\n var oldDriverSetDone = this._driverSet !== null ? this._driverSet[\"catch\"](function () {\n return Promise$1.resolve();\n }) : Promise$1.resolve();\n\n this._driverSet = oldDriverSetDone.then(function () {\n var driverName = supportedDrivers[0];\n self._dbInfo = null;\n self._ready = null;\n\n return self.getDriver(driverName).then(function (driver) {\n self._driver = driver._driver;\n setDriverToConfig();\n self._wrapLibraryMethodsWithReady();\n self._initDriver = initDriver(supportedDrivers);\n });\n })[\"catch\"](function () {\n setDriverToConfig();\n var error = new Error('No available storage method found.');\n self._driverSet = Promise$1.reject(error);\n return self._driverSet;\n });\n\n executeTwoCallbacks(this._driverSet, callback, errorCallback);\n return this._driverSet;\n };\n\n LocalForage.prototype.supports = function supports(driverName) {\n return !!DriverSupport[driverName];\n };\n\n LocalForage.prototype._extend = function _extend(libraryMethodsAndProperties) {\n extend(this, libraryMethodsAndProperties);\n };\n\n LocalForage.prototype._getSupportedDrivers = function _getSupportedDrivers(drivers) {\n var supportedDrivers = [];\n for (var i = 0, len = drivers.length; i < len; i++) {\n var driverName = drivers[i];\n if (this.supports(driverName)) {\n supportedDrivers.push(driverName);\n }\n }\n return supportedDrivers;\n };\n\n LocalForage.prototype._wrapLibraryMethodsWithReady = function _wrapLibraryMethodsWithReady() {\n // Add a stub for each driver API method that delays the call to the\n // corresponding driver method until localForage is ready. These stubs\n // will be replaced by the driver methods as soon as the driver is\n // loaded, so there is no performance impact.\n for (var i = 0, len = LibraryMethods.length; i < len; i++) {\n callWhenReady(this, LibraryMethods[i]);\n }\n };\n\n LocalForage.prototype.createInstance = function createInstance(options) {\n return new LocalForage(options);\n };\n\n return LocalForage;\n}();\n\n// The actual localForage object that we expose as a module or via a\n// global. It's extended by pulling in one of our other libraries.\n\n\nvar localforage_js = new LocalForage();\n\nmodule.exports = localforage_js;\n\n},{\"3\":3}]},{},[4])(4)\n});\n", "\n// 8bitworkshop IDE user interface\n\nimport * as localforage from \"localforage\";\nimport { BaseDebugPlatform, DebugEvalCondition, DebugSymbols, EmuState, isDebuggable, Platform, Preset } from \"../common/baseplatform\";\nimport { EmuHalt, PLATFORMS } from \"../common/emu\";\nimport { StateRecorderImpl } from \"../common/recorder\";\nimport {\n arrayCompare, byteArrayToUTF8, decodeQueryString, getBasePlatform, getCookie, getFilenameForPath, getFilenamePrefix,\n getRootBasePlatform, getWithBinary, hex, highlightDifferences, isProbablyBinary, isProductionHost, loadScript, parseBool, stringToByteArray\n} from \"../common/util\";\nimport { getSkeletonName, getPlatformToolHelpURL, getToolMeta, TOOL_META } from \"../common/toolmeta\";\nimport { PLATFORM_PARAMS } from \"../worker/platforms\";\nimport { CodeListingMap, FileData, WorkerError, WorkerResult } from \"../common/workertypes\";\nimport { reportErrorToServer } from \"./errorreport\";\nimport { importPlatform } from \"../platform/_index\";\nimport { alertError, alertInfo, fatalError, setWaitDialog } from \"./dialogs\";\nimport { openSettings, loadSettings } from \"./settings\";\nimport { getPersistStatusMessage, getQuotaExceededMessage, requestPersistentStorage } from \"./storage\";\nimport { CodeProject, createNewPersistentStore, LocalForageFilesystem, OverlayFilesystem, ProjectFilesystem, WebPresetsFileSystem } from \"./project\";\nimport { getRepos, parseGithubURL } from \"./services\";\nimport { _downloadAllFilesZipFile, _downloadCassetteFile, _downloadProjectZipFile, _downloadROMImage, _downloadSourceFile, _downloadSymFile, _getCassetteFunction, _recordVideo, _shareEmbedLink } from \"./shareexport\";\nimport { _importProjectFromGithub, _loginToGithub, _logoutOfGithub, _publishProjectToGithub, _pullProjectFromGithub, _pushProjectToGithub, _removeRepository, importProjectFromGithub } from \"./sync\";\nimport { Toolbar } from \"./toolbar\";\nimport { Shortcut, initShortcutBar, setGlobalShortcutsFn, setViewShortcutsFn, setBarVisible, refreshShortcutBar } from \"./shortcutbar\";\nimport { openSearchDialog } from \"./search/searchview\";\nimport { resolveDebuggerTarget } from \"./search/debuggerhit\";\nimport { setProjectProvider } from \"./search/projectsource\";\nimport { AssetEditorView } from \"./views/asseteditor\";\nimport { isMobileDevice } from \"./views/baseviews\";\nimport { AddressHeatMapView, BinaryFileView, BreakpointsView, MemoryMapView, MemoryView, ProbeLogView, ProbeSymbolView, RasterStackMapView, ScanlineIOView, VRAMMemoryView } from \"./views/debugviews\";\nimport { DisassemblerView, HeaderView, ListingView, PC_LINE_LOOKAHEAD, SourceEditor, setUppercaseOnly } from \"./views/editors\";\nimport { HELP_TOPICS, HelpView, elementHelpTopicForFocus, helpTopicForView } from \"./views/helpview\";\nimport { CallStackView, DebugBrowserView } from \"./views/treeviews\";\nimport { ProjectWindows } from \"./windows\";\nimport { setupSplits, showTab } from \"./layout\";\nexport { setupSplits, showTab };\nimport { bpStore, resolveBreakpoints } from \"./breakpoints\";\nimport { CondFn } from \"./breakcond\";\nimport { findListingLocation as findListingLocationPure } from \"./search/listinglocation\";\nimport DOMPurify = require(\"dompurify\");\n\n// external libs (TODO)\ndeclare var Tour;\ndeclare var $: JQueryStatic; // use browser jquery\n\n// query string\n\ninterface UIQueryString {\n platform?: string;\n options?: string;\n repo?: string;\n file?: string;\n electron?: string;\n importURL?: string;\n githubURL?: string;\n localfs?: string;\n newfile?: string;\n embed?: string;\n ignore?: string;\n force?: string;\n highlight?: string;\n file0_name?: string;\n file0_data?: string;\n file0_type?: string;\n tool?: string;\n}\n\n/// EXPORTED GLOBALS (TODO: remove)\n\nexport var qs = decodeQueryString(window.location.search || '?') as UIQueryString;\n\nexport var platform_id: string;\t// platform ID string (platform)\nexport var store_id: string;\t\t// store ID string (repo || platform)\nexport var repo_id: string;\t\t// repository ID (repo)\nexport var platform: Platform;\t\t// emulator object\nexport var current_project: CodeProject;\t// current CodeProject object\nexport var projectWindows: ProjectWindows;\t// window manager\nexport var lastDebugState: EmuState;\t// last debug state (object)\n\n// private globals\n\nvar compparams;\t\t\t// received build params from worker\nvar platform_name: string;\t// platform name (after setPlatformUI)\nvar toolbar = $(\"#controls_top\");\nvar uitoolbar: Toolbar;\nvar stateRecorder: StateRecorderImpl;\nvar userPaused: boolean;\t\t// did user explicitly pause?\nvar tabPausedEmulator = false;\t// did a tab switch (mobile tab mode) pause the emulator?\nvar debugSessionActive: boolean;\t// has the user entered a debug session? (drives debug shortcut chips)\nvar current_output: any; // current ROM (or other object)\nvar current_preset: Preset;\t// current preset object (if selected)\nvar store: LocalForage;\t\t\t// persistent store\n\nconst isElectron = parseBool(qs.electron);\nconst isEmbed = parseBool(qs.embed);\n\n\ntype DebugCommandType = null\n | 'toline' | 'step' | 'stepout' | 'stepover'\n | 'tovsync' | 'stepback' | 'restart';\n\nvar lastDebugInfo;\t\t// last debug info (CPU text)\nvar debugCategory;\t\t// current debug category\nvar debugTickPaused = false;\nvar recorderActive = false;\nvar lastViewClicked: string = null;\nvar lastDebugCommand: DebugCommandType = null;\nvar errorWasRuntime = false;\nvar lastBreakExpr = \"c.PC == 0x6000\";\n\nexport function getPlatformStore() {\n return store;\n}\nexport function getCurrentProject() {\n return current_project;\n}\nexport function getCurrentOutput() {\n return current_output;\n}\nexport function getWorkerParams() {\n return compparams;\n}\n\n// editor styles and help URLs live in the tool registry (src/common/toolmeta.ts)\n\nfunction newWorker(): Worker {\n // TODO: return new Worker(\"https://8bitworkshop.com.s3-website-us-east-1.amazonaws.com/dev/gen/worker/bundle.js\");\n return new Worker(\"./gen/worker/bundle.js\");\n}\n\nconst hasLocalStorage: boolean = function () {\n try {\n const key = \"__some_random_key_you_are_not_going_to_use__\";\n localStorage.setItem(key, key);\n var has = localStorage.getItem(key) == key;\n localStorage.removeItem(key);\n return has;\n } catch (e) {\n return false;\n }\n}();\n\n// wrapper for localstorage\nclass UserPrefs {\n setLastPreset(id: string) {\n if (hasLocalStorage && !isEmbed) {\n if (repo_id && platform_id && !isElectron)\n localStorage.setItem(\"__lastrepo_\" + platform_id, repo_id);\n else\n localStorage.removeItem(\"__lastrepo_\" + platform_id);\n localStorage.setItem(\"__lastplatform\", platform_id);\n localStorage.setItem(\"__lastid_\" + store_id, id);\n }\n }\n unsetLastPreset() {\n if (hasLocalStorage && !isEmbed) {\n delete qs.file;\n localStorage.removeItem(\"__lastid_\" + store_id);\n }\n }\n getLastPreset() {\n return hasLocalStorage && !isEmbed && localStorage.getItem(\"__lastid_\" + store_id);\n }\n getLastPlatformID() {\n return hasLocalStorage && !isEmbed && localStorage.getItem(\"__lastplatform\");\n }\n getLastRepoID(platform: string) {\n return hasLocalStorage && !isEmbed && platform && localStorage.getItem(\"__lastrepo_\" + platform);\n }\n shouldCompleteTour() {\n return hasLocalStorage && !isEmbed && !localStorage.getItem(\"8bitworkshop.hello\");\n }\n completedTour() {\n if (hasLocalStorage && !isEmbed) localStorage.setItem(\"8bitworkshop.hello\", \"true\");\n }\n // we only nag about non-persistent storage once per browser\n shouldWarnAboutPersistence() {\n return hasLocalStorage && !isEmbed && !localStorage.getItem(\"8bitworkshop.persistwarn\");\n }\n warnedAboutPersistence() {\n if (hasLocalStorage && !isEmbed) localStorage.setItem(\"8bitworkshop.persistwarn\", \"true\");\n }\n}\n\nvar userPrefs = new UserPrefs();\n\n// Ask the browser to keep our IndexedDB data around.\n// A denial is normal (Chrome/Safari decide on their own and never prompt), so\n// we treat it as advice rather than an error, and only say it once.\n// https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/persist\nasync function requestPersistPermission(interactive: boolean, failureonly: boolean) {\n if (isElectron) {\n // desktop version stores files in the local filesystem\n interactive && alertInfo(\"The desktop version keeps your files in the \\\"8bitworkshop\\\" directory, so browser storage permissions don't apply.\");\n return;\n }\n if (isEmbed) {\n // third-party frames are always denied, so don't even ask\n interactive && alertInfo(\"Embedded pages can't be granted persistent storage. Open 8bitworkshop in its own tab to keep your edits.\");\n return;\n }\n var status = await requestPersistentStorage();\n console.log(\"requestPersistPermission =\", status);\n if (!interactive) return;\n if (status.persisted && failureonly) return; // nothing worth interrupting for\n // don't repeat the bad news on every new file, only when the user asks\n if (!status.persisted && failureonly) {\n if (!userPrefs.shouldWarnAboutPersistence()) return;\n userPrefs.warnedAboutPersistence();\n }\n alertInfo(getPersistStatusMessage(status, true));\n}\n\nvar warnedQuotaExceeded = false;\n\nasync function storageQuotaExceeded() {\n if (warnedQuotaExceeded) return;\n warnedQuotaExceeded = true;\n // requesting persistence can expand the quota, so try that before complaining\n var status = await requestPersistentStorage();\n console.log(\"storageQuotaExceeded =\", status);\n alertError(getQuotaExceededMessage(status));\n}\n\nfunction getCurrentPresetTitle(): string {\n if (!current_preset)\n return current_project.mainPath || \"ROM\";\n else\n return current_preset.title || current_preset.name || current_project.mainPath || \"ROM\";\n}\n\nasync function newFilesystem() {\n var basefs: ProjectFilesystem = new WebPresetsFileSystem(platform_id);\n if (isElectron) {\n console.log('using electron with local filesystem', alternateLocalFilesystem);\n return new OverlayFilesystem(basefs, alternateLocalFilesystem);\n } else if (qs.localfs != null) {\n return new OverlayFilesystem(basefs, await getLocalFilesystem(qs.localfs));\n } else {\n return new OverlayFilesystem(basefs, new LocalForageFilesystem(store));\n }\n}\n\nasync function initProject() {\n var filesystem = await newFilesystem();\n current_project = new CodeProject(newWorker(), platform_id, platform, filesystem);\n setProjectProvider(() => current_project);\n current_project.remoteTool = qs.tool || null;\n projectWindows = new ProjectWindows($(\"#workspace\")[0] as HTMLElement, current_project);\n current_project.callbackBuildResult = (result: WorkerResult) => {\n setCompileOutput(result);\n };\n current_project.callbackBuildStatus = (busy: boolean) => {\n setBusyStatus(busy);\n };\n // Update file views when file contents change.\n current_project.onFileChanged = (path: string, data: FileData) => {\n var wnd = projectWindows.id2window[path];\n if (wnd) {\n if (wnd instanceof SourceEditor && typeof data === 'string') {\n wnd.setText(data);\n } else if (wnd instanceof BinaryFileView && data instanceof Uint8Array) {\n wnd.setData(data);\n } else {\n console.warn('onFileChanged: unknown view or data type');\n }\n }\n // Also refresh the asset editor if it's the active view.\n var assetWnd = projectWindows.id2window['#asseteditor'];\n if (assetWnd && assetWnd === projectWindows.getActive()) {\n assetWnd.refresh(true);\n }\n };\n}\n\nfunction setBusyStatus(busy: boolean) {\n if (busy) {\n toolbar.addClass(\"is-busy\");\n } else {\n toolbar.removeClass(\"is-busy\");\n }\n $('#compile_spinner').css('visibility', busy ? 'visible' : 'hidden');\n}\n\nfunction newDropdownListItem(id, text) {\n var li = document.createElement(\"li\");\n var a = document.createElement(\"a\");\n a.setAttribute(\"class\", \"dropdown-item\");\n var hash = id.startsWith('#') ? id : '#' + encodeURIComponent(id);\n a.setAttribute(\"href\", hash);\n if (id == projectWindows.getActiveID())\n $(a).addClass(\"dropdown-item-checked\");\n a.appendChild(document.createTextNode(text));\n li.appendChild(a);\n return { li, a };\n}\n\n// Toolchain headers opened via include badges: each gets its own window\n// entry (alongside the listings) so it shows in the window list.\nvar openHeaderViews: string[] = [];\n\nexport function openHeaderFile(fn: string) {\n if (!openHeaderViews.includes(fn)) {\n openHeaderViews.push(fn);\n projectWindows.setCreateFunc('#headerview/' + fn, () => new HeaderView(fn));\n refreshWindowList();\n }\n var hash = '#headerview/' + fn;\n if (window.location.hash !== hash) {\n window.location.hash = hash.substring(1);\n }\n}\n\nfunction refreshWindowList() {\n var ul = $(\"#windowMenuList\").empty();\n var separate = false;\n\n function addWindowItem(id, name, createfn) {\n if (separate) {\n ul.append(document.createElement(\"hr\"));\n separate = false;\n }\n let { li, a } = newDropdownListItem(id, name);\n ul.append(li);\n if (createfn) {\n var onopen = (id, wnd) => {\n ul.find('a').removeClass(\"dropdown-item-checked\");\n $(a).addClass(\"dropdown-item-checked\");\n };\n projectWindows.setCreateFunc(id, createfn);\n projectWindows.setShowFunc(id, onopen);\n $(a).click((e) => {\n lastViewClicked = id;\n });\n }\n }\n\n function loadEditor(path: string) {\n var tool = platform.getToolForFilename(path);\n // hack because .h files can be DASM or CC65\n if (tool == 'dasm' && path.endsWith(\".h\") && getCurrentMainFilename().endsWith(\".c\")) {\n tool = 'cc65';\n }\n var mode = tool && getToolMeta(tool)?.editorStyle;\n return new SourceEditor(path, mode);\n }\n\n function addEditorItem(id: string) {\n addWindowItem(id, getFilenameForPath(id), () => {\n var data = current_project.getFile(id);\n if (typeof data === 'string')\n return loadEditor(id);\n else if (data instanceof Uint8Array)\n return new BinaryFileView(id, data as Uint8Array);\n });\n }\n\n // add main file editor\n addEditorItem(current_project.mainPath);\n\n // add other source files\n current_project.iterateFiles((id, text) => {\n if (text && id != current_project.mainPath) {\n addEditorItem(id);\n }\n });\n\n // add listings\n separate = true;\n var listings = current_project.getListings();\n if (listings) {\n for (var lstfn in listings) {\n var lst = listings[lstfn];\n // add listing if source/assembly file exists and has text\n if ((lst.assemblyfile && lst.assemblyfile.text) || (lst.sourcefile && lst.sourcefile.text) || lst.text) {\n addWindowItem(lstfn, getFilenameForPath(lstfn), (path) => {\n return new ListingView(path);\n });\n }\n }\n }\n\n // add opened toolchain headers (from include badges)\n for (let hdrfn of openHeaderViews) {\n let hdrid = '#headerview/' + hdrfn;\n projectWindows.setCreateFunc(hdrid, () => new HeaderView(hdrfn));\n addWindowItem(hdrid, getFilenameForPath(hdrfn), () => {\n return new HeaderView(hdrfn);\n });\n }\n\n // add other tools\n separate = true;\n if (platform.disassemble && platform.saveState) {\n addWindowItem(\"#disasm\", \"Disassembly\", () => {\n return new DisassemblerView();\n });\n }\n if (platform.readAddress) {\n addWindowItem(\"#memory\", \"Memory Browser\", () => {\n return new MemoryView();\n });\n }\n if (current_project.segments && current_project.segments.length) {\n addWindowItem(\"#memmap\", \"Memory Map\", () => {\n return new MemoryMapView();\n });\n }\n if (platform.readVRAMAddress) {\n addWindowItem(\"#vram\", \"VRAM Browser\", () => {\n return new VRAMMemoryView();\n });\n }\n if (platform.startProbing) {\n addWindowItem(\"#memprobe\", \"Memory Probe\", () => {\n return new AddressHeatMapView();\n });\n // TODO: only if raster\n addWindowItem(\"#crtprobe\", \"CRT Probe\", () => {\n //return new RasterPCHeatMapView();\n return new RasterStackMapView();\n });\n addWindowItem(\"#probelog\", \"Probe Log\", () => {\n return new ProbeLogView();\n });\n addWindowItem(\"#scanlineio\", \"Scanline I/O\", () => {\n return new ScanlineIOView();\n });\n addWindowItem(\"#symbols\", \"Symbol Profiler\", () => {\n return new ProbeSymbolView();\n });\n addWindowItem(\"#callstack\", \"Call Stack\", () => {\n return new CallStackView();\n });\n /*\n addWindowItem(\"#framecalls\", \"Frame Profiler\", () => {\n return new FrameCallsView();\n });\n */\n }\n if (platform.getDebugTree) {\n addWindowItem(\"#debugtree\", \"Debug Tree\", () => {\n return new DebugBrowserView();\n });\n }\n if (platform.runEval || platform.runToPC) {\n addWindowItem(\"#breakpoints\", \"Breakpoints\", () => {\n return new BreakpointsView();\n });\n }\n addWindowItem('#asseteditor', 'Asset Editor', () => {\n return new AssetEditorView();\n });\n\n // Help: markdown docs shipped with the IDE. Only the index is listed in the\n // sidebar; every topic still gets a create fn so #help/ deep links work.\n //separate = true;\n addWindowItem('#help', 'Help', () => new HelpView('#help'));\n for (let topic of HELP_TOPICS) {\n projectWindows.setCreateFunc('#help/' + topic.id, () => new HelpView(topic.id));\n }\n}\n\nfunction highlightLines(path: string, hispec: string) {\n if (hispec) {\n var toks = qs.highlight.split(',');\n var start = parseInt(toks[0]) - 1;\n var end = parseInt(toks[1]) - 1;\n var editor = projectWindows.createOrShow(path) as SourceEditor;\n editor.highlightLines(start, end);\n }\n}\n\nasync function loadMainWindow(preset_id: string) {\n // pre-load dependencies so refreshWindowList sees all files\n var maindata = current_project.getFile(preset_id);\n if (typeof maindata === 'string') {\n await current_project.loadFileDependencies(maindata);\n }\n // we need this to build create functions for the editor\n refreshWindowList();\n // show main file\n projectWindows.createOrShow(preset_id);\n // build project\n current_project.setMainFile(preset_id);\n // highlighting?\n highlightLines(preset_id, qs.highlight);\n}\n\nasync function loadProject(preset_id: string) {\n // set current file ID\n // TODO: this is done twice (mainPath and mainpath!)\n current_project.mainPath = preset_id;\n // load persisted breakpoints for this platform/project\n bpStore.setContextFn(() => platform_id + '/' + current_project.mainPath);\n bpStore.load();\n userPrefs.setLastPreset(preset_id);\n // load files from storage or web URLs\n var result = await current_project.loadFiles([preset_id]);\n if (result && result.length) {\n // file found; continue\n await loadMainWindow(preset_id);\n } else {\n // file not found, look for skeleton file\n var tool = platform.getToolForFilename(preset_id);\n var skel = await getSkeletonFile(preset_id);\n current_project.filedata[preset_id] = skel || \"\\n\";\n await loadMainWindow(preset_id);\n // don't alert if we selected \"new file\" unless there's a problem\n if (!qs.newfile) {\n alertInfo(\"Could not find file \\\"\" + preset_id + \"\\\"

\" +\n \"Creating new \" + (skel ? tool : \"blank\") + \" file.\");\n } else if (!skel && !tool) {\n alertError(\"No build tool for \\\"\" + preset_id + \"\\\" on this platform; project will not build.\");\n } else if (!skel) {\n alertInfo(\"No skeleton file for \" + tool + \" on this platform; using blank file.\");\n } else {\n requestPersistPermission(true, true); // ask for persist permissions\n }\n delete qs.newfile;\n replaceURLState();\n }\n}\n\nfunction reloadProject(id: string) {\n // leave repository == '/'\n if (id == '/') {\n qs = { repo: '/' };\n } else if (id.indexOf('://') >= 0) {\n var urlparse = parseGithubURL(id);\n if (urlparse) {\n qs = { repo: urlparse.repopath };\n }\n } else {\n qs.platform = platform_id;\n qs.file = id;\n }\n gotoNewLocation();\n}\n\nasync function getSkeletonFile(fileid: string): Promise {\n var ext = getSkeletonName(platform.getToolForFilename(fileid));\n try {\n return await $.get(\"presets/\" + getBasePlatform(platform_id) + \"/skeleton.\" + ext, 'text');\n } catch (e) {\n console.log(e + \"\");\n return null;\n }\n}\n\nfunction checkEnteredFilename(fn: string): boolean {\n if (fn.indexOf(\" \") >= 0) {\n alertError(\"No spaces in filenames, please.\");\n return false;\n }\n return true;\n}\n\ninterface ToolExtensions {\n tool: string;\n extensions: string[];\n}\n\nasync function _getToolsWithSkeletons(extensions: string[]): Promise {\n var basePlatform = getBasePlatform(platform_id);\n // group extensions by tool id to avoid duplicate requests\n var toolToExts: { [tool: string]: string[] } = {};\n var toolOrder: string[] = [];\n for (var ext of extensions) {\n var tool = platform.getToolForFilename(\"test\" + ext);\n if (!toolToExts[tool]) {\n toolToExts[tool] = [];\n toolOrder.push(tool);\n }\n toolToExts[tool].push(ext);\n }\n // check each tool in parallel\n var checks = toolOrder.map(tool =>\n $.ajax({\n url: \"presets/\" + basePlatform + \"/skeleton.\" + getSkeletonName(tool),\n type: \"HEAD\"\n }).then(() => tool, () => null)\n );\n var validTools = await Promise.all(checks);\n var results: ToolExtensions[] = [];\n for (var validTool of validTools) {\n if (validTool != null) {\n results.push({ tool: validTool, extensions: toolToExts[validTool] });\n }\n }\n return results;\n}\n\nasync function _createNewFile(e) {\n // TODO: support spaces\n var toolExts = await _getToolsWithSkeletons(platform.getDefaultExtensions());\n var allExtensions: string[];\n var helpText: string;\n if (toolExts.length === 0) {\n allExtensions = platform.getDefaultExtensions();\n helpText = allExtensions.join(\" \");\n } else {\n allExtensions = toolExts.reduce((acc, te) => acc.concat(te.extensions), [] as string[]);\n helpText = toolExts.map(te => te.extensions.join(' ')).join(\"
\");\n }\n bootbox.prompt({\n title: \"Enter the name of your new main source file.

\" +\n \"\" + helpText + \"\",\n placeholder: \"newfile\" + allExtensions[0],\n callback: (filename) => {\n if (filename && filename.trim().length > 0) {\n if (!checkEnteredFilename(filename)) return;\n if (filename.indexOf(\".\") < 0) {\n filename += allExtensions[0];\n }\n var path = filename;\n qs.newfile = '1';\n reloadProject(path);\n }\n }\n } as any);\n return true;\n}\n\nfunction _uploadNewFile(e) {\n const uploadFileElem = $(``);\n const file = uploadFileElem[0] as HTMLInputElement;\n uploadFileElem.change((e) => { handleFileUpload(file.files) });\n uploadFileElem.click();\n}\n\n// called from index.html\nfunction handleFileUpload(files: FileList) {\n console.log(files);\n var index = 0;\n function uploadNextFile() {\n var f = files[index++];\n if (!f) {\n console.log(\"Done uploading\", index);\n if (index > 2) {\n alertInfo(\"Files uploaded.\");\n setTimeout(updateSelector, 1000); // TODO: wait for files to upload\n } else {\n qs.file = files[0].name;\n bootbox.confirm({\n message: \"Open '\" + DOMPurify.sanitize(qs.file) + \"' as main project file?\",\n buttons: {\n confirm: { label: \"Open As New Project\" },\n cancel: { label: \"Include/Link With Project Later\" },\n },\n callback: (result) => {\n if (result)\n gotoNewLocation();\n else\n setTimeout(updateSelector, 1000); // TODO: wait for files to upload\n }\n });\n }\n } else {\n var path = f.name;\n var reader = new FileReader();\n reader.onload = function (e) {\n var arrbuf = (e.target).result as ArrayBuffer;\n var data: FileData = new Uint8Array(arrbuf);\n // convert to UTF8, unless it's a binary file\n if (isProbablyBinary(path, data)) {\n //gotoMainFile = false;\n } else {\n data = byteArrayToUTF8(data).replace('\\r\\n', '\\n'); // convert CRLF to LF\n }\n // store in local forage\n projectWindows.updateFile(path, data);\n console.log(\"Uploaded \" + path + \" \" + data.length + \" bytes\");\n uploadNextFile();\n }\n reader.readAsArrayBuffer(f); // read as binary\n }\n }\n if (files) uploadNextFile();\n}\n\nasync function _openLocalDirectory(e) {\n var pickerfn = window['showDirectoryPicker'];\n if (!pickerfn) {\n alertError(`This browser can't open local files on your computer, yet. Try Chrome.`);\n }\n var dirHandle = await pickerfn();\n var repoid = dirHandle.name;\n var storekey = '__localfs__' + repoid;\n var fsdata = {\n handle: dirHandle,\n }\n var lstore = localforage.createInstance({\n name: storekey,\n version: 2.0\n });\n await lstore.setItem(storekey, fsdata);\n qs = { localfs: repoid };\n gotoNewLocation(true);\n}\n\nasync function promptUser(message: string): Promise {\n return new Promise((resolve, reject) => {\n bootbox.prompt(DOMPurify.sanitize(message), (result) => {\n resolve(result);\n });\n });\n}\n\nasync function getLocalFilesystem(repoid: string): Promise {\n const options = { mode: 'readwrite' };\n var storekey = '__localfs__' + repoid;\n var lstore = localforage.createInstance({\n name: storekey,\n version: 2.0\n });\n var fsdata: any = await lstore.getItem(storekey);\n var dirHandle = fsdata.handle as any;\n console.log(fsdata, dirHandle);\n var granted = await dirHandle.queryPermission(options);\n console.log(granted);\n if (granted !== 'granted') {\n await promptUser(`Request permissions to access filesystem?`);\n granted = await dirHandle.requestPermission(options);\n }\n if (granted !== 'granted') {\n alertError(`Could not get permission to access filesystem.`);\n return;\n }\n const lastWriteTime: { [path: string]: number } = {};\n return {\n getFileData: async (path) => {\n console.log('getFileData', path);\n let fileHandle = await dirHandle.getFileHandle(path, { create: false });\n let file = await fileHandle.getFile();\n let contents = await (isProbablyBinary(path) ? file.arrayBuffer() : file.text());\n if (contents instanceof ArrayBuffer) {\n return new Uint8Array(contents);\n }\n return contents;\n },\n setFileData: async (path, data) => {\n lastWriteTime[path] = Date.now();\n const fileHandle = await dirHandle.getFileHandle(path, { create: true });\n const writable = await fileHandle.createWritable();\n await writable.write(data);\n await writable.close();\n },\n onFileSystemUpdate: (callback: (path: string) => void) => {\n // Experimental API:\n // https://developer.mozilla.org/docs/Web/API/FileSystemObserver\n if (typeof (window as any).FileSystemObserver === 'undefined') {\n return;\n }\n const observer = new (window as any).FileSystemObserver((records, observer) => {\n for (const record of records) {\n // TODO Handle different types of changes intelligently.\n // https://developer.mozilla.org/docs/Web/API/FileSystemChangeRecord#type\n if (record.changedHandle) {\n const path = record.changedHandle.name;\n // Ignore filesystem notifications that are likely from our own recent writes,\n // so that onFileChanged in ui.ts doesn't call assets editor's refresh(true).\n // TODO: Consider better options than a time based threshold.\n if (Date.now() - (lastWriteTime[path] || 0) < 2000) continue;\n callback(path);\n }\n }\n });\n observer.observe(dirHandle, { recursive: true });\n }\n }\n}\n\nexport function getCurrentMainFilename(): string {\n return getFilenameForPath(current_project.mainPath);\n}\n\nexport function getCurrentEditorFilename(): string {\n return getFilenameForPath(projectWindows.getActiveID());\n}\n\n\nfunction _revertFile(e) {\n var wnd = projectWindows.getActive();\n var fn = projectWindows.getActiveID();\n var isBinary = wnd && wnd.setData && !wnd.setText;\n if (wnd && (wnd.setText || wnd.setData)) {\n var url = \"presets/\" + getBasePlatform(platform_id) + \"/\" + fn;\n getWithBinary(url, (data) => {\n if (data == null) {\n if (repo_id) alertError(\"Can only revert built-in examples. If you want to revert all files, You can pull from the repository.\");\n else alertError(\"Can only revert built-in examples.\");\n return;\n }\n bootbox.confirm(\"Reset '\" + DOMPurify.sanitize(fn) + \"' to default?\", (ok) => {\n if (ok) {\n if (isBinary) {\n wnd.setData(data as Uint8Array);\n current_project.updateFile(fn, data as Uint8Array);\n } else {\n wnd.setText(data as string);\n }\n }\n });\n }, isBinary ? 'arraybuffer' : 'text');\n } else {\n alertError(\"Cannot revert the active window. Please choose a text file.\");\n }\n}\n\nfunction _deleteFile(e) {\n var wnd = projectWindows.getActive();\n if (wnd && wnd.getPath) {\n var fn = projectWindows.getActiveID();\n bootbox.confirm(\"Delete '\" + DOMPurify.sanitize(fn) + \"'?\", (ok) => {\n if (ok) {\n store.removeItem(fn).then(() => {\n // if we delete what is selected\n if (qs.file == fn) {\n userPrefs.unsetLastPreset();\n gotoNewLocation();\n } else {\n updateSelector();\n alertInfo(\"Deleted \" + fn);\n }\n });\n }\n });\n } else {\n alertError(\"Cannot delete the active window.\");\n }\n}\n\nfunction _renameFile(e) {\n var wnd = projectWindows.getActive();\n if (wnd && wnd.getPath && current_project.getFile(wnd.getPath())) {\n var fn = projectWindows.getActiveID();\n bootbox.prompt({\n title: \"Rename '\" + DOMPurify.sanitize(fn) + \"' to?\",\n value: fn,\n callback: (newfn) => {\n var data = current_project.getFile(wnd.getPath());\n if (newfn && newfn != fn && data) {\n if (!checkEnteredFilename(newfn)) return;\n store.removeItem(fn).then(() => {\n return store.setItem(newfn, data);\n }).then(() => {\n updateSelector();\n alert(\"Renamed \" + fn + \" to \" + newfn); // need alert() so it pauses\n if (fn == current_project.mainPath) {\n reloadProject(newfn);\n }\n });\n }\n }\n });\n } else {\n alertError(\"Cannot rename the active window.\");\n }\n}\n\n\n\nfunction populateExamples(sel) {\n let files = {};\n let optgroup;\n const PRESETS = platform.getPresets ? platform.getPresets() : [];\n for (var i = 0; i < PRESETS.length; i++) {\n var preset = PRESETS[i];\n var name = preset.chapter ? (preset.chapter + \". \" + preset.name) : preset.name;\n var isCurrentPreset = preset.id == current_project.mainPath;\n if (preset.category) {\n optgroup = $(\"\").attr('label', 'Examples: ' + preset.category).appendTo(sel);\n } else if (!optgroup) {\n optgroup = $(\"\").attr('label', 'Examples').appendTo(sel);\n }\n optgroup.append($(\"\").attr('label', 'Repositories').appendTo(sel);\n for (let repopath in repos) {\n var repo = repos[repopath];\n if (repo.platform_id && getBasePlatform(repo.platform_id) == getBasePlatform(platform_id)) {\n optgroup.append($(\"\").attr('label', category).appendTo(sel);\n let name = key.substring(prefix.length);\n optgroup.append($(\"