{"version":3,"sources":["core/constants.ts","core/core.ts","core/config.ts","core/memory/load.ts","core/memory/memoryMap.ts","core/memory/memory.ts","core/memory/banking.ts","core/cpu/cpu.ts","core/memory/dma.ts","core/memory/store.ts","core/graphics/graphics.ts","core/graphics/palette.ts","core/sound/sound.ts","core/sound/channel1.ts","core/sound/channel2.ts","core/sound/channel3.ts","core/sound/channel4.ts","core/sound/accumulator.ts","core/timers/timers.ts","core/helpers/index.ts","core/cpu/opcodes.ts","core/portable/portable.ts","core/memory/writeTraps.ts","core/graphics/lcd.ts","core/sound/duty.ts","core/sound/registers.ts","core/memory/readTraps.ts","core/joypad/joypad.ts","core/interrupts/interrupts.ts","core/cpu/flags.ts","core/cpu/instructions.ts","core/cpu/cbOpcodes.ts","core/graphics/backgroundWindow.ts","core/graphics/tiles.ts","core/graphics/priority.ts","core/graphics/sprites.ts","core/debug/debug-cpu.ts","core/debug/debug-graphics.ts","core/legacy.ts"],"names":[],"mappings":"ygHMsGE,AAA0B,IACtB,AAAC,aAAL,IAA4C,AACzB,KAI2E,AAAjF,KAA0B,WAKoD,AAA9E,AAAS,EAAT,GAAiC,4BF/E9C,AAAiC,EAAiB,uCAQvB,AAAhB,SAOmC,AAAnC,WAKP,AAAsB,IACtB,AAAI,IAAgB,AAEL,AAAyB,IAAoC,MAKG,AAAxE,OAA+D,QAK5B,AAAnC,WAKqD,SAU5D,AAAsB,IACtB,AAAI,IAAgB,AACL,AAAyB,IAAoC,MAE5E,AAAI,EAAa,KAAG,AACL,KAKyF,AAAjG,EAAyE,sBDtFf,AAAhD,WI8GrB,AAAqB,IACrB,AAAgB,IAChB,AAAgB,IAChB,AAAgB,IAChB,AAAgB,IAChB,AAAgB,IAChB,AAAgB,IAChB,AAAgB,IAChB,AAAgB,IAChB,AAAmB,IACnB,AAAqB,IACrB,AAAoB,IACpB,AAAe,IACf,AAAgB,IAEhB,AAAI,IAEF,AAAgB,IAChB,AAAgB,KAChB,AAAgB,IAChB,AAAgB,IAChB,AAAgB,KAChB,AAAgB,KAChB,AAAgB,IAChB,AAAgB,KAOhB,AAAgB,IAChB,AAAgB,KAChB,AAAgB,IAChB,AAAgB,IAChB,AAAgB,IAChB,AAAgB,KAChB,AAAgB,IAChB,AAAgB,MAXhB,AAAqB,KACrB,AAAmB,YFVrB,AAA6B,IAC7B,AAA8B,IAI9B,AAAyB,AAAyB,OAGlD,AAAmB,IACnB,AAAgB,IAChB,AAAgB,IAChB,AAAgB,IAChB,AAAgB,IAEhB,IAEW,EAAiB,OAAQ,EAAiB,MAA9C,IAAoD,AACzC,KACP,EAAiB,OAAQ,EAAiB,MAA9C,IAAoD,AACzC,KACP,EAAiB,OAAQ,EAAiB,MAA9C,IAAoD,AACzC,KACP,EAAiB,OAAQ,EAAiB,MAA9C,IAAoD,AACzC,SATU,AACP,KAWrB,AAAwB,IACxB,AAAwB,OI3JxB,AAAU,YDGR,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,SEyHpC,AAAyB,IACzB,AAAgC,IAChC,AAA4B,IAC5B,AAAmB,IACnB,AAAmB,IACnB,AAAmB,IACnB,AAAmB,IAEnB,AAAI,IACF,AAA4B,KAC5B,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAElC,AAA0B,IAAQ,KAElC,AAA0B,IAAQ,MAOlC,AAA4B,KAC5B,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAElC,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,MAVlC,AAA0B,IAAQ,IAClC,AAA0B,IAAQ,OC3IpC,AAAI,IAEF,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAGlC,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,SEuHlC,IAAuD,KACvD,IAAuD,KACvD,IAAuD,KACvD,IAAuD,KACvD,IAAuD,QC5BvD,IAA2D,KAC3D,IAAuD,IACvD,IAAuD,IACvD,IAAuD,IACvD,IAAuD,QCdvD,IAAuD,KACvD,IAAuD,KACvD,IAAuD,KACvD,IAAuD,IACvD,IAAuD,KAGvD,AAA6B,OCoB7B,IAA2D,KAC3D,IAAuD,KACvD,IAAuD,IACvD,IAAuD,IACvD,IAAuD,QCnHzD,AAAkC,IAClC,AAAkC,IAClC,AAAkC,IAClC,AAAkC,IAClC,AAAsC,IACtC,AAAsC,IACtC,AAAsC,IACtC,AAAsC,IACtC,AAAiD,KACjD,AAAkD,KAClD,AAAsC,IACtC,AAAuC,IACvC,AAAsC,OLyGtC,AAAsB,IACtB,AAA4B,IAC5B,AAA6B,IAC7B,AAA0C,IAC1C,AAA0C,IAC1C,AAA0C,IAC1C,AAA0C,IAC1C,AAA2C,IAC3C,AAA2C,IAC3C,AAA2C,IAC3C,AAA2C,IAC3C,AAA2B,IAC3B,AAAkC,IAClC,AAA+B,IAC/B,AAAuB,IACvB,AAAwB,IAGf,EACA,EACA,EACA,EAGT,IAAoD,KACpD,IAAoD,KACpD,IAAoD,KAEpD,KOnHsC,AAA/B,EAAQ,AAAC,MAAsB,QDuJtC,AAAsB,KACtB,AAAI,IAAoB,AACT,MAEf,MAAQ,QAQD,OAQA,QAbH,AAAa,KACb,AAAI,IAAoB,AACT,MAER,IAIP,AAAa,IACb,AAAI,IAAoB,AACT,KAER,IAGP,AAAa,KACb,AAAI,IAAoB,AACT,MAER,SAtLT,AAAsB,AAAe,QACrC,AAAI,AAAC,OAIL,AAAyB,EAAQ,KAGjC,AAAsB,IACtB,AAA8B,OAkDhC,AAAuB,IACvB,AAAyB,IACzB,AAAsB,IACtB,AAAqB,IACrB,AAAsB,IACtB,AAAyB,IACzB,AAAsB,IACtB,AAAqC,IAErC,AAAI,IACF,AAA0B,IAAQ,IAClC,AAAyB,KAKzB,AAA0B,IAAQ,KAClC,AAAyB,MAJzB,AAA0B,IAAQ,KAC3B,AAAmB,WjBHxB,AAHJ,AAAmB,AAAyB,OAG5B,SAAQ,AAAC,IAA8B,EAAY,WAAnE,IAA0E,AACvD,KACZ,AACY,KAInB,EACA,EACA,EACA,EACA,EACA,EACA,EAGA,AAAI,IAEF,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAElC,AAA0B,IAAQ,KAElC,AAA0B,IAAQ,KAIlC,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,MAElC,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAClC,AAA0B,IAAQ,KAElC,AAA0B,IAAQ,KAElC,AAA0B,IAAQ,MAKpC,AAAa,QA/Gb,AAAI,EAAgB,KAAG,AACE,KAClB,AACkB,KAGzB,AAAI,EAAsB,KAAG,AACE,KACxB,AACwB,KAG/B,AAAI,EAAuB,KAAG,AACE,KACzB,AACyB,KAGhC,AAAI,EAA0B,KAAG,AACE,KAC5B,AAC4B,KAGnC,AAAI,EAAwB,KAAG,AACE,KAC1B,AAC0B,KAGjC,AAAI,EAAmC,KAAG,AACE,KACrC,AACqC,KAG5C,AAAI,EAAyB,KAAG,AACE,KAC3B,AAC2B,KAGlC,AAAI,EAAgB,KAAG,AACE,KAClB,AACkB,KAGzB,AAAI,EAAc,KAAG,AACE,KAChB,AACgB,KAGvB,KM1DE,AAAI,iBcxCS,UDkHwD,AAAnC,AAAY,EAAqB,UAJf,AAAlB,ODjHa,AAA1C,AAAC,EAAY,IAAS,GAAK,EAAW,QC0HkB,AAAlC,MAAkB,WDtHd,AAA1B,EAAe,KAAW,MAIZ,YbVrB,AAAI,MAKJ,AAAI,EAAU,MAAQ,AAChB,IAAiB,AAAC,AAAe,EAAG,kBAKtC,AADA,AAAoB,EAAQ,OAGrB,AAAI,EAAkB,KAAM,AACJ,MAHH,AACG,OAK5B,AAAI,EAAU,OACf,AAAC,QAAiB,EAAU,QADL,IAGzB,AAAI,IAAe,AACO,EAAQ,MAIlC,IACA,AAAI,IAEF,AAAmB,EAAmB,KACtC,AAAwB,EAAwB,OAC3C,AAAI,IAET,AAAmB,EAAmB,MACtC,AAAwB,EAAwB,OAC3C,AAAI,IAAe,OAM1B,AAAwB,QAIxB,AAAoB,IACpB,AAAmB,AAAa,MAChC,AAAI,EAAQ,KAAG,AACF,KAEb,AAAwB,UAEjB,AAAC,OAAiB,EAAU,QAAhC,IAEL,AAAI,IAAiB,UAGnB,AAAwB,EAAwB,KAIhD,AAAwB,EAFA,EAAQ,OAGhC,EAGF,AAAI,IACE,EAAS,OAAQ,EAAS,OAKhC,IAWA,AATA,AAAK,IAKW,EAAc,IAHd,EAAc,SASrB,AAAC,OAAiB,EAAU,QAAhC,IAAwC,AACzC,IAAe,AACb,AAAe,EAAG,UAAY,AACF,KACzB,AACyB,aMtDlC,AAAI,eAmDJ,AAAI,mBC0MA,EAAyB,OAAK,KAAlC,IAA8D,AAC5D,EAA0B,MAG5B,AAAI,KAA8B,AACX,UCnEnB,EAAyB,OAAK,KAAlC,IAA8D,AAC5D,EAA0B,MAG5B,AAAI,KAA8B,AACX,UC+BnB,EAAyB,OAAK,KAAlC,IAA8D,AAC5D,EAA0B,MAG5B,AAAI,KAA8B,AACX,UCPnB,EAAyB,OAAK,MAAlC,IAA8D,AAC5D,EAA0B,MAG5B,AAAI,KAA8B,AACX,WHwGzB,AAAe,AADS,GACO,MAG/B,AAAI,KACa,OAEA,cAjDf,AAAwB,AAFH,MAEe,MAIpC,IATA,AAAkC,EAAY,QAU9C,IAJe,EAPf,AAAmC,EAAa,QAchD,KACA,KACA,AAAqB,AAAC,GAA6B,GAAK,YAStD,AAFJ,AAAwB,IAEJ,QAAS,GAA0B,MAAvD,IAME,KACS,IAGT,AAAe,KAKjB,AAAI,EAAe,MAAO,AACH,QAzFrB,GAAyB,MAEzB,AAAI,GAAyB,KAE3B,AAAwB,MAKxB,AAAI,KAA2B,GAA2B,UAAG,SAoB/D,GAA4B,MAC5B,AAAI,GAA4B,KAC9B,AAA2B,MAK3B,AAAI,KAAgC,AAC9B,KAAgC,GAAkB,YAAI,AACxD,GAAmB,OACV,AAAC,QAAgC,GAAkB,MAAvD,IAA0D,AAC/D,GAAmB,eCzEzB,GAA4B,MAC5B,AAAI,GAA4B,KAC9B,AAA2B,MAI3B,AAAI,KAAgC,AAC9B,KAAgC,GAAkB,YAAI,AACxD,GAAmB,OACV,AAAC,QAAgC,GAAkB,MAAvD,IAA0D,AAC/D,GAAmB,eEwBzB,GAA4B,MAC5B,AAAI,GAA4B,KAC9B,AAA2B,MAI3B,AAAI,KAAgC,AAC9B,KAAgC,GAAkB,YAAI,AACxD,GAAmB,OACV,AAAC,QAAgC,GAAkB,MAAvD,IAA0D,AAC/D,GAAmB,gBJ5B3B,OACA,AAAI,EAAyC,KAG3C,EAAyC,KAIzC,UAAQ,0BAGK,EACA,EACA,EACA,EACT,GAIS,EACA,EACA,EACA,EAEA,EACT,GAIS,EACA,EACA,EACA,EACT,GAIS,EACA,EACA,EACA,EAEA,EACT,GAGS,EACA,EACA,GAKb,EAAwB,KACxB,AAAI,EAAwB,KAAG,AACN,KAGlB,SCvDP,SAGA,AAAI,GAA0B,IAAwB,iBIlKxD,yCAEI,AAAI,EAAwC,MAC1C,AAAsC,KAC/B,IAEF,IAEP,AAAI,EAAwC,MAC1C,AAAsC,KAC/B,IAEF,IAEP,AAAI,EAAwC,MAC1C,AAAsC,KAC/B,IAEF,IAEP,AAAI,EAAwC,MAC1C,AAAsC,KAC/B,IAEF,SH4FT,SAGA,AAAI,GAA0B,IAAwB,cCiCtD,SAGI,GAA0B,IAAwB,OAAK,AAAC,MAA5D,aCdA,SAGA,AAAI,GAA0B,IAAwB,cHvFtD,AAA0B,AAAC,GAAO,IAAsB,MAGxD,AAAI,IAAoB,AACI,GAA0B,oBW1JjD,SAGA,OAGA,QAJ+C,AAA3C,EAAuC,OAGI,AAA3C,EAAuC,OAGI,AAA3C,EAAuC,OAGvC,EAAuC,SXqJhD,SACA,AAAI,GAA2B,KAEC,KAKrB,EACT,GAN0B,iBAW1B,GAAmC,MACnC,AAAI,GAAmC,KAAG,AACN,OAUtC,AAAI,IAAsB,WACT,UAQjB,AAAkB,IAClB,AAAI,AAAC,AAA8C,GAAmB,QAAkC,KAO/F,AAHA,KAGS,QA5DlB,AAA6B,KAC7B,AAAwB,KACmB,OCnB3C,AAA0B,AAAC,GAAO,IAAsB,MAGxD,AAAI,IAAoB,AACI,GAA0B,YAMtD,SACA,AAAI,GAA2B,KAEC,KAKrB,EACT,GAN0B,iBAW1B,GAAmC,MACnC,AAAI,GAAmC,KAAG,AACN,OAUtC,AAAI,IAAsB,WACT,UAQjB,AAAkB,IAClB,AAAI,AAAC,AAA8C,GAAmB,QAAkC,KAO/F,AAHA,KAGS,QA5DlB,AAA6B,KAC7B,AAAwB,KACmB,OCF3C,AAA0B,AAAC,GAAO,IAAsB,MAGxD,AAAI,IAAoB,AACI,GAA0B,aAMtD,SACA,AAAI,GAA2B,KAEC,KAKrB,EACT,GAN0B,iBAS1B,GAA8B,MAC9B,AAAI,GAA8B,KAAI,AACP,OAKjC,AAAwB,IACxB,AAAsB,KAKtB,AAAI,IAAsB,WAAuB,AAE3C,IAIF,AADA,AAAa,AADA,AADA,MACc,GACD,QAE1B,AAA6B,UAejC,AAAS,AAF2B,AADN,GAA6B,YAM3D,AAAI,GAA6B,KAMtB,EAAS,IAHT,AADA,EAAU,GACD,MASpB,cAIO,OAIA,QANH,AAAS,EAAU,KACnB,GAGA,AAAe,IACf,GAEA,AAAS,EAAU,KACnB,AAAe,IACf,GAEA,AAAS,EAAU,KACnB,AAAe,KAYV,AAPT,AAAI,EAAe,KACR,MAEA,KAIO,QAzGlB,AAA6B,KAC7B,AAAwB,KACmB,SCoI3C,AAAoB,GAAoB,MACxC,AAAI,IAAoB,AACX,EAAW,cAzGxB,SAEA,AAAI,GAA2B,KAEC,KAG9B,AAAmC,KACnC,GAJ0B,iBAU1B,AAAuB,GAAuC,KAE9D,AAAa,AADS,GAAwC,GACpC,KAI1B,AAAuC,GAAwC,MAG/E,AAAuC,GAAuC,AAN9E,AAAwB,OAM4E,OAGpG,AAAI,KAEF,AAAuC,UACvC,AAAuC,GAAuC,EAAsB,SAUxG,AAAI,KAAsB,YACT,UAoBR,AAHA,AANT,AAAK,AAAe,EAAG,OAGZ,GAFA,QAQO,QAnElB,AAA6B,KAC7B,AAAwB,KACmB,OT7G3C,AAAI,mBKgEmB,UA4TzB,AAAI,EAAW,UA4BG,AAHA,AAVA,AADA,AAHA,AAHA,AADS,EAAS,WAIgB,QAIhB,qBA5FpC,AAAsC,IAQtC,AAAI,YAKJ,AAAI,YAGmB,MAEvB,AAAI,YAGmB,MAEvB,AAAI,YAGmB,MAKvB,AAAI,YAKJ,AAAI,YAGoB,MAExB,AAAI,YAGoB,MAExB,AAAI,YAGoB,MAIxB,AAAuC,IACvC,AAAsC,IAUtC,AAAyC,EAA2C,EAA4B,OAChH,AAA0C,EAA4C,EAA6B,OAGnH,IACA,IAEqF,WA8CrF,AAJA,AAAuB,AAAwB,EAAkB,UAIrC,EAAkB,MAC9C,AAAU,EAAmB,GAAG,EAAmB,YKjajB,AAAS,SAAqC,OAC9C,AAAS,SAAqC,OAC9C,AAAS,SAAqC,OAC9C,AAAS,SAAqC,OAEhF,OAAwB,AACqB,KAE7C,OAAwB,AACqB,KAE7C,OAAwB,AACqB,KAE7C,OAAwB,AACqB,KAIzC,uCAAJ,OAA0F,AAClD,KAIxC,EAAgC,EAAiB,OACjD,AAAI,EAAsC,KAGxC,EAAsC,KAElC,OAAuC,QAAuC,KAAlF,IAAwH,AAEpH,EACA,EACA,EACA,MAOJ,AACE,EAAiD,GACjD,EAAkD,GAClD,IAEF,EAAyB,KAKzB,AAAI,EAAyB,GAAmC,GAAI,MAAG,AACrE,EAAyB,YLqH7B,AAAmC,MACnC,AAAmC,MACnC,AAAmC,MACnC,AAAmC,MAQnC,IACA,IACA,IACA,IAGA,EAAgC,EAAiB,OACjD,AAAI,EAAsC,KAGxC,EAAsC,KAStC,AAAmC,AALM,AADzC,AAAuB,cAM4C,GAAG,AAJ5B,IAI6D,GAAG,IAC1G,EAAyB,KAKzB,AAAI,EAAyB,GAAmC,GAAI,MAAG,AACrE,EAAyB,YA7D7B,AAAqC,MAErC,AAAI,IAAiC,WAAwB,KAEtD,QAjBP,AAAI,EAA4B,SAIzB,EAA6B,KAClC,AAAkB,IAClB,AAAsB,EAA4B,YC3JlD,AAA2B,EAAS,IAAS,MAC7C,AAAsB,AAAe,SACrC,AAA0B,EAAQ,SENlC,AAAwB,AAAe,YFevC,AAAoB,EAAU,GAAK,MACnC,AAA0B,EAAQ,MAMlC,AAAyB,GAAK,SCnB9B,AAAoB,EAAU,GAAK,MACnC,AAA0B,EAAQ,MAMlC,AAAyB,GAAK,SCF9B,KAOA,AAAyB,GAAM,SCZ/B,AAA0B,EAAQ,MAMlC,AAAyB,GAAK,SHuB9B,AAA8B,EAAU,GAAK,MAC7C,AAA+B,AAAe,SAC9C,AAA8B,EAAQ,MAGtC,AAAwB,EAAS,IAAQ,SCjBzC,AAA8B,EAAU,GAAK,MAC7C,AAA+B,AAAe,SAC9C,AAA8B,EAAQ,MAGtC,AAAwB,EAAS,IAAQ,SCFzC,AAA0B,EAAU,GAAK,SCJzC,AAA8B,EAAU,GAAK,MAC7C,AAA+B,AAAe,SAC9C,AAA8B,EAAQ,MAGtC,AAAwB,EAAS,IAAQ,SHqBzC,KAIA,AADqB,AAAC,GAA6B,GAAK,UCfxD,KAIA,AADqB,AAAC,GAA6B,GAAK,UCLxD,KAIA,AADqB,AAAC,GAA6B,GAAK,aCAxD,AAA0B,EAAS,MACnC,AAAyB,AAAe,SACxC,AAA2B,EAAQ,MAGnC,gBAAQ,4BAEJ,AAAmB,KACnB,EAEA,AAAmB,KACnB,EAEA,AAAmB,KACnB,EAEA,AAAmB,KACnB,EAEA,AAAmB,MACnB,EAEA,AAAmB,MACnB,EAEA,AAAmB,MACnB,EAEA,AAAmB,UHJvB,AAA6B,AAAe,QAC5C,AAA4B,EAAQ,MAIpC,AADqB,AAAC,GAA6B,GAAK,YAsIxD,AAAqB,IACrB,AAAI,KAA8B,AACP,MAMlB,EAET,AAA2B,MAE3B,AAAkB,MAIlB,AAAgC,MAGhC,AAAwB,MAGpB,GAA2B,OAAK,GAA0B,MAA9D,IAAiE,AACrC,MACrB,AACqB,MAI5B,AAAI,GAA0B,KAAG,GAKjC,AAAI,AAAC,MAAuB,AACL,QCzLvB,AAA6B,AAAe,QAC5C,AAA4B,EAAQ,MAIpC,AADqB,AAAC,GAA6B,GAAK,UA0HxD,AAAqB,IACrB,AAAI,KAA8B,AACP,MAMlB,EAET,AAA2B,MAE3B,AAAkB,MAGlB,AAAI,AAAC,MAAuB,AACL,QChJvB,AAA6B,AAAe,QAC5C,AAA4B,EAAQ,MAIpC,AADqB,AAAC,GAA6B,GAAK,UAkKxD,AAAqB,IACrB,AAAI,KAA8B,AACP,MAKlB,EAGT,AAA6B,KAG7B,AAAI,AAAC,MAAuB,AACL,QCxJvB,AAA6B,AAAe,YA4H5C,AAAqB,KACrB,AAAI,KAA8B,AACP,MAI3B,AAAmC,KAEnC,AAA2B,MAE3B,AAAkB,MAGlB,AAAuC,OAGvC,AAAI,AAAC,MAAuB,AACL,SJzLvB,AAA4B,EAAU,GAAK,KAC3C,AAA6B,EAAQ,QAcrC,AAA0C,AAAe,QACzD,AAA0C,AAAe,QACzD,AAA0C,AAAe,QACzD,AAA0C,AAAe,QACzD,AAA2C,AAAe,QAC1D,AAA2C,AAAe,QAC1D,AAA2C,AAAe,QAC1D,AAA2C,AAAe,WAO1D,AAA2B,AAAe,gBalExC,WAAuC,AAAC,MAA5C,QASA,2FAGa,OAGA,OAIA,OAGA,OAGA,OAGA,OAIA,OAGA,OAKT,AAA6B,IACpB,OAGA,OAIA,OAGA,OAGA,OAGA,OAIT,AAAI,AAAe,QACR,IACA,MAIX,AAAI,AAAe,QACR,IACA,MAIX,AAAI,AAAe,QACR,IACA,MAIX,AAAI,AAAe,QACR,IACA,MAKL,IACN,AAAsC,OAIhC,IACN,AAAuC,OAIjC,IACN,AAAI,AAAC,AAAe,SAAW,EACxB,AAAa,QAAQ,EAAI,OAC5B,EAA6B,IADO,qBArFjC,KFqBT,AAAc,AAAe,SAC7B,AAAiC,AAAe,SAChD,AAA2B,AAAe,SAC1C,AAA6B,AAAe,SAC5C,AAA6B,AAAe,SAC5C,AAAqB,AAAe,SACpC,AAA0B,AAAe,SACzC,AAAuB,AAAe,cf9BxC,AAAgB,EAAiB,KAEjC,EAAK,AAAa,MAAG,EAAK,MAGxB,AADoC,OADH,AAAyB,SAD5B,aAQhC,AAAmB,SWWe,EAApB,AAAC,SXkHF,AAJS,AAHI,AAAyB,KAC1B,AAAyB,OAMxB,QAiBR,AANS,AAHI,AAAyB,KAC1B,AAAyB,OAQnB,YW/HD,EAArB,AAAC,AAAC,cM8EhB,AAAI,SAKF,AAAe,AAHS,MAGM,MAG9B,AAAI,IACW,OAEE,QAGjB,AAAI,IACW,OAEE,QAGjB,AAAI,IACW,OAEE,QAGjB,AAAI,KACW,OAEE,QAMV,AAFQ,EAAe,cEjGhC,AAA0B,KAE1B,AAAI,KAIF,AAAI,KACe,AAAe,OAEf,AAAa,SAIhC,AAAI,KACe,AAAe,OAEf,AAAa,SAIhC,AAAI,KACe,AAAe,OAEf,AAAa,SAIhC,AAAI,KACe,AAAe,OAEf,AAAa,UAE3B,AAAI,KAET,AAAI,KACe,AAAe,OAEf,AAAa,SAIhC,AAAI,KACe,AAAe,OAEf,AAAa,SAIhC,AAAI,KACe,AAAe,OAEf,AAAa,SAIhC,AAAI,KACe,AAAe,OAEf,AAAa,WAKjB,EAAiB,UDhHlC,AAAI,aAMA,WAA8B,UAAlC,QAcI,WAAoC,UAAxC,IAAwF,AAE/E,AAAyB,EAAS,QAMvC,WAAmD,UAAvD,IAGE,AAAI,EAAqB,UAOjB,AAAD,IAMT,AAAI,SACF,EAAkC,IAClB,AAAT,IAML,EAAU,SAAU,EAAU,QAAlC,IACE,EACoC,AAA7B,MAIL,EAAU,SAAU,EAAU,QAAlC,IACE,EACQ,AAAD,IAIT,AAAI,SACF,EAAkC,IACpB,AAAP,IAET,AAAI,SACF,EAAkC,IACpB,AAAP,IAIT,AAAI,SAAgD,AAC3C,WvBjFT,AAA0B,MAEnB,KACmC,AAA/B,MAEA,WK8GX,IAAqB,OACnB,AAAsB,AAAkC,SAGxD,AAAkC,SAC3B,EAA8B,OAAQ,AAEb,EAA8B,UAE9D,MATuC,aAiBzC,AAAsB,IACtB,AAAI,IAAoB,AACT,MAGf,GADa,EAAa,EAAkB,cAvG5C,AAAI,AAAC,OAKL,AAAI,KAA6B,AAAC,AAAe,gBAE/C,AAA4B,KAC5B,AAAsB,AAAyB,OAC/C,AAA0B,GAAkC,AAAa,QACzE,EAIF,AAAsB,IACtB,AAA2B,IAM3B,AAAiB,AADS,AAAe,MACN,GAAK,KAGxC,AAAI,AAAe,QAEjB,AAA4B,KAC5B,KACA,KACA,KAMA,AAA0B,GAAkC,AAAe,SAG3E,QAGA,AAA0B,GAAkC,WGoE9D,AAAwB,EAAmB,KAG3C,OAAc,EACI,MAGlB,AAAU,eAhEV,AAAI,AAAe,QAMjB,EAFe,AAAa,IADZ,aA3Bd,EAAW,SAA+C,EAAW,OAAzE,IAKE,AAAe,AAAe,EAHN,AAAyB,EAAS,SAO1D,AAAI,EAAW,MAAyC,AAC3C,KAGb,QAEA,EAAyC,EAAS,SOpE3C,MAyKT,OAEA,AAAI,EAA6C,KAG/C,EAA6C,KAE7C,EAA0B,KAE1B,AAAI,EAAyB,MAAM,AACR,WUnC7B,AAFA,AAAmB,EAHI,aAOvB,WAkBA,AAAuC,KACvC,OVzBA,IAEA,AAAI,AAAC,OAKL,SAEO,EAAuB,KAG5B,EAAuB,KAEvB,AAAI,EAAuB,MAEzB,AAAsB,IAGtB,GACK,AACL,EAAuB,eArC3B,AAAqC,IACrC,AAAI,IAAuB,WAAkD,AACtD,KAGvB,AAAI,WAIG,OACL,IACA,AAAuB,cAjIvB,AAAyB,IACzB,IAAgE,IAGhE,AAAsB,IACtB,AAAsB,OAKtB,OAKA,OSKA,AAA+B,EAAQ,OACvC,AAAoB,AAAe,EAAG,QACtC,AAAsB,AAAe,EAAG,WCExC,AAAwC,SACxC,AAAqC,SACrC,AAAuC,SACvC,AAAwC,SAExC,QArBA,AAAsC,SACtC,AAAmC,SACnC,AAAqC,SACrC,AAAsC,SAEtC,eNhBF,AAAI,SACF,SAME,WAA8B,cAmB9B,WAAoC,UAAxC,IAEE,AADsB,EAAS,WAU7B,WAA4C,UAAhD,IAGM,EAAqB,QAUvB,WAA2C,cAM3C,EAAU,SAAU,EAAU,QAAlC,IACE,EAC4C,AAArC,QAKL,EAAU,SAAU,EAAU,QAAlC,IAA0C,GAKtC,WAA0C,UAA9C,IAIE,AAAI,SAEE,OAKN,AAAI,SACF,AAA4B,IAC5B,EAAkC,OAKpC,AAAI,SACF,QAOF,AAAI,SAGF,OAKF,6CAEI,OAGA,OAGA,OAGA,UASN,AAAI,EAAW,MACb,OAME,EAAW,QAAoC,EAAW,MAA9D,IAAgG,AAC1F,KAEA,AAAC,GAA2B,SAAU,GAA2B,QAAjE,KACA,AAAC,GAA2B,SAAU,GAA2B,eAQnE,EAAU,QAAgD,EAAU,OAAxE,IAEE,SAKE,WAAkD,UAAtD,IAEE,EAEA,sCAEW,OAGA,OAGA,OAGA,UAQb,AAAI,SAAgD,KAKpD,AAAI,SACS,OAGb,AAAI,SACS,eAnLJ,KbbT,AAAI,QAAgC,YoBLpC,AAAyB,AAAW,SACpC,AAAI,EAAY,KAAG,AACD,YAIhB,AAAgB,EADC,UAIR,KAaX,AAAW,WA2BX,AAAI,EAAe,KAGjB,AADiB,AAAW,EAAe,GAAQ,EAAqB,MAAS,KAC5D,AACF,MACZ,AACY,OAEd,AAGD,AAAK,aAAmB,GAAQ,EAAS,MAAO,AACjC,MACZ,AACY,UAjDrB,AAAW,WAIX,AAAW,WAQX,AAAW,WVPkC,AAA3B,EAAU,GAAK,MAAU,WVN3C,AAAoB,MAEpB,AAAsB,EAAS,KAE/B,AAAI,EAHJ,AAAmB,UAGmB,OAItC,AAAI,QAAuC,WoBqE3C,OAKE,AAAkB,AADlB,AAA0B,cAK1B,AAAI,AAFJ,AAAmB,UAEJ,KAAa,AACT,MACZ,AACY,MAGnB,AAAI,EAAW,MAAc,AACd,MACR,AACQ,OASf,AAAI,AAHJ,AAAkB,AAAY,mBAGP,AACR,MACR,AACQ,MAMf,AADwB,AAAY,AADZ,QAC2B,QACxB,AACR,MACZ,AACY,UVjGwB,AAA3B,MAAU,GAAK,EAAU,oBCiG3C,oEAUI,AAAgB,AAAkB,eAClC,AAAgB,AAAiB,eACjC,AAAqB,AAAY,EAAqB,OAC/C,IAKP,AAAmC,AAAiB,EAAe,IAAgB,IAC5E,IAMP,AAAgB,AAAkB,AAFX,AAAsB,EAAe,yBAS5D,AAAiC,EAAe,KAChD,AAAgB,AAAW,EAAgB,OAC3C,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,QAMhB,AAAiC,EAAe,KAChD,AAAgB,AAAW,EAAgB,OAC3C,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,QAKhB,AAAgB,WAQhB,AAAI,AAAC,EAAgB,IAAU,MAAM,AACtB,MACR,AACQ,MAEf,AAAgB,AAAe,UAU/B,AAAqC,OAA2B,KAChE,AAAqB,AAAY,EAAqB,OAC/C,IAOP,AAFA,AAAsB,AAAsB,EAAe,MAEJ,AADvD,AAAuB,AAAsB,EAAe,WACa,KAEzE,AAAgB,AADhB,AAAkB,AAAY,iBAE9B,AAAgB,UAChB,AAAgB,KACT,IAIP,AAAgB,AAAsC,AAAiB,EAAe,YAC/E,IAMP,AAAgB,AADhB,AAAc,AAAY,AADH,AAAsB,EAAe,IACpB,kBAQxC,AAAiC,EAAe,KAChD,AAAgB,AAAW,EAAgB,OAC3C,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,QAMhB,AAAiC,EAAe,KAChD,AAAgB,AAAW,EAAgB,OAC3C,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,QAKhB,AAAgB,WAQhB,AAAI,AAAC,EAAgB,GAAQ,KAAG,AACjB,MACR,AACQ,MAEf,AAAgB,AAAgB,cA5HhC,AAAgB,UACT,IA+BP,AAAqB,AAAY,EAAqB,OAC/C,IAaP,AAAY,KACZ,AAAgB,KAChB,AAAiB,MArEV,KS3FmB,AAAvB,AAAC,EAAiB,GAAK,MVlBiB,AAA7B,EAAU,GAAK,WEZjC,AAAI,AAAe,EADnB,AAAoB,cACa,AACpB,AAAC,GAAM,SAAc,aSqZlC,AAA6B,OAE7B,AAAqB,AAAY,eAMjC,AAAqB,AAAY,EAAqB,UXrYA,AAApC,MAAU,GAAK,AAAC,GAAkB,oBC4PpD,kCACO,kCAQH,AAAI,IAEF,AAAI,AAAe,EADnB,AAAuB,YAerB,IATA,AAAK,AAAe,EAHpB,AAAc,AAAe,YAO3B,AAAqB,IACP,AAAe,OAJ7B,AAAqB,IACP,AAAa,WAUtB,MAMX,AAAqB,AAAY,EAAqB,UAKtD,AAAgB,AAAkB,eAClC,AAAgB,AAAiB,eACjC,AAAqB,AAAY,EAAqB,OAC/C,IAIP,AAAmC,AAAiB,EAAe,IAAgB,IAC5E,IAMP,AAAgB,AADhB,AAAc,AAAY,AADR,AAAsB,EAAe,IACf,kBAQxC,AAAiC,EAAe,KAChD,AAAgB,AAAW,EAAgB,OAC3C,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,QAMhB,AAAiC,EAAe,KAChD,AAAgB,AAAW,EAAgB,OAC3C,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,QAKhB,AAAgB,WAShB,AAAiB,IACjB,AAAI,AAAC,EAAgB,IAAU,MAAM,AACtB,KAEf,AAAgB,AAA2B,UAkB3C,AAAa,KACN,IAQP,AAFA,AAAsB,AAAsB,EAAe,MAEJ,AADvD,AAAuB,AAAsB,EAAe,WACa,KAEzE,AAAgB,AADhB,AAAkB,AAAY,iBAE9B,AAAgB,UAChB,AAAgB,KACT,IAKP,AAAgB,AAAsC,AAD/B,AAAsB,EAAe,iBAErD,IAMP,AAAgB,AADhB,AAAc,AAAY,AADH,AAAsB,EAAe,IACpB,kBAQxC,AAAiC,EAAe,KAChD,AAAgB,AAAW,EAAgB,OAC3C,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,QAMhB,AAAiC,EAAe,KAChD,AAAgB,AAAW,EAAgB,OAC3C,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,QAKhB,AAAgB,WAShB,AAAgB,IAChB,AAAI,AAAC,EAAgB,GAAU,KAAM,AACvB,KAEd,AAAgB,AAA4B,cArI5C,AAAgB,UACT,IA+BP,AAAqB,AAAY,EAAqB,OAC/C,IAaP,IAAgB,AACD,MACR,AACQ,MAGf,AAAY,KACZ,AAAgB,KAChB,AAAiB,MAzEV,KStSmB,AAAvB,AAAC,EAAiB,GAAK,MAQA,AAAvB,AAAC,EAAiB,GAAK,MAJA,AAAvB,AAAC,EAAiB,GAAK,iBT4c9B,kCACO,kCAKH,AAAI,QACF,AAAa,KACN,MAUT,AAAgB,AAAkB,AADT,iBAEzB,AAAgB,UAChB,AAAqB,AAAY,EAAqB,OAC/C,IAKP,AAAmC,AADnC,AAAuB,AAAsB,EAAe,WACZ,IAEhD,AAAgB,AADhB,AAAc,AAAY,EAAc,kBASxC,AAAgB,AADhB,AAAc,AAAY,AADR,AAAsB,EAAe,IACf,kBAQxC,AAAiC,EAAe,KAChD,AAAgB,AAAW,EAAgB,OAC3C,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,QAMhB,AAAiC,EAAe,KAChD,AAAgB,AAAW,EAAgB,OAC3C,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,QAKhB,AAAgB,WAUhB,AAAI,GAAqB,KAAG,KAG5B,AAAI,GAAiB,KAAG,AACT,EAAa,OAgB5B,AAbA,AAAI,GAAoB,KACH,AAAW,QAE9B,AAAI,AAAC,EAAgB,GAAQ,KAAM,AACpB,EAAa,MAE5B,AAAI,EAAgB,MAAM,AACX,EAAa,OAET,AAAW,YAMzB,AACO,MAHc,AACd,MAId,AAAI,EAAc,MAAa,AAChB,MACR,AACQ,MAEf,AAAiB,KAEjB,OAKA,AAAI,GAAgB,KAClB,AAAa,KACN,SAWT,AADA,AAAuB,AAAsB,EAAe,MACT,OAAa,KAEhE,AAAgB,AADhB,AAAc,AAAY,EAAc,eAExC,AAAgB,UAChB,AAAgB,KACT,IAKP,AAAgB,AAAsC,AADtD,AAAuB,AAAsB,EAAe,mBAG5D,AAAgB,AADhB,AAAc,AAAY,EAAc,kBASxC,AAAgB,AADhB,AAAc,AAAY,AADR,AAAsB,EAAe,IACf,kBAQxC,AAAiC,EAAe,KAChD,AAAgB,AAAW,EAAgB,OAC3C,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,QAMhB,AAAiC,EAAe,KAChD,AAAgB,AAAW,EAAgB,OAC3C,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,QAKhB,AAAgB,WAOhB,AAAgB,AAAC,WACjB,AAAgB,KAChB,AAAiB,YAhDjB,AAAgB,UACT,IAjHP,AAAgB,UACT,IAnBL,AAAqB,AAAY,EAAqB,OAC/C,IAuCF,cAiJX,kCACO,kCAGH,AAAI,QACF,AAAa,KACN,MAST,AAAmB,SACnB,AAAqB,AAAY,EAAqB,OAC/C,IAKP,AAAmC,AADnC,AAAuB,AAAsB,EAAe,WACZ,OAQhD,AAAmB,AAAY,EAAmB,OAC3C,IAWP,AALA,AAAqB,AAAsC,AAD3D,AAAuB,AAAsB,EAAe,oBAS5D,AAFA,AAAa,AAAW,WAIjB,AACO,MAHQ,AACR,MAId,AAAgB,QAWhB,AAHA,AAAqB,AAAsC,AAD3D,AAAuB,AAAsB,EAAe,eAIX,KAEjD,AADA,AAAa,AAAW,EAAa,SAG9B,AACO,MAHQ,AACR,MAId,AAAgB,QAMhB,AAAmC,AAAK,AAAiB,EAAe,SAAgB,QACxF,AAAqB,AAAY,EAAqB,OAC/C,IAMP,AAAgB,KAChB,AAAiB,KACjB,AAAa,KACN,IAIP,AAAI,GAAmB,KACrB,AAAa,KACN,SAWT,AADA,AAAuB,AAAsB,EAAe,MACJ,EAAkB,KAE1E,AAAgB,AADhB,AAAkB,AAAY,EAAoB,eAElD,AAAgB,UAChB,AAAgB,KACT,IAKP,AAAgB,AAAsC,AADtD,AAAuB,AAAsB,EAAe,sBAS5D,AAAmB,AAAY,EAAmB,OAC3C,IAKP,AAAiC,EAAe,KAChD,AAAgB,AAAW,EAAgB,OAC3C,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,KACT,IAKP,AAAiC,EAAe,KAChD,AAAgB,AAAW,EAAgB,OAC3C,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,KACT,IAIP,AAAgB,WAOhB,AAAgB,KAChB,AAAiB,KACjB,AAAI,GAAiB,KAAG,AACT,MACR,AACQ,MAER,QA7IP,AAAgB,AADhB,AAAc,AAAY,EAAc,eAExC,AAAgB,UACT,IAjBL,AAAqB,AAAY,EAAqB,OAC/C,IAyCT,AAAmC,WAC5B,UAuHX,kCACO,UAKA,sCAGH,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,AAAsC,AAAiB,EAAe,YAC/E,IAIP,AAAgB,OAKhB,AAAgB,UAUhB,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,AAAsC,AAAiB,EAAe,YAC/E,IAIP,AAAgB,WA1ET,UAiFX,kCACO,UAKA,mCAFH,AAAgB,OAKhB,AAAgB,UAUhB,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,AAAsC,AAAiB,EAAe,YAC/E,IAIP,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,UAUhB,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,AAAsC,AAAiB,EAAe,eAKtF,AAAgB,WA1ET,UAiFX,kCACO,UAMA,mCAFH,AAAgB,IACT,IAIP,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,UAUhB,AAAgB,OAKhB,AAAgB,AAAsC,AAAiB,EAAe,YAC/E,IAIP,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,UAUhB,AAAgB,AAAsC,AAAiB,EAAe,YAC/E,IAIP,AAAgB,WArET,YA4EX,kCACO,UAKA,mCAFH,AAAmC,AAAiB,EAAe,IAAgB,OAKnF,AAAmC,AAAiB,EAAe,IAAgB,OAKnF,AAAmC,AAAiB,EAAe,IAAgB,OAKnF,AAAmC,AAAiB,EAAe,IAAgB,OAKnF,AAAmC,AAAiB,EAAe,IAAgB,OAKnF,AAAmC,AAAiB,EAAe,IAAgB,OAWnF,AAAI,AAAC,MAA2B,AACf,QAMjB,AAAmC,AAAiB,EAAe,IAAgB,OAKnF,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,OAKhB,AAAgB,OAMhB,AAAgB,AAAsC,AAAiB,EAAe,sBA9E/E,IAsCA,KSvkCX,AAAI,EAAe,KAEjB,AAAI,MADa,AAAW,UACR,AACL,MACR,AACQ,OAEV,AACD,aAAmB,SAAY,AACpB,MACR,AACQ,YCvDjB,AAAiC,EAAe,WAChD,AAA6B,OAC7B,AAAgB,AAAW,SAC3B,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,UAOhB,AAAiB,AAAW,KAA2B,QACvD,AAAI,AAAC,AAAW,UAAqC,KAAY,AAC9C,MACZ,AACY,MAInB,AAAI,AADwB,AAAY,EAAqB,OAAgB,MACrD,IAAS,KAAG,AACrB,MACR,AACQ,MAGf,IACA,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,aVooChB,oCACO,oCAIH,AAAa,QAMb,AAAa,QAMb,AAAa,QAMb,AAAa,QAMb,AAAa,QAMb,AAAa,QAOb,AADqB,AAAsC,AAAiB,EAAe,SAEpF,IAKP,AAAa,QAMb,AAAyB,QAMzB,AAAyB,QAMzB,AAAyB,QAMzB,AAAyB,QAMzB,AAAyB,QAMzB,AAAyB,QAOzB,AADqB,AAAsC,AAAiB,EAAe,SAEpF,IAKP,AAAyB,YA3FlB,OUloCX,AAAiC,EAFjC,AAAmB,AADS,MACU,QAGtC,AAA6B,OAC7B,AAAgB,AAAW,SAC3B,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,UAOhB,AAAiB,AAAW,KAA2B,QAGvD,AADyB,AAAW,AAAC,QAAqC,OAC7C,AACV,MACZ,AACY,MAInB,AAAI,AADwB,AAAY,EAAqB,OAAgB,MACrD,IAAS,KAAG,AACrB,MACR,AACQ,MAGf,IACA,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,aV+rChB,oCACO,oCAIH,AAAa,QAMb,AAAa,QAMb,AAAa,QAMb,AAAa,QAMb,AAAa,QAMb,AAAa,QAOb,AADqB,AAAsC,AAAiB,EAAe,SAEpF,IAKP,AAAa,QAMb,AAAyB,QAMzB,AAAyB,QAMzB,AAAyB,QAMzB,AAAyB,QAMzB,AAAyB,QAMzB,AAAyB,QAOzB,AADqB,AAAsC,AAAiB,EAAe,SAEpF,IAKP,AAAyB,YA3FlB,KUjsCX,AAAgB,OAChB,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,KAChB,AAAiB,KACjB,AAAa,QAIb,AAAgB,AAAW,SAC3B,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,KAChB,AAAiB,KACjB,AAAa,aV+wCb,oCACO,oCAIH,AAAa,QAMb,AAAa,QAMb,AAAa,QAMb,AAAa,QAMb,AAAa,QAMb,AAAa,QAOb,AADqB,AAAsC,AAAiB,EAAe,SAEpF,IAMP,AAAa,QAMb,AAAa,QAMb,AAAa,QAMb,AAAa,QAMb,AAAa,QAMb,AAAa,QAMb,AAAa,QAOb,AADqB,AAAsC,AAAiB,EAAe,SAEpF,IAKP,AAAa,YA5FN,KUjxCX,AAAgB,WAChB,AAAI,IAEG,AACO,MAHW,AACX,MAId,AAAgB,KAChB,AAAiB,KACjB,AAAa,UAUb,AAAiC,EADjC,AAAmB,AADS,MACU,QAEtC,AAA6B,OAE7B,AADsB,OAGf,AACO,MAHQ,AACR,MAId,AAAgB,aV01ChB,oCACO,oCAIH,AAAY,QAMZ,AAAY,QAMZ,AAAY,QAMZ,AAAY,QAMZ,AAAY,QAMZ,AAAY,QAOZ,AADqB,AAAsC,AAAiB,EAAe,SAEpF,IAKP,AAAY,QAMZ,AAAY,QAMZ,AAAY,QAMZ,AAAY,QAMZ,AAAY,QAMZ,AAAY,QAMZ,AAAY,QAOZ,AADqB,AAAsC,AAAiB,EAAe,SAEpF,IAKP,AAAY,YA3FL,OjB7+CX,IADA,AAAiC,MAE1B,QACH,AAAU,QAad,IADA,AAAkC,AAJlC,AAAsB,EAAS,SAMxB,KACH,AAAW,QAQ0B,S2B0HzC,AAAI,EAAY,IAAU,MAAM,AACjB,MACR,AACQ,MAGf,AADA,AAAW,SAGJ,AACO,MAHM,AACN,MAMd,AAAgB,KAChB,AAAiB,UAUjB,AAAI,EAAY,GAAQ,KAAG,AACZ,MACR,AACQ,MAIf,AAFA,AAAW,SAIJ,AACO,MAHM,AACN,MAKd,AAAgB,KAChB,AAAiB,YAWjB,AAAI,EAAY,IAAU,MAAM,AACjB,KAEf,AAAW,OAEX,IAAgB,AACD,MACR,AACQ,MAGf,IAEO,AACO,MAHM,AACN,MAKd,AAAgB,KAChB,AAAiB,YASjB,AAAI,EAAY,GAAU,KAAM,AAClB,KAEd,AAAW,OAEX,IAAe,AACA,MACR,AACQ,MAGf,IAEO,AACO,MAHM,AACN,MAKd,AAAgB,KAChB,AAAiB,YASjB,AAAI,EAAY,IAAU,MAAM,AACjB,KAGf,AAAW,AAAW,EAAY,OAElC,IAAgB,AACD,MACR,AACQ,MAGf,IAEO,AACO,MAHM,AACN,MAKd,AAAgB,KAChB,AAAiB,YAWjB,AAAI,EAAY,IAAU,MAAM,AACjB,KAIf,AAAI,EAAY,GAAU,KAAM,AAClB,KAGd,AAAW,AAAW,MAAY,OAElC,IAAgB,AACH,EAAW,OAGxB,IAEO,AACO,MAHM,AACN,MAKd,AAAgB,KAChB,AAAiB,KAEjB,IAAe,AACA,MACR,AACQ,WAaf,AAFA,AAAW,AAAW,AADN,EAAW,GACS,GAAK,AAFxB,EAAW,IAE4B,UAIjD,AACO,MAHM,AACN,MAKd,AAAgB,KAChB,AAAiB,KACjB,AAAa,YAYb,AAAI,EAAY,GAAU,KAAM,AAClB,KAKd,AAFA,AAAW,AAAW,MAAY,SAI3B,AACO,MAHM,AACN,MAKd,AAAgB,KAChB,AAAiB,KAEjB,IAAe,AACA,MACR,AACQ,WAYf,AADa,EADM,YAIZ,AACO,MAHO,AACP,MAKd,AAAgB,KAChB,AAAiB,UAQjB,AAAI,EAAW,KAEF,EADO,OAMP,EADO,AAAC,AAAC,kBCnYtB,AAA0B,IAY1B,kBAHA,AAAqB,EAAW,iCAK5B,AAA2B,IAC3B,GAEA,AAA2B,IAC3B,GAEA,AAA2B,IAC3B,GAEA,AAA2B,IAC3B,GAEA,AAA2B,IAC3B,GAEA,AAA2B,IAC3B,GAGA,AAA2B,AAAsC,AAAiB,EAAe,QACjG,GAEA,AAA2B,KAS/B,kCAHA,AAAmB,AADI,EAAW,IACK,yCAKnC,AAAI,EAAY,KAGd,AAA4B,OAC5B,AAAgB,KACX,AAAI,EAAY,KAGrB,AAA4B,OAC5B,AAAgB,MAElB,GAEA,AAAI,EAAY,KAGd,AAA4B,OAC5B,AAAgB,KACX,AAAI,EAAY,KAGrB,AAA4B,OAC5B,AAAgB,MAElB,GAEA,AAAI,EAAY,KAGd,AAA4B,OAC5B,AAAgB,KACX,AAAI,EAAY,KAGrB,AAA4B,OAC5B,AAAgB,MAElB,GAEA,AAAI,EAAY,KAGd,AAA4B,OAC5B,AAAgB,KACX,AAAI,EAAY,KAGrB,AAA4B,OAC5B,AAAgB,MAElB,GAEA,AAAI,EAAY,MAId,AAA4B,AAAkB,SAC9C,AAAgB,KACX,AAAI,EAAY,MAGrB,AAA4B,AAAkB,SAC9C,AAAgB,MAElB,GAEA,AAAI,EAAY,MAGd,AAA4B,AAAkB,SAC9C,AAAgB,KACX,AAAI,EAAY,MAGrB,AAA4B,AAAkB,SAC9C,AAAgB,MAElB,GAEA,AAAI,EAAY,MAGd,AAA4B,AAAkB,SAC9C,AAAgB,KACX,AAAI,EAAY,MAGrB,AAA4B,AAAkB,SAC9C,AAAgB,MAElB,GAEA,AAAI,EAAY,MAGd,AAA4B,AAAkB,SAC9C,AAAgB,KACX,AAAI,EAAY,MAGrB,AAA4B,AAAkB,SAC9C,AAAgB,MAElB,GAEA,AAAI,EAAY,MAGd,AAA4B,AAAiB,EAAG,SAChD,AAAgB,KACX,AAAI,EAAY,MAGrB,AAA4B,AAAiB,EAAG,SAChD,AAAgB,MAElB,GAEA,AAAI,EAAY,MAGd,AAA4B,AAAiB,EAAG,SAChD,AAAgB,KACX,AAAI,EAAY,MAGrB,AAA4B,AAAiB,EAAG,SAChD,AAAgB,MAElB,GAEA,AAAI,EAAY,MAGd,AAA4B,AAAiB,EAAG,SAChD,AAAgB,KACX,AAAI,EAAY,MAGrB,AAA4B,AAAiB,EAAG,SAChD,AAAgB,MAElB,GAEA,AAAI,EAAY,MAGd,AAA4B,AAAiB,EAAG,SAChD,AAAgB,KACX,AAAI,EAAY,MAGrB,AAA4B,AAAiB,EAAG,SAChD,AAAgB,MAElB,GAEA,AAAI,EAAY,MAGd,AAA4B,AAAiB,EAAG,SAChD,AAAgB,KACX,AAAI,EAAY,MAGrB,AAA4B,AAAiB,EAAG,SAChD,AAAgB,MAElB,GAEA,AAAI,EAAY,MAGd,AAA4B,AAAiB,EAAG,SAChD,AAAgB,KACX,AAAI,EAAY,MAGrB,AAA4B,AAAiB,EAAG,SAChD,AAAgB,MAElB,GAEA,AAAI,EAAY,MAGd,AAA4B,AAAiB,EAAG,SAChD,AAAgB,KACX,AAAI,EAAY,MAGrB,AAA4B,AAAiB,EAAG,SAChD,AAAgB,MAElB,GAEA,AAAI,EAAY,MAGd,AAA4B,AAAiB,EAAG,SAChD,AAAgB,KACX,AAAI,EAAY,MAGrB,AAA4B,AAAiB,EAAG,SAChD,AAAgB,OAMtB,2CAEI,IACA,GAEA,IACA,GAEA,IACA,GAEA,IACA,GAEA,IACA,GAEA,IACA,GAGA,AAAmC,AAAiB,EAAe,QACnE,GAEA,KAMJ,AAAqB,AAAY,EAAqB,OAItD,IAEE,AAAiB,IACjB,AAAI,EAAmB,KAAG,AACP,0BXmyCrB,oCACO,oCAGH,AAAI,cAUJ,AAAuB,AAA2B,OAClD,AAAmB,AAAY,EAAmB,OAClD,AAAgB,UAChB,AAAgB,UACT,IAIP,AAAI,gBAeJ,AAAI,aAYJ,AAAmB,AAAY,EAAmB,OAClD,AAAqC,EAAkB,AAAiB,EAAe,OAChF,IAKP,AAAa,QAMb,AAAmB,AAAY,EAAmB,OAClD,AAAqC,EAAkB,KACvD,AAAqB,IACd,IAIP,AAAI,GAAkB,cAUtB,AAAqB,AAAgC,YACrD,AAAmB,AAAY,EAAmB,OAC3C,IAIP,AAAI,GAAkB,aAWtB,AAAI,AADJ,AAAoB,AAAe,WACpB,KAAG,EACJ,MAEP,IAIP,AAAI,GAAkB,KACpB,AAAmB,AAAY,EAAmB,OAClD,AAAqC,EAAkB,EAAqB,sBAkB9E,AAAyB,QAMzB,AAAmB,AAAY,EAAmB,OAClD,AAAqC,EAAkB,KACvD,AAAqB,IACd,QA3EP,AAAqB,AAAY,EAAqB,OAC/C,IAhCL,AAAqB,AAAY,EAAqB,OAC/C,IAWP,AAAmB,AAAY,EAAmB,OAClD,AAAqC,EAAkB,AAAY,EAAqB,SAmExF,AAAqB,SACd,IApFP,AAAqB,SACd,IAnBP,AAAqB,AAAgC,YACrD,AAAmB,AAAY,EAAmB,OAC3C,KQr8Cb,wBRwkDA,8BACO,oCAGH,AAAI,cAUJ,AAAuB,AAA2B,OAClD,AAAmB,AAAY,EAAmB,OAClD,AAAgB,UAChB,AAAgB,UACT,IAIP,AAAI,aAWJ,AAAI,QACF,AAAmB,AAAY,EAAmB,OAClD,AAAqC,EAAkB,EAAqB,gBAU9E,AAAmB,AAAY,EAAmB,OAClD,AAAqC,EAAkB,AAAiB,EAAe,OAChF,IAKP,AAAa,QAMb,AAAmB,AAAY,EAAmB,OAClD,AAAqC,EAAkB,KACvD,AAAqB,IACd,IAIP,AAAI,GAAmB,cAUvB,AAAqB,AAAgC,YAErD,AAAc,KACd,AAAmB,AAAY,EAAmB,OAC3C,IAIP,AAAI,GAAmB,aAWvB,AAAI,GAAmB,KACrB,AAAmB,AAAY,EAAmB,OAClD,AAAqC,EAAkB,AAAY,EAAqB,gBAY1F,AAAyB,QAMzB,AAAmB,AAAY,EAAmB,OAClD,AAAqC,EAAkB,KACvD,AAAqB,IACd,QAhEP,AAAqB,AAAY,EAAqB,OAC/C,IA5BL,AAAqB,AAAY,EAAqB,OAC/C,IASP,AAAqB,SACd,IAdP,AAAqB,SACd,IAnBP,AAAqB,AAAgC,YACrD,AAAmB,AAAY,EAAmB,OAC3C,QAoHb,wBACO,oCAMH,AAAmC,AADP,MACO,KAA2B,IAC9D,AAAqB,AAAY,EAAqB,OAC/C,IAIP,AAAuB,AAA2B,OAClD,AAAmB,AAAY,EAAmB,OAClD,AAAgB,UAChB,AAAgB,UACT,IASP,AAAmC,AAAS,EAAT,KAA6B,IACzD,IAKP,AAAmB,AAAY,EAAmB,OAClD,AAAqC,EAAkB,AAAiB,EAAe,OAChF,IAKP,AAAa,QAMb,AAAmB,AAAY,EAAmB,OAClD,AAAqC,EAAkB,KACvD,AAAqB,IACd,IAMP,AAA4B,AAAW,OAEvC,AAAsC,EAAkB,UAAmB,KAC3E,AAAmB,AAAY,SAC/B,AAAY,KACZ,AAAgB,KAChB,AAAqB,AAAY,EAAqB,OAC/C,IAIP,AAAqB,AAAsB,EAAe,WACnD,IAIP,AAAmC,OAA2B,IAC9D,AAAqB,AAAY,EAAqB,OAC/C,IAMP,AAAa,QAMb,AAAmB,AAAY,EAAmB,OAClD,AAAqC,EAAkB,KACvD,AAAqB,IACd,QA/CP,AAAqB,AAAY,EAAqB,OAC/C,UAoDX,4BACO,oCAIH,AAAgB,AAAW,AAAsC,AADrC,MACqC,cAOjE,AAAuB,AAAK,AAA2B,YACvD,AAAmB,AAAY,EAAmB,OAClD,AAAgB,UAChB,AAAgB,UACT,IAIP,AAAgB,AAAW,AAAsC,AAAS,EAAT,WAC1D,IAIP,AAAc,KACP,IAKP,AAAmB,AAAY,EAAmB,OAClD,AAAqC,EAAkB,AAAiB,EAAe,OAChF,IAKP,AAAY,QAMZ,AAAmB,AAAY,EAAmB,OAClD,AAAqC,EAAkB,KACvD,AAAqB,IACd,IAMP,AAA4B,AAAW,OAGvC,AAAY,KACZ,AAAgB,KAChB,AAAsC,EAAkB,UAAmB,KAE3E,AAAgB,AADhB,AAAiB,AAAY,iBAE7B,AAAgB,aAMhB,AAAmB,AAAsB,EAAe,WACjD,IAIP,AAAgB,AAAsC,eACtD,AAAqB,AAAY,EAAqB,OAC/C,IAIP,AAAc,KACP,IAMP,AAAY,QAMZ,AAAmB,AAAY,EAAmB,OAClD,AAAqC,EAAkB,KACvD,AAAqB,IACd,QAvDP,AAAqB,AAAY,EAAqB,OAC/C,IAlCP,AAAqB,AAAY,EAAqB,OAC/C,QA14DX,AAAqB,AAAY,EAAqB,qCAKtD,AAAmB,AADS,EAAS,IACE,SAYhC,gCADyB,AAArB,OAEqB,AAArB,OAEqB,AAArB,OAEqB,AAArB,OAEqB,AAArB,OAEqB,AAArB,OAEqB,AAArB,OAEqB,AAArB,OAEqB,AAArB,OAEqB,AAArB,OAEqB,AAArB,OAEqB,AAArB,OAEqB,AAArB,OAEqB,AAArB,OAEqB,AAArB,OAEA,QQzD0E,AAA5E,AAAC,GAAsC,IAAqC,QnBxBrF,AAAoB,MAIpB,EAHmB,MAInB,AAHsB,EAAS,amBqF/B,AAAc,KAKd,AADA,AAAmB,EADI,aAGvB,QAGA,AAAmB,EAAmB,UACtC,AAA4B,EAAkB,KAK9C,8BAEI,AAAwC,KACxC,AAAqB,KACrB,GAEA,AAAqC,KACrC,AAAqB,KACrB,GAEA,AAAuC,KACvC,AAAqB,KACrB,GAEA,AAAwC,KACxC,AAAqB,YAtErB,KAAoC,GAAoC,YAAK,GAAsC,MAAvH,IAIE,AAAmC,IAGnC,AAAI,KAAuC,UACzC,KACA,AAAsB,KACjB,AAAI,KAAoC,UAC7C,KACA,AAAsB,KACjB,AAAI,KAAsC,UAC/C,KACA,AAAsB,KACjB,AAAI,KAAuC,UAChD,KACA,AAAsB,QAIxB,IACE,AAAkC,IAClC,AAAI,IAIF,AAAe,IACf,KAEK,UlB1ET,AAAI,eAToC,MAgRZ,AADJ,OAA6E,AAAS,EAAc,GAAvB,UAbhF,AAAhB,AAAC,EAAI,OAAW,MAQvB,AAAU,AAAiB,uBwBzQV,AAAV,EAAI,UAbmD,AAA9C,AAA2B,oBAT3C,AAA0B,EAAU,KACpC,OAAsB,AACH,AAAa,SAGhC,AAAU,AAA2B,uBFyVjC,EAAS,OAAK,EAAS,MAAvB,IAA4B,EAA2B,OAAvD,IAA2E,EAAW,OAA1F,IAEE,AAA8C,IAC9C,AAAgD,IAChD,AAAI,AAAe,EAAG,AAAyB,EAAiB,SAAK,AAClC,KAEnC,AAAI,AAAe,EAAG,QAA2C,AAC5B,KAIrC,EAAK,AAAqB,MAAG,EAAiB,KAE5C,AAAI,OAAqE,AACtD,QAInB,AAAI,KAA2B,MAE7B,AAAqB,EAAS,AAAC,QAC/B,AAAgC,AAAiB,AAAiB,iBAGlE,EAAK,AAAmB,MAAG,EAAe,KACxC,AAAgB,SAA+C,AAAS,WAD7B,aAM7C,AAAoB,OAAiC,AAAe,EADpE,AAAyB,WAC8D,AAAe,SAEtG,QArB6C,cAwB5C,MAMP,AAAI,EAAU,MACZ,AAA0C,EAAS,MAEnD,AAAI,EADJ,AAAqC,EAAsB,QACnB,AACtC,kBCjQJ,AAAI,SAIF,AAAwB,EAAoB,MAC5C,AAAI,AAAe,QAAuB,AACzB,EAAoB,OAEU,AAAxC,EAAyB,EAAe,MAIG,EAApB,EAAoB,StBpBpD,AAAwB,EAAmB,KAG3C,OAAc,EACI,MAGiC,AAAnC,eAzB4C,AAJjC,AAA8B,AAHzD,AAAwB,EAAY,GAAI,EAAU,MAGsB,QAC9C,YAeN,AAJE,EADH,EAAQ,EAAW,UAKlB,MA3FpB,AAAI,QAAgC,AAC1B,AAAC,IAA0D,EAAW,IAAM,MAMtF,AAAoB,KAEpB,mCAII,AAAW,KACX,GAEA,AAAW,KACX,GAEA,AAAW,UsBwEK,AAHE,QAGF,SAvFpB,AAAsC,AAAiB,AAH5B,OAG8C,EAAY,aACrF,AAAsC,AAAiB,EAAkC,UAGzF,EAAK,MAA6B,OAIhC,AAAI,AADJ,AAAsB,EAAc,aAOlC,IACI,EAAkB,QAAK,AAAC,AAAe,UAA3C,IAAgE,AAC/C,QAIjB,AAA0B,IAC1B,AAAI,QAGF,KAEF,AAAI,QAA0D,EAC1C,MASpB,AAAI,EAAmB,KAOrB,AAAM,AAAyB,EAH/B,AAA2B,AADN,EAAkB,KACsC,YAI7E,AAAQ,AAAyB,SAC1B,AAAyB,QAEhC,AAAI,EAAmB,KAAG,OAI1B,AADA,AAA2B,aAE3B,OAQF,AAAU,EAFY,iBAGtB,AAAU,EAA+B,QACzC,AAAU,EAA+B,QAEzC,AAA6B,IAC7B,AAAI,EAAmB,KAAG,AACR,AAAe,SAOjC,WAEA,QAvEiD,qBDqXrD,AAAqB,EAAsB,KAM3C,KAAiB,AACF,EAAsB,EAAuB,GAAK,OAEjE,AAAoB,IACpB,AAAI,EAAS,GAAI,MAAK,AACT,SAIb,AAA2B,IAE3B,AAAI,IAGF,AAAI,AAAe,EAAG,AADtB,AAAkB,EAAiC,eACP,AAC7B,KAGf,AAAI,AAAe,QAAqB,AAG1B,SAmBhB,gBALE,OAEA,EACA,WAvMF,AAA2B,SAS3B,AAA2B,EAAiC,OAG5D,AAAwB,EAAsB,KAC9C,AAAI,AAAe,QAAqB,AAGvB,QAMjB,AAAsB,IACtB,AAAI,AAAe,QAAqB,AACzB,KAEf,AAAsC,AAAiB,EAAkB,EAAe,aACxF,AAAsC,AAAiB,EAAqC,UAI5F,AAAwB,EAAsB,KAC9C,AAAI,AAAC,AAAe,SAAqB,AACxB,QAQjB,AAA0B,IAC1B,AAAI,QAGF,KAEF,AAAI,QAA0D,EAC1C,MAWpB,AAAe,AAAyB,EAHxC,AAA2B,AAHN,EAAkB,KAGsC,YAI7E,AAAiB,AAAyB,SAC1C,AAAgB,AAAyB,SAGzC,IAAgC,OAChC,IAAgC,OAChC,IAAgC,OAMhC,MAAoD,AAAe,aAxInE,AAAsC,AAAiB,AAhB5B,OAgB8C,AALjD,EAAsB,GAK0C,MAAG,OAiB3F,AAAyB,IAhBzB,AAAsC,AAAiB,EAAqC,GAAG,OAiB/F,AAAI,AARJ,AAAe,EADS,EAAsB,YAY5C,KAEF,AAAI,QAA0D,EAC1C,MAcpB,IAAgC,EADhC,AAA2B,MrB7Mc,UqB+MzC,IAAgC,OAChC,IAAgC,OAMhC,MEnPgG,WFwEhG,AAA8B,EAAuB,KAGrD,IAA0B,EAAI,MAO5B,AAAI,AAHJ,AAA+B,OAGJ,MAAO,EACT,OAoBzB,AAA6B,AAH7B,AAA0B,EAAwB,EAAqB,IATzC,EAAuB,MAYS,OAG9D,AAA6B,IAC7B,AAAI,IAWF,AAAI,AAVJ,AAAuB,mBAUL,KAChB,EAAK,EAAc,MACnB,AAAgB,MAIpB,AAAI,IAAwB,WAY1B,AAAI,AAXJ,AAAuB,mBAWL,KAAG,EACd,EAAc,QAEhB,AAAI,KAAgB,AACrB,IAAgB,kBAWb,kBA5EwB,kBA5DnC,AAAmB,IAWnB,AAAI,AALJ,AAA+B,EALZ,KAUQ,MAAO,EACT,OAIzB,QAAmH,YAOnH,AAAmB,IAMnB,AAAI,EALJ,AAAmB,SAiBnB,AAAmB,AANnB,AAAU,EAAU,KAMD,KAGnB,MAN+B,kBG3C/B,EAAK,AAAa,MAAI,EAAK,KAKzB,AAA2B,AAAyB,AAHpD,AAA4B,EAAI,cAIhC,AAA2B,WAC3B,AAAwB,WAaxB,EAAmB,KACnB,EAAmB,KAGnB,AAAwB,IACxB,OACE,AAAe,IAOf,AAAI,EAAe,GAAM,KAAG,EACV,OAKhB,SAAuC,EAAmB,SAA9D,IAOE,AAAyD,AAAe,EAHxE,AAA4B,eAM5B,AAA2B,AAAe,QAC1C,AAA2B,AAAe,QAG1C,AAA6B,OAG7B,IAKE,AAHoB,KAAoB,GAGnB,MASvB,AADyB,AADS,SAHd,EAAoB,MAQxC,AAAsB,IACtB,AAAI,IAAkB,AAAe,cAAsB,AAC5C,KAEf,AAAgD,SAChD,AAAgD,AAAiB,EAAoB,UAGrF,EAAK,AAAqB,MAAG,EAAa,KAExC,IACA,IAEE,AAAqB,EADC,GACoB,MAM5C,AAAyB,IACzB,AAAI,QAGF,KAEF,AAAI,QAA0E,EAC3D,MAKnB,IAIM,AADJ,AAA4C,EAAkB,AAAC,QACvB,OAAK,EAAoC,OAAjF,IAOE,AAA0C,IAC1C,AAAyC,IACzC,AAAwC,IAExC,AAAI,IAAkB,AAAC,YAAsB,AACd,KAG/B,AAAI,KAKF,AAAmC,AAFnC,AAA0B,SAE0B,KAGpD,AAAI,IAA6C,EAA0B,WAAG,AAEhD,KACnB,IAAkB,AAAe,cAAsB,EAA0B,MAArF,IAAwF,AAElE,OAI3B,KAA8B,AAAC,OAA8B,UAAjE,IAA6F,AACtF,IAwBH,AAAe,AAAyB,EAHxC,AAA2B,AAHN,EAAmB,KAGoC,YAI5E,AAAiB,AAAyB,SAC1C,AAAgB,AAAyB,SAGzC,IAAoE,OACpE,IAAoE,OACpE,IAAoE,QA5BpE,MACA,AAAI,AAAe,QAAsB,OAMzC,IAAoE,EAHpE,AAAuC,IxBjIZ,UwBqI3B,IAAoE,OACpE,IAAoE,WAxEjC,cAjFnB,kBzBqN9B,MACA,AAAI,KAA4B,OAWhC,AAAI,OAAkB,QAEpB,MACA,AAAI,KAA4B,OAKhC,UAKF,AAAI,KAEF,MACA,AAAI,KAAgC,OAKpC,UAGF,AAAI,KAAyB,EAEK,YASlC,IAAoB,EAAK,MACvB,AAAc,SADc,kBwBhQ9B,IAAqB,EAAI,MACvB,EAAK,AAAa,MAAG,EAAI,MACvB,AAAU,AAA2B,YAAqB,KAD9B,aADF,gBDD9B,AAAmB,KACnB,AAA0C,QvByBxC,AAAI,eAQJ,AAAI,ekB2HN,AAAqC,KACrC,SpBzFA,AAAI,AAAC,QAKL,AAA2B,IAC3B,AAAI,QAA4D,AAE5C,MAIpB,AAAa,GAAyB,OAGtC,SACA,SACA,SAEA,AAAI,GAA4C,KAE9C,AAA4B,KAG5B,AAA0B,GAAkC,MAM5D,AAA0B,GAAkC,AAAe,EAD3C,AADG,GACuB,GAAK,WoBsDjE,AAAwC,KACxC,UL9GA,AAAI,AAAC,MAEH,AAAgC,IAChC,AAA4B,IAC5B,IAAmE,IAMnE,AAAY,AAAe,EADf,AAAe,EADN,YAGrB,AAAqB,IAGrB,QACA,EAKF,AAAmB,IAMnB,AAAI,AAPJ,AAA4B,IAOJ,MAAK,AAEd,KACR,AACD,EAA0C,MAA+B,AAE9D,KACR,AAAI,EAA0C,MAAqC,AAE3E,OAIjB,AAAI,OAEF,AAAqB,QAGrB,IAEA,AAAsC,IAGtC,oCAII,AAAyB,AAAe,EADxC,AAAY,AAAe,EADf,AAAe,cAG3B,GAIA,AAAyB,AAAe,EADxC,AAAY,AAAa,EADb,AAAe,cAG3B,GAIA,AAAyB,AAAe,EADxC,AAAY,AAAa,EADb,AAAe,cAG3B,GAGA,AAAY,AAAa,EADb,AAAa,WAM7B,IAA4B,IAK5B,KAAsB,IAMtB,AAAI,EAAe,KAAG,IAMtB,AAA8B,KAC1B,QAAqB,EAAe,MAApC,IAA0C,QAA9C,IAEE,AAAI,AAAe,EADnB,AAAY,AAAa,YACS,KAG7B,AACO,AAAe,SAI7B,cbUF,AAAI,KACF,OAEA,AAAI,EAA0C,MAG5C,EAA0C,MAO1C,AAAI,AAHJ,AAA4B,IAGH,MAEvB,AAAK,IAEE,IAFuC,MAO9C,GAGA,IACK,AAAI,EAAmB,MAAK,AAE7B,AAAC,KAAyC,QAehD,AATA,AAAI,EAAmB,MAGF,KAEC,UAY1B,MAtGA,AAAI,EAAkC,UAI/B,EAAmC,MACxC,AAAwB,MACxB,AAAyB,EAAkC,gBTuH7D,AAAa,IAIb,AAA0B,IAItB,AAAC,OAAgB,AAAC,MAAtB,IAEE,AAAiB,AADR,AAAI,AAAyB,cAIlC,IAAgB,AAAC,YAA+C,MAF/D,IAGH,AAAe,IACf,AAAgB,IAchB,AAAiB,AADR,AAAI,AAAyB,aAEtC,AAAqB,AAAY,EAAqB,SAK1D,AAAgB,EAAgB,MAGhC,AAAI,EAAkB,KAAG,IAKzB,AAAI,GAAmB,KACrB,EAAkB,MAClB,AAAmB,MAKrB,EAAkB,MAGlB,OAGA,AAAI,AAAC,KACH,AAAI,IAGF,OACA,IACK,MAIP,AAAI,IAA6B,AAC/B,QACK,MAKT,AAAI,IAEF,OACA,GACK,cAlJA,OAAU,EAAwB,UAEvC,AAAI,AADa,GACI,KAAG,AACd,SAKZ,AAAI,EAAyB,KAI3B,EAAyB,KAElB,IAKT,AAAqB,AAAY,EAAqB,YWIzC,OXQb,AAA2B,KAE3B,AAAI,IAAkB,EAAiB,SAAG,OAInC,OAAU,EAAwB,MAAlC,IAA4D,aAEjE,AAAI,AADa,GACI,KAAG,AACd,SAKZ,AAAI,EAAyB,KAI3B,EAAyB,KAElB,IAET,AAAI,YAQJ,AAAqB,AAAY,EAAqB,YAiGR,AAAvC,MAAkC,SQrSzC,OAAW,EACS,MACb,EACa,UF2BlB,AAAU,AAAyB,OAA0B,KAC7D,AAAU,AAAyB,OAA0B,KAC7D,AAAU,AAAyB,OAA0B,KAC7D,AAAU,AAAyB,OAA0B,KAC7D,AAAU,AAAyB,OAA0B,KAC7D,AAAU,AAAyB,OAA0B,KAC7D,AAAU,AAAyB,OAA0B,KAC7D,AAAU,AAAyB,OAA0B,KAE7D,AAAW,AAAyB,OAA0B,KAC9D,AAAW,AAAyB,OAA0B,KAE9D,AAAW,AAAyB,OAA0B,KAE9D,AAAiC,AAAyB,OAA0B,KACpF,AAAiC,AAAyB,OAA0B,QGapF,AAAW,AAAyB,OAA+B,KACnE,AAAU,AAAyB,OAA+B,KAElE,IAAmE,OkBtCnE,AAAiC,AAAyB,OAAiC,MAC3F,AAAiC,AAAyB,OAAiC,avBoC3F,AAAW,AAAyB,OAA6B,KACjE,AAAW,AAAyB,OAA6B,KAEjE,AAAiC,AAAyB,OAA6B,KACvF,AAAiC,AAAyB,OAA6B,KAEvF,AAAiC,AAAyB,OAA6B,KACvF,AAAiC,AAAyB,OAA6B,KACvF,AAAiC,AAAyB,OAA6B,KACvF,AAAiC,AAAyB,OAA6B,KACvF,AAAiC,AAAyB,OAA6B,QazBvF,AAAW,AAAyB,OAA6B,KACjE,AAAW,AAAyB,OAA6B,KACjE,AAAW,AAAyB,OAA6B,KAEjE,IAAgE,IAChE,IAA6D,ONgC7D,AAAW,AAAyB,OAA4B,KAChE,AAAU,AAAyB,OAA4B,KAC/D,AAAU,AAAyB,OAA4B,SCb/D,AAAiC,AAAyB,OAA+B,KACzF,AAAW,AAAyB,OAA+B,MACnE,AAAW,AAAyB,OAA+B,MACnE,AAAW,AAAyB,OAA+B,KACnE,AAAW,AAAyB,OAA+B,MAEnE,AAAU,AAAyB,OAA+B,MAClE,AAAU,AAAyB,OAA+B,MAElE,AAAiC,AAAyB,OAA+B,MACzF,AAAW,AAAyB,OAA+B,MACnE,AAAW,AAAyB,OAA+B,SC3BnE,AAAiC,AAAyB,OAA+B,KACzF,AAAW,AAAyB,OAA+B,MACnE,AAAW,AAAyB,OAA+B,MACnE,AAAW,AAAyB,OAA+B,KACnE,AAAW,AAAyB,OAA+B,MAEnE,AAAU,AAAyB,OAA+B,MAClE,AAAU,AAAyB,OAA+B,SCTlE,AAAiC,AAAyB,OAA+B,KACzF,AAAW,AAAyB,OAA+B,MACnE,AAAW,AAAyB,OAA+B,KACnE,AAAW,AAAyB,OAA+B,SCoBnE,AAAiC,AAAyB,OAA+B,MACzF,AAAW,AAAyB,OAA+B,MACnE,AAAW,AAAyB,OAA+B,MACnE,AAAW,AAAyB,OAA+B,KACnE,AAAW,AAAyB,OAA+B,MACnE,AAAW,AAAyB,OAA+B,SfyNjE,GACK,GACE,GACJ,GACA,GACA,GACD,GACG,GACA,GACA,GACA,GAGT,AAAa,OE7Sb,AAAI,AADoB,KACL,eIyCjB,AAAgB,AAAS,AAAyB,YAClD,AAAgB,AAAS,AAAyB,YAClD,AAAgB,AAAS,AAAyB,YAClD,AAAgB,AAAS,AAAyB,YAClD,AAAgB,AAAS,AAAyB,YAClD,AAAgB,AAAS,AAAyB,YAClD,AAAgB,AAAS,AAAyB,YAClD,AAAgB,AAAS,AAAyB,YAElD,AAAmB,AAAU,AAAyB,YACtD,AAAqB,AAAU,AAAyB,YAExD,AAAoB,AAAU,AAAyB,YAEvD,AAAe,AAAkC,AAAyB,YAC1E,AAAgB,AAAkC,AAAyB,eGA3E,AAAgC,AAAU,AAAyB,YACnE,AAAqB,AAAS,AAAyB,YAEvD,AAA4B,QACxB,AAAiB,WkBzCrB,AAAmC,AAAkC,AAAyB,aAC9F,AAAwC,AAAkC,AAAyB,aAExF,AAAuB,QACvB,AAAyB,WDjB7B,AAAa,WtB2DpB,AAAwB,AAAU,AAAyB,YAC3D,AAAwB,AAAU,AAAyB,YAE3D,AAA6B,AAAkC,AAAyB,YACxF,AAA8B,AAAkC,AAAyB,YAEzF,AAAmB,AAAkC,AAAyB,YAC9E,AAAgB,AAAkC,AAAyB,YAC3E,AAAgB,AAAkC,AAAyB,YAC3E,AAAgB,AAAkC,AAAyB,YAC3E,AAAgB,AAAkC,AAAyB,ea9B3E,AAAsB,AAAU,AAAyB,YACzD,AAA8B,AAAU,AAAyB,YACjE,AAAqC,AAAU,AAAyB,YAExE,AAAyB,QAClB,AAAmB,QACnB,AAAkB,QAClB,AAAmB,WNqG5B,AAAwB,OA1EtB,AAAkC,AAAU,AAAyB,YACrE,AAA+B,AAAS,AAAyB,YACjE,AAAuB,AAAS,AAAyB,YAEzD,OCNA,AAAqB,AAAkC,AAAyB,YAChF,AAA0B,AAAU,AAAyB,aAC7D,AAA2B,AAAU,AAAyB,aAC9D,AAAyB,AAAU,AAAyB,YAC5D,AAAkB,AAAU,AAAyB,aAErD,AAAqB,AAAS,AAAyB,aACvD,AAAkC,AAAS,AAAyB,aAEpE,AAA0B,AAAkC,AAAyB,aACrF,AAAwB,AAAU,AAAyB,aAC3D,AAAgC,AAAU,AAAyB,gBC/BnE,AAAqB,AAAkC,AAAyB,YAChF,AAA0B,AAAU,AAAyB,aAC7D,AAA2B,AAAU,AAAyB,aAC9D,AAAyB,AAAU,AAAyB,YAC5D,AAAkB,AAAU,AAAyB,aAErD,AAAqB,AAAS,AAAyB,aACvD,AAAkC,AAAS,AAAyB,gBCbpE,AAAqB,AAAkC,AAAyB,YAChF,AAA0B,AAAU,AAAyB,aAC7D,AAAyB,AAAU,AAAyB,YAC5D,AAA6B,AAAU,AAAyB,gBCsBhE,AAAqB,AAAkC,AAAyB,aAChF,AAA0B,AAAU,AAAyB,aAC7D,AAA2B,AAAU,AAAyB,aAC9D,AAAyB,AAAU,AAAyB,YAC5D,AAAkB,AAAU,AAAyB,aACrD,AAAuC,AAAU,AAAyB,gBfiOxE,GACK,GACE,GACJ,GACA,GACA,GACD,GACG,GACA,GACA,GACA,GAGT,AAAa,OArVb,AAAI,uC0B4MG,yBADW,AAAP,KAEO,AAAP,KAEO,AAAP,KAEO,AAAP,KAEO,AAAP,KAEO,AAAP,KAEO,AAAP,KAEO,AAAP,KAEA,QAKX,wBAIO,yBAFH,QACA,GAEA,QACA,GAEA,QACA,GAEA,QACA,GAEA,QACA,GAEA,QACA,GAEA,QACA,GAEA,YC3FJ,AAAwC,KACxC,SDHA,AAAgB,IAIhB,AAAI,AAAC,QAA6C,AACxB,KAI1B,EAA4C,KAG5C,IAEE,AAAuB,IACvB,AAAI,EAAY,KAAG,AACE,KASrB,AAAI,gBAAuC,AAChB,KAI3B,AAAI,KAAuB,YAAmB,AACnB,KAI3B,IAA4B,QAQ9B,EAA4C,SA9F5C,AAAI,EAAK,KAAG,AACS,MACd,AACgB,MAGvB,AAAI,EAAQ,KAAG,AACM,MACd,AACgB,MAGvB,AAAI,EAAO,KAAG,AACO,MACd,AACgB,MAGvB,AAAI,EAAO,KAAG,AACO,MACd,AACgB,MAGvB,AAAI,EAAI,KAAG,AACU,MACd,AACgB,MAGvB,AAAI,EAAI,KAAG,AACU,MACd,AACgB,MAGvB,AAAI,EAAS,KAAG,AACK,MACd,AACgB,MAGvB,AAAI,EAAQ,KAAG,AACM,MACd,AACgB,SS9KZ,KAIA,KAIA,KAIA,KAIA,KAIA,KAIA,KAIA,KAIA,KAIA,QCbX,MACA,AAAI,KAA4B,OAIhC,MACA,AAAI,KAA4B,OAIhC,IAAqB,EAAI,MACvB,EAAK,AAAa,MAAG,EAAI,MAiCvB,AAA2B,EANE,AAH7B,AAA0B,EAAwB,AATpB,EAAuB,GASkB,IAVzC,EAAuB,MAaS,UAmB9D,AAAwB,EAAsB,KAS9C,AAAe,EADS,EAAsB,MAU9C,AAA2B,IAC3B,AAAI,IAAkB,EAAY,WAAG,AACjB,EAAiC,QAGrD,AAAI,AAAe,QAAqB,AAIvB,QAMjB,AAAsB,IACtB,AAAI,AAAe,QAAqB,AACzB,KAMf,AAAsC,AAAiB,EAAkB,EAAe,aAQxF,AAA0B,IAC1B,AAAI,EARkC,AAAiB,EAAqC,YAW1F,KAEF,AAAI,QAA0D,EAC1C,MAIpB,AAAsB,AAAC,EAAI,MAAW,KAEtC,AAAI,IAAkB,EAAY,WAShC,AAAe,AAAyB,EAHxC,AAA2B,AAHN,EAAkB,KAGsC,YAI7E,AAAiB,AAAyB,SAC1C,AAAgB,AAAyB,SAGzC,AADA,AAAkB,cAElB,AAAU,EAAS,QACnB,AAAU,EAAS,SAInB,AAA2B,M1BnIQ,O0BqInC,EAAK,AAAa,MAAG,EAAI,KAEvB,AADkB,eADQ,cAlIF,aADF,0CJHqB,KAC5B,KACA,wCI8IvB,IAAoC,EAAmB,KACrD,EAAK,AAA4B,MAAG,EAAmB,KAErD,AAAsB,IACtB,AAAI,EAAmB,KAAM,AACd,KAIf,IACA,AAAI,EAAmB,KAAM,EACjB,MAEZ,AAAS,EAAU,KACnB,AAAI,EAAmB,KACZ,EAAS,EAAoB,KAE7B,QAIX,MACA,AAAI,EAAmB,KAAM,OAK7B,EAAK,AAAqB,MAAG,EAAY,gBAKrC,EACA,IAEA,EAAmB,GACnB,EAAmB,aAGnB,UAZwC,aA1Be,aADF,gBpC3J/D,AAAI,AAAO,QAA6B,AAC1B,GAA4B,2BoCTqB","sourceRoot":"assemblyscript:///","sourceContents":["// Constants that will be shared by the wasm core of the emulator\n// And libraries built around the wasm (such as the official JS), or @CryZe wasmboy-rs\n\n// ----------------------------------\n// Wasmboy Memory Map\n// https://docs.google.com/spreadsheets/d/17xrEzJk5-sCB9J2mMJcVnzhbE-XH_NvczVSQH9OHvRk/edit?usp=sharing\n// ----------------------------------\n\n// WasmBoy\nexport const WASMBOY_MEMORY_LOCATION: i32 = 0x000000;\nexport const WASMBOY_MEMORY_SIZE: i32 = 0x8b0000;\nexport const WASMBOY_WASM_PAGES: i32 = WASMBOY_MEMORY_SIZE / 1024 / 64;\n\n// AssemblyScript\nexport const ASSEMBLYSCRIPT_MEMORY_LOCATION: i32 = 0x000000;\nexport const ASSEMBLYSCRIPT_MEMORY_SIZE: i32 = 0x000400;\n\n// WasmBoy States\nexport const WASMBOY_STATE_LOCATION: i32 = 0x000400;\nexport const WASMBOY_STATE_SIZE: i32 = 0x000400;\n\n// Gameboy Internal Memory\nexport const GAMEBOY_INTERNAL_MEMORY_LOCATION: i32 = 0x000800;\nexport const GAMEBOY_INTERNAL_MEMORY_SIZE: i32 = 0x00ffff;\nexport const VIDEO_RAM_LOCATION: i32 = 0x000800;\nexport const VIDEO_RAM_SIZE: i32 = 0x004000;\nexport const WORK_RAM_LOCATION: i32 = 0x004800;\nexport const WORK_RAM_SIZE: i32 = 0x008000;\nexport const OTHER_GAMEBOY_INTERNAL_MEMORY_LOCATION: i32 = 0x00c800;\nexport const OTHER_GAMEBOY_INTERNAL_MEMORY_SIZE: i32 = 0x004000;\n\n// Graphics Output\nexport const GRAPHICS_OUTPUT_LOCATION: i32 = 0x010800;\nexport const GRAPHICS_OUTPUT_SIZE: i32 = 0x07f400;\nexport const GBC_PALETTE_LOCATION: i32 = 0x010800;\nexport const GBC_PALETTE_SIZE: i32 = 0x000200;\nexport const BG_PRIORITY_MAP_LOCATION: i32 = 0x011000;\nexport const BG_PRIORITY_MAP_SIZE: i32 = 0x005c00;\nexport const FRAME_LOCATION: i32 = 0x016c00;\nexport const FRAME_SIZE: i32 = 0x016c00;\nexport const BACKGROUND_MAP_LOCATION: i32 = 0x038c00;\nexport const BACKGROUND_MAP_SIZE: i32 = 0x030000;\nexport const TILE_DATA_LOCATION: i32 = 0x068c00;\nexport const TILE_DATA_SIZE: i32 = 0x024000;\nexport const OAM_TILES_LOCATION: i32 = 0x08cc00;\nexport const OAM_TILES_SIZE: i32 = 0x003000;\n\n// Audio Output\nexport const AUDIO_BUFFER_LOCATION: i32 = 0x08fc00;\nexport const AUDIO_BUFFER_SIZE: i32 = 0x020000;\n\n// Catridge Memory\nexport const CARTRIDGE_RAM_LOCATION: i32 = 0x0afc00;\nexport const CARTRIDGE_RAM_SIZE: i32 = 0x020000;\nexport const CARTRIDGE_ROM_LOCATION: i32 = 0x0cfc00;\nexport const CARTRIDGE_ROM_SIZE: i32 = 0x7e0400;\n","// Imports\nimport { WASMBOY_STATE_LOCATION } from './constants';\nimport { Cpu, initializeCpu, executeOpcode } from './cpu/index';\nimport { Graphics, initializeGraphics, initializePalette, updateGraphics, batchProcessGraphics } from './graphics/index';\nimport { Interrupts, checkInterrupts } from './interrupts/index';\nimport { Joypad } from './joypad/index';\nimport { Memory, initializeCartridge, initializeDma, eightBitStoreIntoGBMemory, eightBitLoadFromGBMemory } from './memory/index';\nimport { Timers, initializeTimers, updateTimers, batchProcessTimers } from './timers/index';\nimport {\n Sound,\n initializeSound,\n Channel1,\n Channel2,\n Channel3,\n Channel4,\n updateSound,\n getNumberOfSamplesInAudioBuffer\n} from './sound/index';\nimport { WASMBOY_WASM_PAGES } from './constants';\nimport { Config } from './config';\nimport { hexLog, log } from './helpers/index';\nimport { u16Portable } from './portable/portable';\n\n// Grow our memory to the specified size\nif (memory.size() < WASMBOY_WASM_PAGES) {\n memory.grow(WASMBOY_WASM_PAGES - memory.size());\n}\n\n// Function to track if the core has started\nlet hasStarted: boolean = false;\nexport function hasCoreStarted(): i32 {\n if (hasStarted) {\n return 1;\n }\n\n return 0;\n}\n\n// Function to configure & initialize wasmboy\nexport function config(\n enableBootRom: i32,\n useGbcWhenAvailable: i32,\n audioBatchProcessing: i32,\n graphicsBatchProcessing: i32,\n timersBatchProcessing: i32,\n graphicsDisableScanlineRendering: i32,\n audioAccumulateSamples: i32,\n tileRendering: i32,\n tileCaching: i32\n): void {\n // TODO: depending on the boot rom, initialization may be different\n // From: http://www.codeslinger.co.uk/pages/projects/gameboy/hardware.html\n // All values default to zero in memory, so not setting them yet\n // log('initializing (includeBootRom=$0)', 1, enableBootRom);\n\n if (enableBootRom > 0) {\n Config.enableBootRom = true;\n } else {\n Config.enableBootRom = false;\n }\n\n if (useGbcWhenAvailable > 0) {\n Config.useGbcWhenAvailable = true;\n } else {\n Config.useGbcWhenAvailable = false;\n }\n\n if (audioBatchProcessing > 0) {\n Config.audioBatchProcessing = true;\n } else {\n Config.audioBatchProcessing = false;\n }\n\n if (graphicsBatchProcessing > 0) {\n Config.graphicsBatchProcessing = true;\n } else {\n Config.graphicsBatchProcessing = false;\n }\n\n if (timersBatchProcessing > 0) {\n Config.timersBatchProcessing = true;\n } else {\n Config.timersBatchProcessing = false;\n }\n\n if (graphicsDisableScanlineRendering > 0) {\n Config.graphicsDisableScanlineRendering = true;\n } else {\n Config.graphicsDisableScanlineRendering = false;\n }\n\n if (audioAccumulateSamples > 0) {\n Config.audioAccumulateSamples = true;\n } else {\n Config.audioAccumulateSamples = false;\n }\n\n if (tileRendering > 0) {\n Config.tileRendering = true;\n } else {\n Config.tileRendering = false;\n }\n\n if (tileCaching > 0) {\n Config.tileCaching = true;\n } else {\n Config.tileCaching = false;\n }\n\n initialize();\n}\n\n// Function to initiialize the core\nfunction initialize(): void {\n // Initialization variables from BGB\n\n // First, try to switch to Gameboy Color Mode\n // Get our GBC support from the cartridge header\n // http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header\n let gbcType: i32 = eightBitLoadFromGBMemory(0x0143);\n\n // Detecting GBC http://bgb.bircd.org/pandocs.htm#cgbregisters\n if (gbcType === 0xc0 || (Config.useGbcWhenAvailable && gbcType === 0x80)) {\n Cpu.GBCEnabled = true;\n } else {\n Cpu.GBCEnabled = false;\n }\n\n // Call our respective classes intialization\n initializeCpu();\n initializeCartridge();\n initializeDma();\n initializeGraphics();\n initializePalette();\n initializeSound();\n initializeTimers();\n\n // Various Other Registers\n if (Cpu.GBCEnabled) {\n // Various other registers\n eightBitStoreIntoGBMemory(0xff70, 0xf8);\n eightBitStoreIntoGBMemory(0xff4f, 0xfe);\n eightBitStoreIntoGBMemory(0xff4d, 0x7e);\n eightBitStoreIntoGBMemory(0xff00, 0xcf);\n // FF01 = 0x00\n eightBitStoreIntoGBMemory(0xff02, 0x7c);\n\n eightBitStoreIntoGBMemory(0xff0f, 0xe1);\n // 0xFFFF = 0x00\n\n // Undocumented from Pandocs\n eightBitStoreIntoGBMemory(0xff6c, 0xfe);\n eightBitStoreIntoGBMemory(0xff75, 0x8f);\n } else {\n eightBitStoreIntoGBMemory(0xff70, 0xff);\n eightBitStoreIntoGBMemory(0xff4f, 0xff);\n eightBitStoreIntoGBMemory(0xff4d, 0xff);\n eightBitStoreIntoGBMemory(0xff00, 0xcf);\n // FF01 = 0x00\n eightBitStoreIntoGBMemory(0xff02, 0x7e);\n\n eightBitStoreIntoGBMemory(0xff0f, 0xe1);\n // 0xFFFF = 0x00\n }\n\n // Reset hasStarted, since we are now reset\n hasStarted = false;\n}\n\n// Public funciton to run opcodes until,\n// a frame is ready, or error.\n// Return values:\n// -1 = error\n// 0 = render a frame\nexport function executeFrame(): i32 {\n let error: boolean = false;\n let numberOfCycles: i32 = -1;\n\n while (!error && Cpu.currentCycles < Cpu.MAX_CYCLES_PER_FRAME()) {\n numberOfCycles = executeStep();\n if (numberOfCycles < 0) {\n error = true;\n }\n }\n\n // Find our exit reason\n if (Cpu.currentCycles >= Cpu.MAX_CYCLES_PER_FRAME()) {\n // Render a frame\n\n // Reset our currentCycles\n Cpu.currentCycles -= Cpu.MAX_CYCLES_PER_FRAME();\n\n return 0;\n }\n // TODO: Boot ROM handling\n\n // There was an error, return -1, and push the program counter back to grab the error opcode\n Cpu.programCounter = u16Portable(Cpu.programCounter - 1);\n return -1;\n}\n\n// Public Function to run opcodes until,\n// a frame is ready, audio bufer is filled, or error\n// -1 = error\n// 0 = render a frame\n// 1 = output audio\nexport function executeFrameAndCheckAudio(maxAudioBuffer: i32): i32 {\n let error: boolean = false;\n let numberOfCycles: i32 = -1;\n let audioBufferSize: i32 = 1024;\n\n if (maxAudioBuffer && maxAudioBuffer > 0) {\n audioBufferSize = maxAudioBuffer;\n }\n\n while (!error && Cpu.currentCycles < Cpu.MAX_CYCLES_PER_FRAME() && getNumberOfSamplesInAudioBuffer() < audioBufferSize) {\n numberOfCycles = executeStep();\n if (numberOfCycles < 0) {\n error = true;\n }\n }\n\n // Find our exit reason\n if (Cpu.currentCycles >= Cpu.MAX_CYCLES_PER_FRAME()) {\n // Render a frame\n\n // Reset our currentCycles\n Cpu.currentCycles -= Cpu.MAX_CYCLES_PER_FRAME();\n\n return 0;\n }\n if (getNumberOfSamplesInAudioBuffer() >= audioBufferSize) {\n // Output Audio\n return 1;\n }\n\n // TODO: Boot ROM handling\n\n // There was an error, return -1, and push the program counter back to grab the error opcode\n Cpu.programCounter = u16Portable(Cpu.programCounter - 1);\n return -1;\n}\n\n// Function to execute an opcode, and update other gameboy hardware.\n// http://www.codeslinger.co.uk/pages/projects/gameboy/beginning.html\nexport function executeStep(): i32 {\n // Set has started to 1 since we ran a emulation step\n hasStarted = true;\n\n // Get the opcode, and additional bytes to be handled\n // Number of cycles defaults to 4, because while we're halted, we run 4 cycles (according to matt :))\n let numberOfCycles: i32 = 4;\n let opcode: i32 = 0;\n\n // Cpu Halting best explained: https://www.reddit.com/r/EmuDev/comments/5ie3k7/infinite_loop_trying_to_pass_blarggs_interrupt/db7xnbe/\n if (!Cpu.isHalted && !Cpu.isStopped) {\n opcode = eightBitLoadFromGBMemory(Cpu.programCounter);\n numberOfCycles = executeOpcode(opcode);\n } else {\n // if we were halted, and interrupts were disabled but interrupts are pending, stop waiting\n if (Cpu.isHalted && !Interrupts.masterInterruptSwitch && Interrupts.areInterruptsPending()) {\n Cpu.isHalted = false;\n Cpu.isStopped = false;\n\n // Need to run the next opcode twice, it's a bug menitoned in\n // The reddit comment mentioned above\n\n // CTRL+F \"low-power\" on gameboy cpu manual\n // http://marc.rawer.de/Gameboy/Docs/GBCPUman.pdf\n // E.g\n // 0x76 - halt\n // FA 34 12 - ld a,(1234)\n // Becomes\n // FA FA 34 ld a,(34FA)\n // 12 ld (de),a\n opcode = eightBitLoadFromGBMemory(Cpu.programCounter);\n numberOfCycles = executeOpcode(opcode);\n Cpu.programCounter = u16Portable(Cpu.programCounter - 1);\n }\n }\n\n // blarggFixes, don't allow register F to have the bottom nibble\n Cpu.registerF = Cpu.registerF & 0xf0;\n\n // Check if there was an error decoding the opcode\n if (numberOfCycles <= 0) {\n return numberOfCycles;\n }\n\n // Check if we did a DMA TRansfer, if we did add the cycles\n if (Memory.DMACycles > 0) {\n numberOfCycles += Memory.DMACycles;\n Memory.DMACycles = 0;\n }\n\n // Interrupt Handling requires 20 cycles\n // https://github.com/Gekkio/mooneye-gb/blob/master/docs/accuracy.markdown#what-is-the-exact-timing-of-cpu-servicing-an-interrupt\n numberOfCycles += checkInterrupts();\n\n // Finally, Add our number of cycles to the CPU Cycles\n Cpu.currentCycles += numberOfCycles;\n\n // Check other Gameboy components\n if (!Cpu.isStopped) {\n if (Config.graphicsBatchProcessing) {\n // Need to do this, since a lot of things depend on the scanline\n // Batch processing will simply return if the number of cycles is too low\n Graphics.currentCycles += numberOfCycles;\n batchProcessGraphics();\n } else {\n updateGraphics(numberOfCycles);\n }\n\n if (Config.audioBatchProcessing) {\n Sound.currentCycles += numberOfCycles;\n } else {\n updateSound(numberOfCycles);\n }\n }\n\n if (Config.timersBatchProcessing) {\n // Batch processing will simply return if the number of cycles is too low\n Timers.currentCycles += numberOfCycles;\n batchProcessTimers();\n } else {\n updateTimers(numberOfCycles);\n }\n\n return numberOfCycles;\n}\n\n// Function to return an address to store into save state memory\n// this is to regulate our 20 slots\n// https://docs.google.com/spreadsheets/d/17xrEzJk5-sCB9J2mMJcVnzhbE-XH_NvczVSQH9OHvRk/edit?usp=sharing\nexport function getSaveStateMemoryOffset(offset: i32, saveStateSlot: i32): i32 {\n // 50 byutes per save state memory partiton sli32\n return WASMBOY_STATE_LOCATION + offset + 50 * saveStateSlot;\n}\n\n// Function to save state to memory for all of our classes\nexport function saveState(): void {\n Cpu.saveState();\n Graphics.saveState();\n Interrupts.saveState();\n Joypad.saveState();\n Memory.saveState();\n Timers.saveState();\n Sound.saveState();\n Channel1.saveState();\n Channel2.saveState();\n Channel3.saveState();\n Channel4.saveState();\n\n // Reset hasStarted, since we are now reset\n hasStarted = false;\n}\n\n// Function to load state from memory for all of our classes\nexport function loadState(): void {\n Cpu.loadState();\n Graphics.loadState();\n Interrupts.loadState();\n Joypad.loadState();\n Memory.loadState();\n Timers.loadState();\n Sound.loadState();\n Channel1.loadState();\n Channel2.loadState();\n Channel3.loadState();\n Channel4.loadState();\n\n // Reset hasStarted, since we are now reset\n hasStarted = false;\n}\n","export class Config {\n // Boot Rom\n static enableBootRom: boolean = false;\n\n // GBC Preference\n static useGbcWhenAvailable: boolean = true;\n\n // Batch Processing\n static audioBatchProcessing: boolean = false;\n static graphicsBatchProcessing: boolean = false;\n static timersBatchProcessing: boolean = false;\n\n // Scanline Rendering\n static graphicsDisableScanlineRendering: boolean = false;\n\n // Acumulate Sound Samples\n static audioAccumulateSamples: boolean = false;\n\n // Tile Rednering\n static tileRendering: boolean = false;\n static tileCaching: boolean = false;\n}\n","// Load/Read functionality for memory\nimport { checkReadTraps } from './readTraps';\nimport { getWasmBoyOffsetFromGameBoyOffset } from './memoryMap';\nimport { concatenateBytes, performanceTimestamp } from '../helpers/index';\n\nexport function eightBitLoadFromGBMemory(gameboyOffset: i32): i32 {\n return load(getWasmBoyOffsetFromGameBoyOffset(gameboyOffset));\n}\n\nexport function eightBitLoadFromGBMemoryWithTraps(offset: i32): i32 {\n let readTrapResult: i32 = checkReadTraps(offset);\n switch (readTrapResult) {\n case -1:\n return eightBitLoadFromGBMemory(offset);\n default:\n return readTrapResult;\n }\n}\n\nexport function sixteenBitLoadFromGBMemory(offset: i32): i32 {\n // Get our low byte\n let lowByte: i32 = 0;\n let lowByteReadTrapResult: i32 = checkReadTraps(offset);\n switch (lowByteReadTrapResult) {\n case -1:\n lowByte = eightBitLoadFromGBMemory(offset);\n break;\n default:\n lowByte = lowByteReadTrapResult;\n break;\n }\n\n // Get the next offset for the second byte\n let nextOffset: i32 = offset + 1;\n\n // Get our high byte\n let highByte: i32 = 0;\n let highByteReadTrapResult: i32 = checkReadTraps(nextOffset);\n switch (highByteReadTrapResult) {\n case -1:\n highByte = eightBitLoadFromGBMemory(nextOffset);\n break;\n default:\n highByte = highByteReadTrapResult;\n break;\n }\n\n // Concatenate the bytes and return\n return concatenateBytes(highByte, lowByte);\n}\n\nexport function loadBooleanDirectlyFromWasmMemory(offset: i32): boolean {\n let booleanAsInt: i32 = load(offset);\n if (booleanAsInt > 0) {\n return true;\n }\n return false;\n}\n","// WasmBoy memory map:\n// https://docs.google.com/spreadsheets/d/17xrEzJk5-sCB9J2mMJcVnzhbE-XH_NvczVSQH9OHvRk/edit?usp=sharing\nimport {\n WASMBOY_MEMORY_LOCATION,\n ASSEMBLYSCRIPT_MEMORY_LOCATION,\n WASMBOY_STATE_LOCATION,\n GAMEBOY_INTERNAL_MEMORY_LOCATION,\n VIDEO_RAM_LOCATION,\n WORK_RAM_LOCATION,\n OTHER_GAMEBOY_INTERNAL_MEMORY_LOCATION,\n GRAPHICS_OUTPUT_LOCATION,\n GBC_PALETTE_LOCATION,\n BG_PRIORITY_MAP_LOCATION,\n FRAME_LOCATION,\n BACKGROUND_MAP_LOCATION,\n TILE_DATA_LOCATION,\n OAM_TILES_LOCATION,\n AUDIO_BUFFER_LOCATION,\n CARTRIDGE_RAM_LOCATION,\n CARTRIDGE_ROM_LOCATION\n} from '../constants';\nimport { Memory } from './memory';\nimport { eightBitLoadFromGBMemory } from './load';\nimport { getRomBankAddress, getRamBankAddress } from './banking';\nimport { Cpu } from '../cpu/index';\nimport { hexLog } from '../helpers/index';\n\n// Private function to translate a offset meant for the gameboy memory map\n// To the wasmboy memory map\n// Following: http://gameboy.mongenel.com/dmg/asmmemmap.html\n// And https://github.com/Dooskington/GameLad/wiki/Part-11---Memory-Bank-Controllers\n// Performance help from @dcodeIO, and awesome-gbdev\nexport function getWasmBoyOffsetFromGameBoyOffset(gameboyOffset: i32): i32 {\n // Get the top byte and switch\n let gameboyOffsetHighByte: i32 = gameboyOffset >> 12;\n switch (gameboyOffsetHighByte) {\n case 0x00:\n case 0x01:\n case 0x02:\n case 0x03:\n // Cartridge ROM - Bank 0 (fixed)\n // 0x0000 -> 0x0D2400\n return gameboyOffset + CARTRIDGE_ROM_LOCATION;\n case 0x04:\n case 0x05:\n case 0x06:\n case 0x07:\n // Cartridge ROM - Switchable Banks 1-xx\n // 0x4000 -> (0x0D2400 + 0x4000)\n return getRomBankAddress(gameboyOffset) + CARTRIDGE_ROM_LOCATION;\n case 0x08:\n case 0x09:\n // Video RAM\n // 0x8000 -> 0x000400\n let vramBankId: i32 = 0;\n if (Cpu.GBCEnabled) {\n // Find our current VRAM Bank\n vramBankId = eightBitLoadFromGBMemory(Memory.memoryLocationGBCVRAMBank) & 0x01;\n // Even though We added another 0x2000, the Cartridge ram is pulled out of our Internal Memory Space\n // Therefore, we do not need to adjust for this extra 0x2000\n }\n\n return gameboyOffset - Memory.videoRamLocation + VIDEO_RAM_LOCATION + 0x2000 * vramBankId;\n case 0x0a:\n case 0x0b:\n // Cartridge RAM - A.K.A External RAM\n // 0xA000 -> 0x008400\n return getRamBankAddress(gameboyOffset) + CARTRIDGE_RAM_LOCATION;\n case 0x0c:\n // Gameboy Ram Bank 0\n // 0xC000 -> 0x000400\n // Don't need to add head, since we move out 0x200 from the cartridge ram\n return gameboyOffset - Memory.internalRamBankZeroLocation + WORK_RAM_LOCATION;\n case 0x0d:\n // Gameboy Ram Banks, Switchable in GBC Mode\n // 0xD000 -> 0x000400\n // In CGB Mode 32 KBytes internal RAM are available.\n // This memory is divided into 8 banks of 4 KBytes each.\n // Bank 0 is always available in memory at C000-CFFF,\n // Bank 1-7 can be selected into the address space at D000-DFFF.\n // http://gbdev.gg8.se/wiki/articles/CGB_Registers#FF70_-_SVBK_-_CGB_Mode_Only_-_WRAM_Bank\n // Get the last 3 bits to find our wram ID\n let wramBankId: i32 = 0;\n if (Cpu.GBCEnabled) {\n wramBankId = eightBitLoadFromGBMemory(Memory.memoryLocationGBCWRAMBank) & 0x07;\n }\n if (wramBankId < 1) {\n wramBankId = 1;\n }\n // (0x1000 * (wramBankId - 1)) -> To find the correct wram bank.\n // wramBankId - 1, because we alreayd have the space for wramBank 1, and are currently in it\n // So need to address space for 6 OTHER banks\n return gameboyOffset - Memory.internalRamBankZeroLocation + WORK_RAM_LOCATION + 0x1000 * (wramBankId - 1);\n default:\n // Everything Else after Gameboy Ram Banks\n // 0xE000 -> 0x000400\n // 0x6000 For the Extra WRAM Banks\n return gameboyOffset - Memory.echoRamLocation + OTHER_GAMEBOY_INTERNAL_MEMORY_LOCATION;\n }\n}\n","// WasmBoy memory map:\n// https://docs.google.com/spreadsheets/d/17xrEzJk5-sCB9J2mMJcVnzhbE-XH_NvczVSQH9OHvRk/edit?usp=sharing\nimport {\n WASMBOY_MEMORY_LOCATION,\n ASSEMBLYSCRIPT_MEMORY_LOCATION,\n WASMBOY_STATE_LOCATION,\n GAMEBOY_INTERNAL_MEMORY_LOCATION,\n VIDEO_RAM_LOCATION,\n WORK_RAM_LOCATION,\n OTHER_GAMEBOY_INTERNAL_MEMORY_LOCATION,\n GRAPHICS_OUTPUT_LOCATION,\n GBC_PALETTE_LOCATION,\n BG_PRIORITY_MAP_LOCATION,\n FRAME_LOCATION,\n BACKGROUND_MAP_LOCATION,\n TILE_DATA_LOCATION,\n OAM_TILES_LOCATION,\n AUDIO_BUFFER_LOCATION,\n CARTRIDGE_RAM_LOCATION,\n CARTRIDGE_ROM_LOCATION\n} from '../constants';\nimport { getSaveStateMemoryOffset } from '../core';\nimport { eightBitLoadFromGBMemory, loadBooleanDirectlyFromWasmMemory } from './load';\nimport { eightBitStoreIntoGBMemory, storeBooleanDirectlyToWasmMemory } from './store';\nimport { handleBanking } from './banking';\nimport { checkBitOnByte, resetBitOnByte, hexLog } from '../helpers/index';\n\nexport class Memory {\n // ----------------------------------\n // Gameboy Memory Map\n // ----------------------------------\n // https://github.com/AntonioND/giibiiadvance/blob/master/docs/TCAGBD.pdf\n // http://gameboy.mongenel.com/dmg/asmmemmap.html\n // using Arrays, first index is start, second is end\n static readonly cartridgeRomLocation: i32 = 0x0000;\n\n static readonly switchableCartridgeRomLocation: i32 = 0x4000;\n\n static readonly videoRamLocation: i32 = 0x8000;\n\n static readonly cartridgeRamLocation: i32 = 0xa000;\n\n static readonly internalRamBankZeroLocation: i32 = 0xc000;\n\n // This ram bank is switchable\n static readonly internalRamBankOneLocation: i32 = 0xd000;\n\n static readonly echoRamLocation: i32 = 0xe000;\n\n static readonly spriteInformationTableLocation: i32 = 0xfe00;\n\n static readonly spriteInformationTableLocationEnd: i32 = 0xfe9f;\n\n static readonly unusableMemoryLocation: i32 = 0xfea0;\n static readonly unusableMemoryEndLocation: i32 = 0xfeff;\n\n // Hardware I/O, 0xFF00 -> 0xFF7F\n // Zero Page, 0xFF80 -> 0xFFFE\n // Intterupt Enable Flag, 0xFFFF\n\n // ----------------------------------\n // Rom/Ram Banking\n // ----------------------------------\n // http://gbdev.gg8.se/wiki/articles/Memory_Bank_Controllers#MBC3_.28max_2MByte_ROM_and.2For_32KByte_RAM_and_Timer.29\n // http://www.codeslinger.co.uk/pages/projects/gameboy/banking.html\n static currentRomBank: i32 = 0x00;\n static currentRamBank: i32 = 0x00;\n static isRamBankingEnabled: boolean = false;\n static isMBC1RomModeEnabled: boolean = true;\n\n // Cartridge Types\n // http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header\n static isRomOnly: boolean = true;\n static isMBC1: boolean = false;\n static isMBC2: boolean = false;\n static isMBC3: boolean = false;\n static isMBC5: boolean = false;\n\n // DMA\n static memoryLocationHdmaSourceHigh: i32 = 0xff51;\n static memoryLocationHdmaSourceLow: i32 = 0xff52;\n static memoryLocationHdmaDestinationHigh: i32 = 0xff53;\n static memoryLocationHdmaDestinationLow: i32 = 0xff54;\n static memoryLocationHdmaTrigger: i32 = 0xff55;\n // Cycles accumulated for DMA\n static DMACycles: i32 = 0;\n // Boolean we will mirror to indicate if Hdma is active\n static isHblankHdmaActive: boolean = false;\n static hblankHdmaTransferLengthRemaining: i32 = 0x00;\n // Store the source and destination for performance, and update as needed\n static hblankHdmaSource: i32 = 0x00;\n static hblankHdmaDestination: i32 = 0x00;\n\n // GBC Registers\n static memoryLocationGBCVRAMBank: i32 = 0xff4f;\n static memoryLocationGBCWRAMBank: i32 = 0xff70;\n\n // Save States\n\n static readonly saveStateSlot: i32 = 4;\n\n // Function to save the state of the class\n static saveState(): void {\n store(getSaveStateMemoryOffset(0x00, Memory.saveStateSlot), Memory.currentRomBank);\n store(getSaveStateMemoryOffset(0x02, Memory.saveStateSlot), Memory.currentRamBank);\n\n storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x04, Memory.saveStateSlot), Memory.isRamBankingEnabled);\n storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x05, Memory.saveStateSlot), Memory.isMBC1RomModeEnabled);\n\n storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x06, Memory.saveStateSlot), Memory.isRomOnly);\n storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x07, Memory.saveStateSlot), Memory.isMBC1);\n storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x08, Memory.saveStateSlot), Memory.isMBC2);\n storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x09, Memory.saveStateSlot), Memory.isMBC3);\n storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x0a, Memory.saveStateSlot), Memory.isMBC5);\n }\n\n // Function to load the save state from memory\n static loadState(): void {\n Memory.currentRomBank = load(getSaveStateMemoryOffset(0x00, Memory.saveStateSlot));\n Memory.currentRamBank = load(getSaveStateMemoryOffset(0x02, Memory.saveStateSlot));\n\n Memory.isRamBankingEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x04, Memory.saveStateSlot));\n Memory.isMBC1RomModeEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x05, Memory.saveStateSlot));\n\n Memory.isRomOnly = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x06, Memory.saveStateSlot));\n Memory.isMBC1 = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x07, Memory.saveStateSlot));\n Memory.isMBC2 = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x08, Memory.saveStateSlot));\n Memory.isMBC3 = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x09, Memory.saveStateSlot));\n Memory.isMBC5 = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x0a, Memory.saveStateSlot));\n }\n}\n\nexport function initializeCartridge(): void {\n // Reset stateful variables\n Memory.isRamBankingEnabled = false;\n Memory.isMBC1RomModeEnabled = true;\n\n // Get our game MBC type from the cartridge header\n // http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header\n let cartridgeType: i32 = eightBitLoadFromGBMemory(0x0147);\n\n // Reset our Cartridge types\n Memory.isRomOnly = false;\n Memory.isMBC1 = false;\n Memory.isMBC2 = false;\n Memory.isMBC3 = false;\n Memory.isMBC5 = false;\n\n if (cartridgeType === 0x00) {\n Memory.isRomOnly = true;\n } else if (cartridgeType >= 0x01 && cartridgeType <= 0x03) {\n Memory.isMBC1 = true;\n } else if (cartridgeType >= 0x05 && cartridgeType <= 0x06) {\n Memory.isMBC2 = true;\n } else if (cartridgeType >= 0x0f && cartridgeType <= 0x13) {\n Memory.isMBC3 = true;\n } else if (cartridgeType >= 0x19 && cartridgeType <= 0x1e) {\n Memory.isMBC5 = true;\n }\n\n Memory.currentRomBank = 0x01;\n Memory.currentRamBank = 0x00;\n}\n","// Function to handle rom/rambanking\nimport { Memory } from './memory';\nimport { concatenateBytes, checkBitOnByte, splitLowByte, hexLog } from '../helpers/index';\n\nexport function handleBanking(offset: i32, value: i32): void {\n // Is rom Only does not bank\n if (Memory.isRomOnly) {\n return;\n }\n\n // Enable Ram Banking\n if (offset <= 0x1fff) {\n if (Memory.isMBC2 && !checkBitOnByte(4, value)) {\n // Do Nothing\n return;\n } else {\n let romEnableByte = value & 0x0f;\n if (romEnableByte === 0x00) {\n Memory.isRamBankingEnabled = false;\n } else if (romEnableByte === 0x0a) {\n Memory.isRamBankingEnabled = true;\n }\n }\n } else if (offset <= 0x3fff) {\n if (!Memory.isMBC5 || offset <= 0x2fff) {\n // Change Low Bits on the Current Rom Bank\n if (Memory.isMBC2) {\n Memory.currentRomBank = value & 0x0f;\n }\n\n // Set the number of bottom bytes from the MBC type\n let romBankLowerBits = value;\n if (Memory.isMBC1) {\n // Only want the bottom 5\n romBankLowerBits = romBankLowerBits & 0x1f;\n Memory.currentRomBank = Memory.currentRomBank & 0xe0;\n } else if (Memory.isMBC3) {\n // Only Want the bottom 7\n romBankLowerBits = romBankLowerBits & 0x7f;\n Memory.currentRomBank = Memory.currentRomBank & 0x80;\n } else if (Memory.isMBC5) {\n // Going to switch the whole thing\n Memory.currentRomBank = Memory.currentRomBank & 0x00;\n }\n\n // Set the lower bytes\n Memory.currentRomBank = Memory.currentRomBank | romBankLowerBits;\n return;\n } else {\n // TODO: MBC5 High bits Rom bank, check if this works, not sure about the value\n let highByte: i32 = 0;\n let lowByte: i32 = splitLowByte(Memory.currentRomBank);\n if (value > 0) {\n highByte = 1;\n }\n Memory.currentRomBank = concatenateBytes(highByte, lowByte);\n }\n } else if (!Memory.isMBC2 && offset <= 0x5fff) {\n // ROM / RAM Banking, MBC2 doesn't do this\n if (Memory.isMBC1 && Memory.isMBC1RomModeEnabled) {\n // Do an upper bit rom bank for MBC 1\n // Remove upper bits of currentRomBank\n Memory.currentRomBank = Memory.currentRomBank & 0x1f;\n\n let romBankHigherBits = value & 0xe0;\n\n Memory.currentRomBank = Memory.currentRomBank | romBankHigherBits;\n return;\n }\n\n if (Memory.isMBC3) {\n if (value >= 0x08 && value <= 0x0c) {\n // TODO: MBC3 RTC Register Select\n }\n }\n\n let ramBankBits: i32 = value;\n\n if (!Memory.isMBC5) {\n // Get the bottom 2 bits\n ramBankBits = ramBankBits & 0x03;\n } else {\n // Get the bottom nibble\n ramBankBits = ramBankBits & 0x0f;\n }\n\n // Set our ram bank\n Memory.currentRamBank = ramBankBits;\n return;\n } else if (!Memory.isMBC2 && offset <= 0x7fff) {\n if (Memory.isMBC1) {\n if (checkBitOnByte(0, value)) {\n Memory.isMBC1RomModeEnabled = true;\n } else {\n Memory.isMBC1RomModeEnabled = false;\n }\n }\n // TODO: MBC3 Latch Clock Data\n }\n}\n\nexport function getRomBankAddress(gameboyOffset: i32): i32 {\n let currentRomBank: i32 = Memory.currentRomBank;\n if (!Memory.isMBC5 && currentRomBank === 0) {\n currentRomBank = 1;\n }\n\n // Adjust our gameboy offset relative to zero for the gameboy memory map\n return (0x4000 * currentRomBank + (gameboyOffset - Memory.switchableCartridgeRomLocation));\n}\n\nexport function getRamBankAddress(gameboyOffset: i32): i32 {\n // Adjust our gameboy offset relative to zero for the gameboy memory map\n return (0x2000 * Memory.currentRamBank + (gameboyOffset - Memory.cartridgeRamLocation));\n}\n","import { Config } from '../config';\nimport { getSaveStateMemoryOffset } from '../core';\nimport {\n eightBitStoreIntoGBMemoryWithTraps,\n eightBitStoreIntoGBMemory,\n eightBitLoadFromGBMemoryWithTraps,\n eightBitLoadFromGBMemory,\n initializeCartridge,\n initializeDma,\n loadBooleanDirectlyFromWasmMemory,\n storeBooleanDirectlyToWasmMemory\n} from '../memory/index';\n\nimport { log, hexLog } from '../helpers/index';\n\n// Everything Static as class instances just aren't quite there yet\n// https://github.com/AssemblyScript/assemblyscript/blob/master/tests/compiler/showcase.ts\nexport class Cpu {\n // Status to track if we are in Gameboy Color Mode, and GBC State\n static GBCEnabled: boolean = false;\n static GBCDoubleSpeed: boolean = false;\n\n // 8-bit Cpu.registers\n static registerA: u8 = 0;\n static registerB: u8 = 0;\n static registerC: u8 = 0;\n static registerD: u8 = 0;\n static registerE: u8 = 0;\n static registerH: u8 = 0;\n static registerL: u8 = 0;\n static registerF: u8 = 0;\n\n // 16-bit Cpu.registers\n static stackPointer: u16 = 0;\n // Boot rom from 0x00 to 0x99, all games start at 0x100\n static programCounter: u16 = 0x00;\n\n // Current number of cycles, shouldn't execeed max number of cycles\n static currentCycles: i32 = 0;\n static CLOCK_SPEED(): i32 {\n if (Cpu.GBCDoubleSpeed) {\n // 2^23, thanks binji!\n return 8388608;\n }\n\n return 4194304;\n }\n\n // Cycles Per Frame = Clock Speed / fps\n // So: 4194304 / 59.73\n static MAX_CYCLES_PER_FRAME(): i32 {\n if (Cpu.GBCDoubleSpeed) {\n return 140448;\n }\n\n return 70224;\n }\n\n // HALT and STOP instructions need to stop running opcodes, but simply check timers\n // https://github.com/nakardo/node-gameboy/blob/master/lib/cpu/opcodes.js\n // Matt said is should work to, so it must work!\n static isHalted: boolean = false;\n static isStopped: boolean = false;\n\n // Memory Location for the GBC Speed switch\n static readonly memoryLocationSpeedSwitch: u16 = 0xff4d;\n\n // Save States\n static readonly saveStateSlot: u16 = 0;\n\n // Function to save the state of the class\n static saveState(): void {\n // Registers\n store(getSaveStateMemoryOffset(0x00, Cpu.saveStateSlot), Cpu.registerA);\n store(getSaveStateMemoryOffset(0x01, Cpu.saveStateSlot), Cpu.registerB);\n store(getSaveStateMemoryOffset(0x02, Cpu.saveStateSlot), Cpu.registerC);\n store(getSaveStateMemoryOffset(0x03, Cpu.saveStateSlot), Cpu.registerD);\n store(getSaveStateMemoryOffset(0x04, Cpu.saveStateSlot), Cpu.registerE);\n store(getSaveStateMemoryOffset(0x05, Cpu.saveStateSlot), Cpu.registerH);\n store(getSaveStateMemoryOffset(0x06, Cpu.saveStateSlot), Cpu.registerL);\n store(getSaveStateMemoryOffset(0x07, Cpu.saveStateSlot), Cpu.registerF);\n\n store(getSaveStateMemoryOffset(0x08, Cpu.saveStateSlot), Cpu.stackPointer);\n store(getSaveStateMemoryOffset(0x0a, Cpu.saveStateSlot), Cpu.programCounter);\n\n store(getSaveStateMemoryOffset(0x0c, Cpu.saveStateSlot), Cpu.currentCycles);\n\n storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x11, Cpu.saveStateSlot), Cpu.isHalted);\n storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x12, Cpu.saveStateSlot), Cpu.isStopped);\n }\n\n // Function to load the save state from memory\n static loadState(): void {\n // Registers\n Cpu.registerA = load(getSaveStateMemoryOffset(0x00, Cpu.saveStateSlot));\n Cpu.registerB = load(getSaveStateMemoryOffset(0x01, Cpu.saveStateSlot));\n Cpu.registerC = load(getSaveStateMemoryOffset(0x02, Cpu.saveStateSlot));\n Cpu.registerD = load(getSaveStateMemoryOffset(0x03, Cpu.saveStateSlot));\n Cpu.registerE = load(getSaveStateMemoryOffset(0x04, Cpu.saveStateSlot));\n Cpu.registerH = load(getSaveStateMemoryOffset(0x05, Cpu.saveStateSlot));\n Cpu.registerL = load(getSaveStateMemoryOffset(0x06, Cpu.saveStateSlot));\n Cpu.registerF = load(getSaveStateMemoryOffset(0x07, Cpu.saveStateSlot));\n\n Cpu.stackPointer = load(getSaveStateMemoryOffset(0x08, Cpu.saveStateSlot));\n Cpu.programCounter = load(getSaveStateMemoryOffset(0x0a, Cpu.saveStateSlot));\n\n Cpu.currentCycles = load(getSaveStateMemoryOffset(0x0c, Cpu.saveStateSlot));\n\n Cpu.isHalted = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x11, Cpu.saveStateSlot));\n Cpu.isStopped = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x12, Cpu.saveStateSlot));\n }\n}\n\nexport function initializeCpu(): void {\n // Reset all stateful Cpu variables\n // Cpu.GBCEnabled is done by core/initialize\n Cpu.GBCDoubleSpeed = false;\n Cpu.registerA = 0;\n Cpu.registerB = 0;\n Cpu.registerC = 0;\n Cpu.registerD = 0;\n Cpu.registerE = 0;\n Cpu.registerH = 0;\n Cpu.registerL = 0;\n Cpu.registerF = 0;\n Cpu.stackPointer = 0;\n Cpu.programCounter = 0x00;\n Cpu.currentCycles = 0;\n Cpu.isHalted = false;\n Cpu.isStopped = false;\n\n if (Cpu.GBCEnabled) {\n // CPU Registers\n Cpu.registerA = 0x11;\n Cpu.registerF = 0x80;\n Cpu.registerB = 0x00;\n Cpu.registerC = 0x00;\n Cpu.registerD = 0xff;\n Cpu.registerE = 0x56;\n Cpu.registerH = 0x00;\n Cpu.registerL = 0x0d;\n\n // Cpu Control Flow\n Cpu.programCounter = 0x100;\n Cpu.stackPointer = 0xfffe;\n } else {\n // Cpu Registers\n Cpu.registerA = 0x01;\n Cpu.registerF = 0xb0;\n Cpu.registerB = 0x00;\n Cpu.registerC = 0x13;\n Cpu.registerD = 0x00;\n Cpu.registerE = 0xd8;\n Cpu.registerH = 0x01;\n Cpu.registerL = 0x4d;\n\n // Cpu Control Flow\n Cpu.programCounter = 0x100;\n Cpu.stackPointer = 0xfffe;\n }\n}\n","import { Cpu } from '../cpu/index';\nimport { Memory } from './memory';\nimport { eightBitLoadFromGBMemoryWithTraps, eightBitLoadFromGBMemory } from './load';\nimport { eightBitStoreIntoGBMemoryWithTraps, eightBitStoreIntoGBMemory } from './store';\nimport { concatenateBytes, checkBitOnByte, setBitOnByte, resetBitOnByte, hexLog } from '../helpers/index';\n\nexport function initializeDma(): void {\n if (Cpu.GBCEnabled) {\n // GBC DMA\n eightBitStoreIntoGBMemory(0xff51, 0xff);\n eightBitStoreIntoGBMemory(0xff52, 0xff);\n eightBitStoreIntoGBMemory(0xff53, 0xff);\n eightBitStoreIntoGBMemory(0xff54, 0xff);\n eightBitStoreIntoGBMemory(0xff55, 0xff);\n } else {\n // GBC DMA\n eightBitStoreIntoGBMemory(0xff51, 0xff);\n eightBitStoreIntoGBMemory(0xff52, 0xff);\n eightBitStoreIntoGBMemory(0xff53, 0xff);\n eightBitStoreIntoGBMemory(0xff54, 0xff);\n eightBitStoreIntoGBMemory(0xff55, 0xff);\n }\n}\n\nexport function startDmaTransfer(sourceAddressOffset: i32): void {\n let sourceAddress: i32 = sourceAddressOffset;\n sourceAddress = sourceAddress << 8;\n\n for (let i: i32 = 0; i <= 0x9f; i++) {\n let spriteInformationByte: i32 = eightBitLoadFromGBMemory(sourceAddress + i);\n let spriteInformationAddress: i32 = Memory.spriteInformationTableLocation + i;\n eightBitStoreIntoGBMemory(spriteInformationAddress, spriteInformationByte);\n }\n\n // TCAGBD: This copy (DMA) needs 160 × 4 + 4 clocks to complete in both double speed and single speeds modes\n // Increment all of our Cycle coiunters in ../cpu/opcodes\n Memory.DMACycles = 644;\n}\n\n// https://gist.github.com/drhelius/3394856\n// http://bgb.bircd.org/pandocs.htm\nexport function startHdmaTransfer(hdmaTriggerByteToBeWritten: i32): void {\n // Check if we are Gbc\n if (!Cpu.GBCEnabled) {\n return;\n }\n\n // Check if we are trying to terminate an already active HBLANK HDMA\n if (Memory.isHblankHdmaActive && !checkBitOnByte(7, hdmaTriggerByteToBeWritten)) {\n // Don't reset anything, just set bit 7 to 1 on the trigger byte\n Memory.isHblankHdmaActive = false;\n let hdmaTriggerByte = eightBitLoadFromGBMemory(Memory.memoryLocationHdmaTrigger);\n eightBitStoreIntoGBMemory(Memory.memoryLocationHdmaTrigger, setBitOnByte(7, hdmaTriggerByte));\n return;\n }\n\n // Get our source and destination for the HDMA\n let hdmaSource: i32 = getHdmaSourceFromMemory();\n let hdmaDestination: i32 = getHdmaDestinationFromMemory();\n\n // Get the length from the trigger\n // Lower 7 bits, Add 1, times 16\n // https://gist.github.com/drhelius/3394856\n let transferLength: i32 = resetBitOnByte(7, hdmaTriggerByteToBeWritten);\n transferLength = (transferLength + 1) * 16;\n\n // Get bit 7 of the trigger for the HDMA type\n if (checkBitOnByte(7, hdmaTriggerByteToBeWritten)) {\n // H-Blank DMA\n Memory.isHblankHdmaActive = true;\n Memory.hblankHdmaTransferLengthRemaining = transferLength;\n Memory.hblankHdmaSource = hdmaSource;\n Memory.hblankHdmaDestination = hdmaDestination;\n\n // This will be handled in updateHblankHdma()\n\n // Since we return false in write traps, we need to now write the byte\n // Be sure to reset bit 7, to show that the hdma is active\n eightBitStoreIntoGBMemory(Memory.memoryLocationHdmaTrigger, resetBitOnByte(7, hdmaTriggerByteToBeWritten));\n } else {\n // General DMA\n hdmaTransfer(hdmaSource, hdmaDestination, transferLength);\n\n // Stop the DMA\n eightBitStoreIntoGBMemory(Memory.memoryLocationHdmaTrigger, 0xff);\n }\n}\n\nexport function updateHblankHdma(): void {\n if (!Memory.isHblankHdmaActive) {\n return;\n }\n\n // Get our amount of bytes to transfer (Only 0x10 bytes at a time)\n let bytesToTransfer: i32 = 0x10;\n if (Memory.hblankHdmaTransferLengthRemaining < bytesToTransfer) {\n // Set to the difference\n bytesToTransfer = Memory.hblankHdmaTransferLengthRemaining;\n }\n\n // Do the transfer (Only 0x10 bytes at a time)\n hdmaTransfer(Memory.hblankHdmaSource, Memory.hblankHdmaDestination, bytesToTransfer);\n\n // Update our source and destination\n Memory.hblankHdmaSource += bytesToTransfer;\n Memory.hblankHdmaDestination += bytesToTransfer;\n Memory.hblankHdmaTransferLengthRemaining -= bytesToTransfer;\n\n if (Memory.hblankHdmaTransferLengthRemaining <= 0) {\n // End the transfer\n Memory.isHblankHdmaActive = false;\n\n // Need to clear the HDMA with 0xFF, which sets bit 7 to 1 to show the HDMA has ended\n eightBitStoreIntoGBMemory(Memory.memoryLocationHdmaTrigger, 0xff);\n } else {\n // Set our new transfer length, make sure it is in the weird format,\n // and make sure bit 7 is 0, to show that the HDMA is Active\n let remainingTransferLength: i32 = Memory.hblankHdmaTransferLengthRemaining;\n let transferLengthAsByte: i32 = remainingTransferLength / 16 - 1;\n eightBitStoreIntoGBMemory(Memory.memoryLocationHdmaTrigger, resetBitOnByte(7, transferLengthAsByte));\n }\n}\n\n// Simple Function to transfer the bytes from a destination to a source for a general pourpose or Hblank HDMA\nfunction hdmaTransfer(hdmaSource: i32, hdmaDestination: i32, transferLength: i32): void {\n for (let i: i32 = 0; i < transferLength; i++) {\n let sourceByte: i32 = eightBitLoadFromGBMemoryWithTraps(hdmaSource + i);\n // get the hdmaDestination with wrapping\n // See issue #61: https://github.com/torch2424/wasmBoy/issues/61\n let hdmaDestinationWithWrapping = hdmaDestination + i;\n while (hdmaDestinationWithWrapping > 0x9fff) {\n // Simply clear the top 3 bits\n hdmaDestinationWithWrapping = hdmaDestinationWithWrapping - 0x2000;\n }\n eightBitStoreIntoGBMemoryWithTraps(hdmaDestinationWithWrapping, sourceByte);\n }\n\n // Set our Cycles used for the HDMA\n // Since DMA in GBC Double Speed Mode takes 80 micro seconds,\n // And HDMA takes 8 micro seconds per 0x10 bytes in GBC Double Speed mode (and GBC Normal Mode)\n // Will assume (644 / 10) cycles for GBC Double Speed Mode,\n // and (644 / 10 / 2) for GBC Normal Mode\n let hdmaCycles: i32 = 32;\n if (Cpu.GBCDoubleSpeed) {\n hdmaCycles = 64;\n }\n hdmaCycles = hdmaCycles * (transferLength / 0x10);\n Memory.DMACycles += hdmaCycles;\n}\n\n// Function to get our HDMA Source\n// Follows the poan docs\nfunction getHdmaSourceFromMemory(): i32 {\n // Get our source for the HDMA\n let hdmaSourceHigh: i32 = eightBitLoadFromGBMemory(Memory.memoryLocationHdmaSourceHigh);\n let hdmaSourceLow: i32 = eightBitLoadFromGBMemory(Memory.memoryLocationHdmaSourceLow);\n\n let hdmaSource: i32 = concatenateBytes(hdmaSourceHigh, hdmaSourceLow);\n\n // And off the appopriate bits for the source and destination\n // And off the bottom 4 bits\n hdmaSource = hdmaSource & 0xfff0;\n\n return hdmaSource;\n}\n\n// Function to get our HDMA Destination\n// Follows the poan docs\nfunction getHdmaDestinationFromMemory(): i32 {\n let hdmaDestinationHigh: i32 = eightBitLoadFromGBMemory(Memory.memoryLocationHdmaDestinationHigh);\n let hdmaDestinationLow: i32 = eightBitLoadFromGBMemory(Memory.memoryLocationHdmaDestinationLow);\n\n let hdmaDestination: i32 = concatenateBytes(hdmaDestinationHigh, hdmaDestinationLow);\n\n // Can only be in VRAM, 0x8000 -> 0x9FF0\n // Pan docs says to knock off upper 3 bits, and lower 4 bits\n // Which gives us: 0001111111110000 or 0x1FF0\n // Meaning we must add 0x8000\n hdmaDestination = hdmaDestination & 0x1ff0;\n hdmaDestination += Memory.videoRamLocation;\n\n return hdmaDestination;\n}\n","// Store / Write memory access\nimport { checkWriteTraps } from './writeTraps';\nimport { getWasmBoyOffsetFromGameBoyOffset } from './memoryMap';\nimport { splitHighByte, splitLowByte, hexLog } from '../helpers/index';\n\nexport function eightBitStoreIntoGBMemory(gameboyOffset: i32, value: i32): void {\n store(getWasmBoyOffsetFromGameBoyOffset(gameboyOffset), value);\n}\n\nexport function eightBitStoreIntoGBMemoryWithTraps(offset: i32, value: i32): void {\n if (checkWriteTraps(offset, value)) {\n eightBitStoreIntoGBMemory(offset, value);\n }\n}\n\nexport function sixteenBitStoreIntoGBMemoryWithTraps(offset: i32, value: i32): void {\n // Dividing into two seperate eight bit calls to help with debugging tilemap overwrites\n // Split the value into two seperate bytes\n let highByte: i32 = splitHighByte(value);\n let lowByte: i32 = splitLowByte(value);\n let nextOffset: i32 = offset + 1;\n\n if (checkWriteTraps(offset, lowByte)) {\n eightBitStoreIntoGBMemory(offset, lowByte);\n }\n\n if (checkWriteTraps(nextOffset, highByte)) {\n eightBitStoreIntoGBMemory(nextOffset, highByte);\n }\n}\n\nexport function sixteenBitStoreIntoGBMemory(offset: i32, value: i32): void {\n // Dividing into two seperate eight bit calls to help with debugging tilemap overwrites\n // Split the value into two seperate bytes\n let highByte: i32 = splitHighByte(value);\n let lowByte: i32 = splitLowByte(value);\n let nextOffset: i32 = offset + 1;\n\n eightBitStoreIntoGBMemory(offset, lowByte);\n eightBitStoreIntoGBMemory(nextOffset, highByte);\n}\n\nexport function storeBooleanDirectlyToWasmMemory(offset: i32, value: boolean): void {\n if (value) {\n store(offset, 0x01);\n } else {\n store(offset, 0x00);\n }\n}\n","// Main Class and funcitons for rendering the gameboy display\nimport { FRAME_LOCATION, GAMEBOY_INTERNAL_MEMORY_LOCATION } from '../constants';\nimport { getSaveStateMemoryOffset } from '../core';\nimport { Lcd, setLcdStatus } from './lcd';\nimport { renderBackground, renderWindow } from './backgroundWindow';\nimport { renderSprites } from './sprites';\nimport { clearPriorityMap } from './priority';\nimport { resetTileCache } from './tiles';\nimport { Cpu } from '../cpu/index';\nimport { Config } from '../config';\nimport {\n Memory,\n eightBitLoadFromGBMemory,\n eightBitStoreIntoGBMemory,\n loadBooleanDirectlyFromWasmMemory,\n storeBooleanDirectlyToWasmMemory\n} from '../memory/index';\nimport { checkBitOnByte, setBitOnByte, resetBitOnByte, performanceTimestamp, hexLog } from '../helpers/index';\n\nexport class Graphics {\n // Current cycles\n // This will be used for batch processing\n static currentCycles: i32 = 0;\n\n // Number of cycles to run in each batch process\n // This number should be in sync so that graphics doesn't run too many cyles at once\n // and does not exceed the minimum number of cyles for either scanlines, or\n // How often we change the frame, or a channel's update process\n static batchProcessCycles(): i32 {\n return Graphics.MAX_CYCLES_PER_SCANLINE();\n }\n\n // Count the number of cycles to keep synced with cpu cycles\n // Found GBC cycles by finding clock speed from Gb Cycles\n // See TCAGBD For cycles\n static scanlineCycleCounter: i32 = 0x00;\n\n static MAX_CYCLES_PER_SCANLINE(): i32 {\n if (Cpu.GBCDoubleSpeed) {\n return 912;\n }\n\n return 456;\n }\n\n static MIN_CYCLES_SPRITES_LCD_MODE(): i32 {\n if (Cpu.GBCDoubleSpeed) {\n // TODO: Confirm these clock cyles, double similar to scanline, which TCAGBD did\n return 752;\n }\n\n return 376;\n }\n static MIN_CYCLES_TRANSFER_DATA_LCD_MODE(): i32 {\n if (Cpu.GBCDoubleSpeed) {\n // TODO: Confirm these clock cyles, double similar to scanline, which TCAGBD did\n return 498;\n }\n\n return 249;\n }\n\n // LCD\n // scanlineRegister also known as LY\n // See: http://bgb.bircd.org/pandocs.txt , and search \" LY \"\n static readonly memoryLocationScanlineRegister: i32 = 0xff44;\n static scanlineRegister: i32 = 0;\n static readonly memoryLocationDmaTransfer: i32 = 0xff46;\n\n // Scroll and Window\n static readonly memoryLocationScrollX: i32 = 0xff43;\n static scrollX: i32 = 0;\n static readonly memoryLocationScrollY: i32 = 0xff42;\n static scrollY: i32 = 0;\n static readonly memoryLocationWindowX: i32 = 0xff4b;\n static windowX: i32 = 0;\n static readonly memoryLocationWindowY: i32 = 0xff4a;\n static windowY: i32 = 0;\n\n // Tile Maps And Data\n static readonly memoryLocationTileMapSelectZeroStart: i32 = 0x9800;\n static readonly memoryLocationTileMapSelectOneStart: i32 = 0x9c00;\n static readonly memoryLocationTileDataSelectZeroStart: i32 = 0x8800;\n static readonly memoryLocationTileDataSelectOneStart: i32 = 0x8000;\n\n // Sprites\n static readonly memoryLocationSpriteAttributesTable: i32 = 0xfe00;\n\n // Palettes\n static readonly memoryLocationBackgroundPalette: i32 = 0xff47;\n static readonly memoryLocationSpritePaletteOne: i32 = 0xff48;\n static readonly memoryLocationSpritePaletteTwo: i32 = 0xff49;\n\n // Screen data needs to be stored in wasm memory\n\n // Save States\n\n static readonly saveStateSlot: i32 = 1;\n\n // Function to save the state of the class\n static saveState(): void {\n store(getSaveStateMemoryOffset(0x00, Graphics.saveStateSlot), Graphics.scanlineCycleCounter);\n store(getSaveStateMemoryOffset(0x04, Graphics.saveStateSlot), Lcd.currentLcdMode);\n\n eightBitStoreIntoGBMemory(Graphics.memoryLocationScanlineRegister, Graphics.scanlineRegister);\n }\n\n // Function to load the save state from memory\n static loadState(): void {\n Graphics.scanlineCycleCounter = load(getSaveStateMemoryOffset(0x00, Graphics.saveStateSlot));\n Lcd.currentLcdMode = load(getSaveStateMemoryOffset(0x04, Graphics.saveStateSlot));\n\n Graphics.scanlineRegister = eightBitLoadFromGBMemory(Graphics.memoryLocationScanlineRegister);\n Lcd.updateLcdControl(eightBitLoadFromGBMemory(Lcd.memoryLocationLcdControl));\n }\n}\n\n// Batch Process Graphics\n// http://gameboy.mongenel.com/dmg/asmmemmap.html and http://gbdev.gg8.se/wiki/articles/Video_Display\n// Function to batch process our graphics after we skipped so many cycles\n// This is not currently checked in memory read/write\nexport function batchProcessGraphics(): void {\n if (Graphics.currentCycles < Graphics.batchProcessCycles()) {\n return;\n }\n\n while (Graphics.currentCycles >= Graphics.batchProcessCycles()) {\n updateGraphics(Graphics.batchProcessCycles());\n Graphics.currentCycles = Graphics.currentCycles - Graphics.batchProcessCycles();\n }\n}\n\nexport function initializeGraphics(): void {\n // Reset Stateful Variables\n Graphics.currentCycles = 0;\n Graphics.scanlineCycleCounter = 0x00;\n Graphics.scanlineRegister = 0;\n Graphics.scrollX = 0;\n Graphics.scrollY = 0;\n Graphics.windowX = 0;\n Graphics.windowY = 0;\n\n if (Cpu.GBCEnabled) {\n Graphics.scanlineRegister = 0x91;\n eightBitStoreIntoGBMemory(0xff40, 0x91);\n eightBitStoreIntoGBMemory(0xff41, 0x81);\n // 0xFF42 -> 0xFF43 = 0x00\n eightBitStoreIntoGBMemory(0xff44, 0x90);\n // 0xFF45 -> 0xFF46 = 0x00\n eightBitStoreIntoGBMemory(0xff47, 0xfc);\n // 0xFF48 -> 0xFF4B = 0x00\n\n // GBC VRAM Banks\n eightBitStoreIntoGBMemory(0xff4f, 0x00);\n eightBitStoreIntoGBMemory(0xff70, 0x01);\n } else {\n Graphics.scanlineRegister = 0x91;\n eightBitStoreIntoGBMemory(0xff40, 0x91);\n eightBitStoreIntoGBMemory(0xff41, 0x85);\n // 0xFF42 -> 0xFF45 = 0x00\n eightBitStoreIntoGBMemory(0xff46, 0xff);\n eightBitStoreIntoGBMemory(0xff47, 0xfc);\n eightBitStoreIntoGBMemory(0xff48, 0xff);\n eightBitStoreIntoGBMemory(0xff49, 0xff);\n // 0xFF4A -> 0xFF4B = 0x00\n\n // GBC VRAM Banks\n eightBitStoreIntoGBMemory(0xff4f, 0x00);\n eightBitStoreIntoGBMemory(0xff70, 0x01);\n }\n}\n\nexport function updateGraphics(numberOfCycles: i32): void {\n if (Lcd.enabled) {\n Graphics.scanlineCycleCounter += numberOfCycles;\n\n if (Graphics.scanlineCycleCounter >= Graphics.MAX_CYCLES_PER_SCANLINE()) {\n // Reset the scanlineCycleCounter\n // Don't set to zero to catch extra cycles\n Graphics.scanlineCycleCounter -= Graphics.MAX_CYCLES_PER_SCANLINE();\n\n // Move to next scanline\n // let scanlineRegister: i32 = eightBitLoadFromGBMemory(Graphics.memoryLocationScanlineRegister);\n let scanlineRegister: i32 = Graphics.scanlineRegister;\n\n // Check if we've reached the last scanline\n if (scanlineRegister === 144) {\n // Draw the scanline\n if (!Config.graphicsDisableScanlineRendering) {\n _drawScanline(scanlineRegister);\n } else {\n _renderEntireFrame();\n }\n\n // Clear the priority map\n clearPriorityMap();\n\n // Reset the tile cache\n resetTileCache();\n } else if (scanlineRegister < 144) {\n // Draw the scanline\n if (!Config.graphicsDisableScanlineRendering) {\n _drawScanline(scanlineRegister);\n }\n }\n\n // Post increment the scanline register after drawing\n if (scanlineRegister > 153) {\n // Check if we overflowed scanlines\n // if so, reset our scanline number\n scanlineRegister = 0;\n } else {\n scanlineRegister += 1;\n }\n\n // Store our new scanline value\n Graphics.scanlineRegister = scanlineRegister;\n // eightBitStoreIntoGBMemory(Graphics.memoryLocationScanlineRegister, scanlineRegister);\n }\n }\n\n // Games like Pokemon crystal want the vblank right as it turns to the value, and not have it increment after\n // It will break and lead to an infinite loop in crystal\n // Therefore, we want to be checking/Setting our LCD status after the scanline updates\n setLcdStatus();\n}\n\n// TODO: Make this a _drawPixelOnScanline, as values can be updated while drawing a scanline\nfunction _drawScanline(scanlineRegister: i32): void {\n // Get our seleted tile data memory location\n let tileDataMemoryLocation: i32 = Graphics.memoryLocationTileDataSelectZeroStart;\n if (Lcd.bgWindowTileDataSelect) {\n tileDataMemoryLocation = Graphics.memoryLocationTileDataSelectOneStart;\n }\n\n // Check if the background is enabled\n // NOTE: On Gameboy color, Pandocs says this does something completely different\n // LCDC.0 - 2) CGB in CGB Mode: BG and Window Master Priority\n // When Bit 0 is cleared, the background and window lose their priority -\n // the sprites will be always displayed on top of background and window,\n // independently of the priority flags in OAM and BG Map attributes.\n // TODO: Enable this different feature for GBC\n if (Cpu.GBCEnabled || Lcd.bgDisplayEnabled) {\n // Get our map memory location\n let tileMapMemoryLocation: i32 = Graphics.memoryLocationTileMapSelectZeroStart;\n if (Lcd.bgTileMapDisplaySelect) {\n tileMapMemoryLocation = Graphics.memoryLocationTileMapSelectOneStart;\n }\n\n // Finally, pass everything to draw the background\n renderBackground(scanlineRegister, tileDataMemoryLocation, tileMapMemoryLocation);\n }\n\n // Check if the window is enabled, and we are currently\n // Drawing lines on the window\n if (Lcd.windowDisplayEnabled) {\n // Get our map memory location\n let tileMapMemoryLocation: i32 = Graphics.memoryLocationTileMapSelectZeroStart;\n if (Lcd.windowTileMapDisplaySelect) {\n tileMapMemoryLocation = Graphics.memoryLocationTileMapSelectOneStart;\n }\n\n // Finally, pass everything to draw the background\n renderWindow(scanlineRegister, tileDataMemoryLocation, tileMapMemoryLocation);\n }\n\n if (Lcd.spriteDisplayEnable) {\n // Sprites are enabled, render them!\n renderSprites(scanlineRegister, Lcd.tallSpriteSize);\n }\n}\n\n// Function to render everything for a frame at once\n// This is to improve performance\n// See above for comments on how things are donw\nfunction _renderEntireFrame(): void {\n // Scanline needs to be in sync while we draw, thus, we can't shortcut anymore than here\n for (let i: u8 = 0; i <= 144; i++) {\n _drawScanline(i);\n }\n}\n\n// Function to get the start of a RGB pixel (R, G, B)\nexport function getRgbPixelStart(x: i32, y: i32): i32 {\n // Get the pixel number\n // let pixelNumber: i32 = (y * 160) + x;\n // Each pixel takes 3 slots, therefore, multiply by 3!\n return (y * 160 + x) * 3;\n}\n\n// Also need to store current frame in memory to be read by JS\nexport function setPixelOnFrame(x: i32, y: i32, colorId: i32, color: i32): void {\n // Currently only supports 160x144\n // Storing in X, then y\n // So need an offset\n store(FRAME_LOCATION + getRgbPixelStart(x, y) + colorId, color);\n}\n\n// Function to shortcut the memory map, and load directly from the VRAM Bank\nexport function loadFromVramBank(gameboyOffset: i32, vramBankId: i32): u8 {\n let wasmBoyAddress: i32 = gameboyOffset - Memory.videoRamLocation + GAMEBOY_INTERNAL_MEMORY_LOCATION + 0x2000 * (vramBankId & 0x01);\n return load(wasmBoyAddress);\n}\n","import { GBC_PALETTE_LOCATION } from '../constants';\nimport { Cpu } from '../cpu/index';\nimport { Memory, eightBitLoadFromGBMemory, eightBitStoreIntoGBMemory } from '../memory/index';\nimport { checkBitOnByte, resetBitOnByte, setBitOnByte, concatenateBytes, hexLog } from '../helpers/index';\n\n// Class for GBC Color palletes\n// http://gbdev.gg8.se/wiki/articles/Video_Display#FF68_-_BCPS.2FBGPI_-_CGB_Mode_Only_-_Background_Palette_Index\nexport class Palette {\n static memoryLocationBackgroundPaletteIndex: i32 = 0xff68;\n static memoryLocationBackgroundPaletteData: i32 = 0xff69;\n static memoryLocationSpritePaletteIndex: i32 = 0xff6a;\n static memoryLocationSpritePaletteData: i32 = 0xff6b;\n}\n\nexport function initializePalette(): void {\n if (Cpu.GBCEnabled) {\n // GBC Palettes\n eightBitStoreIntoGBMemory(0xff68, 0xc0);\n eightBitStoreIntoGBMemory(0xff69, 0xff);\n eightBitStoreIntoGBMemory(0xff6a, 0xc1);\n eightBitStoreIntoGBMemory(0xff6b, 0x0d);\n } else {\n // GBC Palettes\n eightBitStoreIntoGBMemory(0xff68, 0xff);\n eightBitStoreIntoGBMemory(0xff69, 0xff);\n eightBitStoreIntoGBMemory(0xff6a, 0xff);\n eightBitStoreIntoGBMemory(0xff6b, 0xff);\n }\n}\n\n// Simple get pallete color or monochroime GB\n// shouldRepresentColorByColorId is good for debugging tile data for GBC games that don't have\n// monochromePalettes\nexport function getMonochromeColorFromPalette(\n colorId: i32,\n paletteMemoryLocation: i32,\n shouldRepresentColorByColorId: boolean = false\n): i32 {\n // Shift our paletteByte, 2 times for each color ID\n // And off any extra bytes\n // Return our Color (00 - white, 01 - light grey, 10 Dark grey, or 11 - Black)\n let color: i32 = colorId;\n if (!shouldRepresentColorByColorId) {\n color = ((eightBitLoadFromGBMemory(paletteMemoryLocation)) >> (colorId * 2)) & 0x03;\n }\n\n // Since our max is 254, and max is 3.\n // monochrome color palette is modified from bgb\n // TODO: Make these colors into a constant\n let rgbColor: i32 = 242;\n\n switch (color) {\n case 0:\n break;\n case 1:\n rgbColor = 160;\n break;\n case 2:\n rgbColor = 88;\n break;\n case 3:\n rgbColor = 8;\n break;\n }\n\n return rgbColor;\n}\n\nexport function writeColorPaletteToMemory(offset: i32, value: i32): void {\n // FF68\n // Bit 0-5 Index (00-3F)\n if (offset === Palette.memoryLocationBackgroundPaletteData || offset === Palette.memoryLocationSpritePaletteData) {\n // Get the palette index\n let paletteIndex: i32 = eightBitLoadFromGBMemory(offset - 1);\n\n // Clear the 6th bit, as it does nothing\n paletteIndex = resetBitOnByte(6, paletteIndex);\n\n // Check if we are changing the sprite pallete data\n let isSprite: boolean = false;\n if (offset === Palette.memoryLocationSpritePaletteData) {\n isSprite = true;\n }\n\n storePaletteByteInWasmMemory(paletteIndex, value, isSprite);\n\n incrementPaletteIndexIfSet(paletteIndex, offset - 1);\n }\n}\n\n// Functions to Handle Write to pallete data registers\n// http://gbdev.gg8.se/wiki/articles/Video_Display#FF68_-_BCPS.2FBGPI_-_CGB_Mode_Only_-_Background_Palette_Index\n// Function to handle incrementing the pallete index if required\nfunction incrementPaletteIndexIfSet(paletteIndex: i32, offset: i32): void {\n // Check ther auto increment box\n if (checkBitOnByte(7, paletteIndex)) {\n // Increment the index, and return the value before the increment\n // Ensure we don't ouverflow our auto increment bit\n paletteIndex += 1;\n paletteIndex = setBitOnByte(7, paletteIndex);\n\n eightBitStoreIntoGBMemory(offset, paletteIndex);\n }\n}\n\n// FF68\n// Bit 0-5 Index (00-3F)\n// Bit 7 Auto Increment (0=Disabled, 1=Increment after Writing)\n// Index is 00-0x3F because the means 0 - 63 (64),\n// and apparently there are 8 bytes per pallete to describe Color 0-3 (4 colors),\n// and 0-7 (8 palltetes). Therefore, 64!\nexport function getRgbColorFromPalette(paletteId: i32, colorId: i32, isSprite: boolean): i32 {\n // Each Pallete takes 8 bytes, so multiply by 8 to get the pallete\n // And Each color takes 2 bytes, therefore, multiple by 2 for the correct color bytes in the palette\n let paletteIndex: i32 = paletteId * 8 + colorId * 2;\n\n // Load the Color that is seperated into two bytes\n let paletteHighByte: i32 = loadPaletteByteFromWasmMemory(paletteIndex + 1, isSprite);\n let paletteLowByte: i32 = loadPaletteByteFromWasmMemory(paletteIndex, isSprite);\n\n // Return the concatenated color byte\n return concatenateBytes(paletteHighByte, paletteLowByte);\n}\n\n// Function to return the color from a passed 16 bit color pallette\nexport function getColorComponentFromRgb(colorId: i32, colorRgb: i32): i32 {\n // Get our bitmask for the color ID\n // bit mask tested good :)\n let bitMask: i32 = 0x1f << (colorId * 5);\n let colorValue: i32 = (colorRgb & bitMask) >> (colorId * 5);\n\n // Goal is to reach 254 for each color, so 255 / 31 (0x1F) ~8 TODO: Make exact\n // Want 5 bits for each\n return colorValue * 8;\n}\n\n// Function to load a byte from our Gbc Palette memory\nexport function loadPaletteByteFromWasmMemory(paletteIndexByte: i32, isSprite: boolean): u8 {\n // Clear the top two bits to just get the bottom palette Index\n let paletteIndex: i32 = paletteIndexByte & 0x3f;\n\n // Move over the palette index to not overlap the background has 0x3F, so Zero for Sprites is 0x40)\n if (isSprite) {\n paletteIndex += 0x40;\n }\n\n return load(GBC_PALETTE_LOCATION + paletteIndex);\n}\n\n// Function to store a byte to our Gbc Palette memory\nexport function storePaletteByteInWasmMemory(paletteIndexByte: i32, value: i32, isSprite: boolean): void {\n // Clear the top two bits to just get the bottom palette Index\n let paletteIndex: i32 = paletteIndexByte & 0x3f;\n\n // Move over the palette index to not overlap the background (has 0x3F, so Zero for Sprites is 0x40)\n if (isSprite) {\n paletteIndex += 0x40;\n }\n\n store(GBC_PALETTE_LOCATION + paletteIndex, value);\n}\n","// https://emu-docs.org/Game%20Boy/gb_sound.txt\n// https://www.youtube.com/watch?v=HyzD8pNlpwI\n// https://gist.github.com/drhelius/3652407\n\n// For our wasm -> JS, we will be passing in our -1.0 to 1.0 volume\n// As an unsigned byte. Each channel will give 0 (representing -1.0), to\n// 30 (representing 1.0), and will be added together. in the fucntion\n// getSampleAsUnsignedByte() will do the conversion of getting the total\n// of all the channels, times the (mixer volume + 1), to give us an unsigned\n// byte from 0 (-1.0) to 254 (1.0)\nimport { AUDIO_BUFFER_LOCATION } from '../constants';\nimport { getSaveStateMemoryOffset } from '../core';\nimport { SoundAccumulator, initializeSoundAccumulator, accumulateSound } from './accumulator';\nimport { Channel1 } from './channel1';\nimport { Channel2 } from './channel2';\nimport { Channel3 } from './channel3';\nimport { Channel4 } from './channel4';\nimport { Cpu } from '../cpu/index';\nimport { Config } from '../config';\nimport {\n eightBitLoadFromGBMemory,\n eightBitStoreIntoGBMemory,\n loadBooleanDirectlyFromWasmMemory,\n storeBooleanDirectlyToWasmMemory\n} from '../memory/index';\nimport { checkBitOnByte, concatenateBytes, splitLowByte, splitHighByte, hexLog, performanceTimestamp } from '../helpers/index';\nimport { i32Portable } from '../portable/portable';\n\nexport class Sound {\n // Current cycles\n // This will be used for batch processing\n // https://github.com/binji/binjgb/commit/e028f45e805bc0b0aa4697224a209f9ae514c954\n // TODO: May Also need to do this for Reads\n static currentCycles: i32 = 0;\n\n // Number of cycles to run in each batch process\n // This number should be in sync so that sound doesn't run too many cyles at once\n // and does not exceed the minimum number of cyles for either down sampling, or\n // How often we change the frame, or a channel's update process\n static batchProcessCycles(): i32 {\n if (Cpu.GBCDoubleSpeed) {\n return 174;\n }\n\n return 87;\n }\n\n // Channel control / On-OFF / Volume (RW)\n static readonly memoryLocationNR50: i32 = 0xff24;\n static NR50LeftMixerVolume: i32 = 0;\n static NR50RightMixerVolume: i32 = 0;\n static updateNR50(value: i32): void {\n Sound.NR50LeftMixerVolume = (value >> 4) & 0x07;\n Sound.NR50RightMixerVolume = value & 0x07;\n }\n\n // 0xFF25 selects which output each channel goes to, Referred to as NR51\n static readonly memoryLocationNR51: i32 = 0xff25;\n static NR51IsChannel1EnabledOnLeftOutput: boolean = true;\n static NR51IsChannel2EnabledOnLeftOutput: boolean = true;\n static NR51IsChannel3EnabledOnLeftOutput: boolean = true;\n static NR51IsChannel4EnabledOnLeftOutput: boolean = true;\n static NR51IsChannel1EnabledOnRightOutput: boolean = true;\n static NR51IsChannel2EnabledOnRightOutput: boolean = true;\n static NR51IsChannel3EnabledOnRightOutput: boolean = true;\n static NR51IsChannel4EnabledOnRightOutput: boolean = true;\n static updateNR51(value: i32): void {\n Sound.NR51IsChannel4EnabledOnLeftOutput = checkBitOnByte(7, value);\n Sound.NR51IsChannel3EnabledOnLeftOutput = checkBitOnByte(6, value);\n Sound.NR51IsChannel2EnabledOnLeftOutput = checkBitOnByte(5, value);\n Sound.NR51IsChannel1EnabledOnLeftOutput = checkBitOnByte(4, value);\n Sound.NR51IsChannel4EnabledOnRightOutput = checkBitOnByte(3, value);\n Sound.NR51IsChannel3EnabledOnRightOutput = checkBitOnByte(2, value);\n Sound.NR51IsChannel2EnabledOnRightOutput = checkBitOnByte(1, value);\n Sound.NR51IsChannel1EnabledOnRightOutput = checkBitOnByte(0, value);\n }\n\n // Sound on/off\n static readonly memoryLocationNR52: i32 = 0xff26;\n static NR52IsSoundEnabled: boolean = true;\n static updateNR52(value: i32): void {\n Sound.NR52IsSoundEnabled = checkBitOnByte(7, value);\n }\n\n // $FF30 -- $FF3F is the load register space for the 4-bit samples for channel 3\n static readonly memoryLocationChannel3LoadRegisterStart: i32 = 0xff30;\n\n // Need to count how often we need to increment our frame sequencer\n // Which you can read about below\n static frameSequenceCycleCounter: i32 = 0x0000;\n static maxFrameSequenceCycles(): i32 {\n if (Cpu.GBCDoubleSpeed) {\n return 16384;\n }\n\n return 8192;\n }\n\n // Also need to downsample our audio to average audio qualty\n // https://www.reddit.com/r/EmuDev/comments/5gkwi5/gb_apu_sound_emulation/\n // Want to do 48000hz, so CpuRate / Sound Rate, 4194304 / 48000 ~ 87 cycles\n static downSampleCycleCounter: i32 = 0x00;\n static downSampleCycleMultiplier: i32 = 48000;\n static maxDownSampleCycles(): i32 {\n return Cpu.CLOCK_SPEED();\n }\n\n // Frame sequencer controls what should be updated and and ticked\n // Everyt time the sound is updated :) It is updated everytime the\n // Cycle counter reaches the max cycle\n static frameSequencer: i32 = 0x00;\n\n // Our current sample number we are passing back to the wasmboy memory map\n // Found that a static number of samples doesn't work well on mobile\n // Will just update the queue index, grab as much as we can whenever we need more audio, then reset\n // NOTE: Giving a really large sample rate gives more latency, but less pops!\n //static readonly MAX_NUMBER_OF_SAMPLES: i32 = 4096;\n static audioQueueIndex: i32 = 0x0000;\n static wasmBoyMemoryMaxBufferSize: i32 = 0x20000;\n\n // Save States\n static readonly saveStateSlot: i32 = 6;\n\n // Function to save the state of the class\n static saveState(): void {\n store(getSaveStateMemoryOffset(0x00, Sound.saveStateSlot), Sound.frameSequenceCycleCounter);\n store(getSaveStateMemoryOffset(0x04, Sound.saveStateSlot), Sound.downSampleCycleCounter);\n store(getSaveStateMemoryOffset(0x05, Sound.saveStateSlot), Sound.frameSequencer);\n }\n\n // Function to load the save state from memory\n static loadState(): void {\n Sound.frameSequenceCycleCounter = load(getSaveStateMemoryOffset(0x00, Sound.saveStateSlot));\n Sound.downSampleCycleCounter = load(getSaveStateMemoryOffset(0x04, Sound.saveStateSlot));\n Sound.frameSequencer = load(getSaveStateMemoryOffset(0x05, Sound.saveStateSlot));\n\n clearAudioBuffer();\n }\n}\n\n// Initialize sound registers\n// From: https://emu-docs.org/Game%20Boy/gb_sound.txt\nexport function initializeSound(): void {\n // Reset Stateful variables\n Sound.currentCycles = 0;\n Sound.NR50LeftMixerVolume = 0;\n Sound.NR50RightMixerVolume = 0;\n Sound.NR51IsChannel1EnabledOnLeftOutput = true;\n Sound.NR51IsChannel2EnabledOnLeftOutput = true;\n Sound.NR51IsChannel3EnabledOnLeftOutput = true;\n Sound.NR51IsChannel4EnabledOnLeftOutput = true;\n Sound.NR51IsChannel1EnabledOnRightOutput = true;\n Sound.NR51IsChannel2EnabledOnRightOutput = true;\n Sound.NR51IsChannel3EnabledOnRightOutput = true;\n Sound.NR51IsChannel4EnabledOnRightOutput = true;\n Sound.NR52IsSoundEnabled = true;\n Sound.frameSequenceCycleCounter = 0x0000;\n Sound.downSampleCycleCounter = 0x00;\n Sound.frameSequencer = 0x00;\n Sound.audioQueueIndex = 0x0000;\n\n // intiialize our channels\n Channel1.initialize();\n Channel2.initialize();\n Channel3.initialize();\n Channel4.initialize();\n\n // Other Sound Registers\n eightBitStoreIntoGBMemory(Sound.memoryLocationNR50, 0x77);\n eightBitStoreIntoGBMemory(Sound.memoryLocationNR51, 0xf3);\n eightBitStoreIntoGBMemory(Sound.memoryLocationNR52, 0xf1);\n\n initializeSoundAccumulator();\n}\n\n// Function to batch process our audio after we skipped so many cycles\nexport function batchProcessAudio(): void {\n if (Sound.currentCycles < Sound.batchProcessCycles()) {\n return;\n }\n\n while (Sound.currentCycles >= Sound.batchProcessCycles()) {\n updateSound(Sound.batchProcessCycles());\n Sound.currentCycles = Sound.currentCycles - Sound.batchProcessCycles();\n }\n}\n\n// Function for updating sound\nexport function updateSound(numberOfCycles: i32): void {\n // Check if our frameSequencer updated\n let frameSequencerUpdated: boolean = updateFrameSequencer(numberOfCycles);\n\n if (Config.audioAccumulateSamples && !frameSequencerUpdated) {\n accumulateSound(numberOfCycles);\n } else {\n calculateSound(numberOfCycles);\n }\n}\n\n// Funciton to get the current Audio Queue index\nexport function getNumberOfSamplesInAudioBuffer(): i32 {\n return Sound.audioQueueIndex;\n}\n\n// Function to reset the audio queue\nexport function clearAudioBuffer(): void {\n Sound.audioQueueIndex = 0;\n}\n\nfunction calculateSound(numberOfCycles: i32): void {\n // Update all of our channels\n // All samples will be returned as 0 to 30\n // 0 being -1.0, and 30 being 1.0\n // (see blurb at top)\n let channel1Sample: i32 = Channel1.getSample(numberOfCycles);\n let channel2Sample: i32 = Channel2.getSample(numberOfCycles);\n let channel3Sample: i32 = Channel3.getSample(numberOfCycles);\n let channel4Sample: i32 = Channel4.getSample(numberOfCycles);\n // TODO: Allow individual channels to be muted\n // let channel1Sample: i32 = 15;\n // let channel2Sample: i32 = 15;\n // let channel3Sample: i32 = 15;\n // let channel4Sample: i32 = 15;\n\n // Save the samples in the accumulator\n SoundAccumulator.channel1Sample = channel1Sample;\n SoundAccumulator.channel2Sample = channel2Sample;\n SoundAccumulator.channel3Sample = channel3Sample;\n SoundAccumulator.channel4Sample = channel4Sample;\n\n // Do Some downsampling magic\n Sound.downSampleCycleCounter += numberOfCycles * Sound.downSampleCycleMultiplier;\n if (Sound.downSampleCycleCounter >= Sound.maxDownSampleCycles()) {\n // Reset the downsample counter\n // Don't set to zero to catch overflowed cycles\n Sound.downSampleCycleCounter -= Sound.maxDownSampleCycles();\n\n // Mixe our samples\n let mixedSample: i32 = mixChannelSamples(channel1Sample, channel2Sample, channel3Sample, channel4Sample);\n let leftChannelSampleUnsignedByte: i32 = splitHighByte(mixedSample);\n let rightChannelSampleUnsignedByte: i32 = splitLowByte(mixedSample);\n\n // Set our volumes in memory\n // +1 so it can not be zero\n setLeftAndRightOutputForAudioQueue(leftChannelSampleUnsignedByte + 1, rightChannelSampleUnsignedByte + 1, Sound.audioQueueIndex);\n Sound.audioQueueIndex += 1;\n\n // Don't allow our audioQueueIndex to overflow into other parts of the wasmBoy memory map\n // https://docs.google.com/spreadsheets/d/17xrEzJk5-sCB9J2mMJcVnzhbE-XH_NvczVSQH9OHvRk/edit#gid=0\n // Not 0xFFFF because we need half of 64kb since we store left and right channel\n if (Sound.audioQueueIndex >= Sound.wasmBoyMemoryMaxBufferSize / 2 - 1) {\n Sound.audioQueueIndex -= 1;\n }\n }\n}\n\nfunction updateFrameSequencer(numberOfCycles: i32): boolean {\n // APU runs at 4194304 / 512\n // Or Cpu.clockSpeed / 512\n // Which means, we need to update once every 8192 cycles :)\n Sound.frameSequenceCycleCounter += numberOfCycles;\n if (Sound.frameSequenceCycleCounter >= Sound.maxFrameSequenceCycles()) {\n // Reset the frameSequenceCycleCounter\n // Not setting to zero as we do not want to drop cycles\n Sound.frameSequenceCycleCounter -= Sound.maxFrameSequenceCycles();\n\n // Check our frame sequencer\n // https://gist.github.com/drhelius/3652407\n switch (Sound.frameSequencer) {\n case 0:\n // Update Length on Channels\n Channel1.updateLength();\n Channel2.updateLength();\n Channel3.updateLength();\n Channel4.updateLength();\n break;\n /* Do Nothing on one */\n case 2:\n // Update Sweep and Length on Channels\n Channel1.updateLength();\n Channel2.updateLength();\n Channel3.updateLength();\n Channel4.updateLength();\n\n Channel1.updateSweep();\n break;\n /* Do Nothing on three */\n case 4:\n // Update Length on Channels\n Channel1.updateLength();\n Channel2.updateLength();\n Channel3.updateLength();\n Channel4.updateLength();\n break;\n /* Do Nothing on five */\n case 6:\n // Update Sweep and Length on Channels\n Channel1.updateLength();\n Channel2.updateLength();\n Channel3.updateLength();\n Channel4.updateLength();\n\n Channel1.updateSweep();\n break;\n case 7:\n // Update Envelope on channels\n Channel1.updateEnvelope();\n Channel2.updateEnvelope();\n Channel4.updateEnvelope();\n break;\n }\n\n // Update our frame sequencer\n Sound.frameSequencer += 1;\n if (Sound.frameSequencer >= 8) {\n Sound.frameSequencer = 0;\n }\n\n return true;\n }\n\n return false;\n}\n\nexport function mixChannelSamples(\n channel1Sample: i32 = 15,\n channel2Sample: i32 = 15,\n channel3Sample: i32 = 15,\n channel4Sample: i32 = 15\n): i32 {\n // Do Some Cool mixing\n // NR50 FF24 ALLL BRRR Vin L enable, Left vol, Vin R enable, Right vol\n // NR51 FF25 NW21 NW21 Left enables, Right enables\n // NR52 FF26 P--- NW21 Power control/status, Channel length statuses\n // NW21 = 4 bits on byte\n // 3 -> Channel 4, 2 -> Channel 3, 1 -> Channel 2, 0 -> Channel 1\n\n // Matt's Proccess\n // I push out 1024 samples at a time and use 96000 hz sampling rate, so I guess i'm a bit less than one frame,\n // but I let the queue fill up with 4 x 1024 samples before I start waiting for the audio\n\n // TODO: Vin Mixing\n\n SoundAccumulator.mixerVolumeChanged = false;\n\n // Get our channel volume for left/right\n let leftChannelSample: i32 = 0;\n let rightChannelSample: i32 = 0;\n\n // Find the sample for the left if enabled\n // other wise add silence (15) for the channel\n if (Sound.NR51IsChannel1EnabledOnLeftOutput) {\n leftChannelSample += channel1Sample;\n } else {\n leftChannelSample += 15;\n }\n if (Sound.NR51IsChannel2EnabledOnLeftOutput) {\n leftChannelSample += channel2Sample;\n } else {\n leftChannelSample += 15;\n }\n if (Sound.NR51IsChannel3EnabledOnLeftOutput) {\n leftChannelSample += channel3Sample;\n } else {\n leftChannelSample += 15;\n }\n if (Sound.NR51IsChannel4EnabledOnLeftOutput) {\n leftChannelSample += channel4Sample;\n } else {\n leftChannelSample += 15;\n }\n\n // Find the sample for the right if enabled\n // other wise add silence (15) for the channel\n if (Sound.NR51IsChannel1EnabledOnRightOutput) {\n rightChannelSample += channel1Sample;\n } else {\n rightChannelSample += 15;\n }\n if (Sound.NR51IsChannel2EnabledOnRightOutput) {\n rightChannelSample += channel2Sample;\n } else {\n rightChannelSample += 15;\n }\n if (Sound.NR51IsChannel3EnabledOnRightOutput) {\n rightChannelSample += channel3Sample;\n } else {\n rightChannelSample += 15;\n }\n if (Sound.NR51IsChannel4EnabledOnRightOutput) {\n rightChannelSample += channel4Sample;\n } else {\n rightChannelSample += 15;\n }\n\n // Update our accumulator\n SoundAccumulator.mixerEnabledChanged = false;\n SoundAccumulator.needToRemixSamples = false;\n\n // Finally multiply our volumes by the mixer volume\n // Mixer volume can be at most 7 + 1\n // Can be at most 7, because we only have 3 bits, 111 = 7\n // http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Mixer\n // Done in the getSampleAsUnsignedByte(), since we are doing some weirdness there :)\n\n // Convert our samples from unsigned 32 to unsigned byte\n // Reason being, We want to be able to pass in wasm memory as usigned byte. Javascript will handle the conversion back\n let leftChannelSampleUnsignedByte: i32 = getSampleAsUnsignedByte(leftChannelSample, Sound.NR50LeftMixerVolume + 1);\n let rightChannelSampleUnsignedByte: i32 = getSampleAsUnsignedByte(rightChannelSample, Sound.NR50RightMixerVolume + 1);\n\n // Save these samples in the accumulator\n SoundAccumulator.leftChannelSampleUnsignedByte = leftChannelSampleUnsignedByte;\n SoundAccumulator.rightChannelSampleUnsignedByte = rightChannelSampleUnsignedByte;\n\n return concatenateBytes(leftChannelSampleUnsignedByte, rightChannelSampleUnsignedByte);\n}\n\nfunction getSampleAsUnsignedByte(sample: i32, mixerVolume: i32): i32 {\n // If the sample is silence, return silence as unsigned byte\n // Silence is common, and should be checked for performance\n if (sample === 60) {\n return 127;\n }\n\n // convert to a signed, precise scale of -6000 to 6000 (cheap way of -1.0 to 1.0)\n // Multiply by the mixer volume fraction (to find the actual volume)\n let precision: i32 = 100000;\n let convertedSample: i32 = sample - 60;\n convertedSample = convertedSample * precision;\n\n // Multiply by the mixer volume fraction (to find the actual volume)\n convertedSample = (convertedSample * mixerVolume) / 8;\n\n // Convert back to scale of 0 to 120\n convertedSample = convertedSample / precision;\n convertedSample = convertedSample + 60;\n\n // Finally, convert to an unsigned byte scale\n // With Four Channels (0 to 30) and no global volume. Max is 120\n // max unsigned byte goal is 254 (see blurb at top).\n // 120 / 254 should give the correct conversion\n // For example, 120 / 254 = 0.47244094488188976\n // Multiply by 1000 to increase the float into an int\n // so, 120 * 1000 / (0.47244094488188976 * 1000) should give approximate answer for max mixer volume\n let maxDivider: i32 = (120 * precision) / 254;\n convertedSample = (convertedSample * precision) / maxDivider;\n\n // Ensure we have an i32 and not a float for JS builds\n convertedSample = i32Portable(convertedSample);\n\n return convertedSample;\n}\n\n// Function to set our left and right channels at the correct queue index\nexport function setLeftAndRightOutputForAudioQueue(leftVolume: i32, rightVolume: i32, audioQueueIndex: i32): void {\n // Get our stereo index\n let audioQueueOffset = AUDIO_BUFFER_LOCATION + audioQueueIndex * 2;\n\n // Store our volumes\n // +1 that way we don't have empty data to ensure that the value is set\n store(audioQueueOffset, (leftVolume + 1));\n store(audioQueueOffset + 1, (rightVolume + 1));\n}\n","// NOTE: Tons of Copy-pasta btween channels, because Classes cannot be instantiated yet in assemblyscript\n\n// Square Channel with Frequency Sweep\n// http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Square_Wave\n// http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Frequency_Sweep\nimport { getSaveStateMemoryOffset } from '../core';\nimport { isDutyCycleClockPositiveOrNegativeForWaveform } from './duty';\nimport { Cpu } from '../cpu/index';\nimport {\n eightBitLoadFromGBMemory,\n eightBitStoreIntoGBMemory,\n loadBooleanDirectlyFromWasmMemory,\n storeBooleanDirectlyToWasmMemory\n} from '../memory/index';\nimport { checkBitOnByte, hexLog } from '../helpers/index';\n\nexport class Channel1 {\n // Cycle Counter for our sound accumulator\n static cycleCounter: i32 = 0;\n\n // Squarewave channel with volume envelope and frequency sweep functions.\n // NR10 -> Sweep Register R/W\n static readonly memoryLocationNRx0: i32 = 0xff10;\n // -PPP NSSS Sweep period, negate, shift\n static NRx0SweepPeriod: i32 = 0;\n static NRx0Negate: boolean = false;\n static NRx0SweepShift: i32 = 0;\n static updateNRx0(value: i32): void {\n Channel1.NRx0SweepPeriod = (value & 0x70) >> 4;\n Channel1.NRx0Negate = checkBitOnByte(3, value);\n Channel1.NRx0SweepShift = value & 0x07;\n }\n\n // NR11 -> Sound length/Wave pattern duty (R/W)\n static readonly memoryLocationNRx1: i32 = 0xff11;\n // DDLL LLLL Duty, Length load (64-L)\n static NRx1Duty: i32 = 0;\n static NRx1LengthLoad: i32 = 0;\n static updateNRx1(value: i32): void {\n Channel1.NRx1Duty = (value >> 6) & 0x03;\n Channel1.NRx1LengthLoad = value & 0x3f;\n\n // Also need to set our length counter. Taken from the old, setChannelLengthCounter\n // Channel length is determined by 64 (or 256 if channel 3), - the length load\n // http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Registers\n // Note, this will be different for channel 3\n Channel1.lengthCounter = 64 - Channel1.NRx1LengthLoad;\n }\n\n // NR12 -> Volume Envelope (R/W)\n static readonly memoryLocationNRx2: i32 = 0xff12;\n // VVVV APPP Starting volume, Envelope add mode, period\n static NRx2StartingVolume: i32 = 0;\n static NRx2EnvelopeAddMode: boolean = false;\n static NRx2EnvelopePeriod: i32 = 0;\n static updateNRx2(value: i32): void {\n Channel1.NRx2StartingVolume = (value >> 4) & 0x0f;\n Channel1.NRx2EnvelopeAddMode = checkBitOnByte(3, value);\n Channel1.NRx2EnvelopePeriod = value & 0x07;\n\n // Also, get our channel is dac enabled\n Channel1.isDacEnabled = (value & 0xf8) > 0;\n }\n\n // NR13 -> Frequency lo (W)\n static readonly memoryLocationNRx3: i32 = 0xff13;\n // FFFF FFFF Frequency LSB\n static NRx3FrequencyLSB: i32 = 0;\n static updateNRx3(value: i32): void {\n Channel1.NRx3FrequencyLSB = value;\n\n // Update Channel Frequency\n let frequency: i32 = (Channel1.NRx4FrequencyMSB << 8) | Channel1.NRx3FrequencyLSB;\n Channel1.frequency = frequency;\n }\n\n // NR14 -> Frequency hi (R/W)\n static readonly memoryLocationNRx4: i32 = 0xff14;\n // TL-- -FFF Trigger, Length enable, Frequency MSB\n static NRx4LengthEnabled: boolean = false;\n static NRx4FrequencyMSB: i32 = 0;\n static updateNRx4(value: i32): void {\n Channel1.NRx4LengthEnabled = checkBitOnByte(6, value);\n Channel1.NRx4FrequencyMSB = value & 0x07;\n\n // Update Channel Frequency\n let frequency: i32 = (Channel1.NRx4FrequencyMSB << 8) | Channel1.NRx3FrequencyLSB;\n Channel1.frequency = frequency;\n }\n\n // Channel Properties\n static readonly channelNumber: i32 = 1;\n static isEnabled: boolean = false;\n static isDacEnabled: boolean = false;\n static frequency: i32 = 0;\n static frequencyTimer: i32 = 0x00;\n static envelopeCounter: i32 = 0x00;\n static lengthCounter: i32 = 0x00;\n static volume: i32 = 0x00;\n\n // Square Wave properties\n static dutyCycle: i32 = 0x00;\n static waveFormPositionOnDuty: i32 = 0x00;\n\n // Channel 1 Sweep\n static isSweepEnabled: boolean = false;\n static sweepCounter: i32 = 0x00;\n static sweepShadowFrequency: i32 = 0x00;\n\n // Save States\n static readonly saveStateSlot: i32 = 7;\n\n // Function to save the state of the class\n static saveState(): void {\n storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x00, Channel1.saveStateSlot), Channel1.isEnabled);\n store(getSaveStateMemoryOffset(0x01, Channel1.saveStateSlot), Channel1.frequencyTimer);\n store(getSaveStateMemoryOffset(0x05, Channel1.saveStateSlot), Channel1.envelopeCounter);\n store(getSaveStateMemoryOffset(0x09, Channel1.saveStateSlot), Channel1.lengthCounter);\n store(getSaveStateMemoryOffset(0x0e, Channel1.saveStateSlot), Channel1.volume);\n\n store(getSaveStateMemoryOffset(0x13, Channel1.saveStateSlot), Channel1.dutyCycle);\n store(getSaveStateMemoryOffset(0x14, Channel1.saveStateSlot), Channel1.waveFormPositionOnDuty);\n\n storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x19, Channel1.saveStateSlot), Channel1.isSweepEnabled);\n store(getSaveStateMemoryOffset(0x1a, Channel1.saveStateSlot), Channel1.sweepCounter);\n store(getSaveStateMemoryOffset(0x1f, Channel1.saveStateSlot), Channel1.sweepShadowFrequency);\n }\n\n // Function to load the save state from memory\n static loadState(): void {\n Channel1.isEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x00, Channel1.saveStateSlot));\n Channel1.frequencyTimer = load(getSaveStateMemoryOffset(0x01, Channel1.saveStateSlot));\n Channel1.envelopeCounter = load(getSaveStateMemoryOffset(0x05, Channel1.saveStateSlot));\n Channel1.lengthCounter = load(getSaveStateMemoryOffset(0x09, Channel1.saveStateSlot));\n Channel1.volume = load(getSaveStateMemoryOffset(0x0e, Channel1.saveStateSlot));\n\n Channel1.dutyCycle = load(getSaveStateMemoryOffset(0x13, Channel1.saveStateSlot));\n Channel1.waveFormPositionOnDuty = load(getSaveStateMemoryOffset(0x14, Channel1.saveStateSlot));\n\n Channel1.isSweepEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x19, Channel1.saveStateSlot));\n Channel1.sweepCounter = load(getSaveStateMemoryOffset(0x1a, Channel1.saveStateSlot));\n Channel1.sweepShadowFrequency = load(getSaveStateMemoryOffset(0x1f, Channel1.saveStateSlot));\n }\n\n static initialize(): void {\n eightBitStoreIntoGBMemory(Channel1.memoryLocationNRx0, 0x80);\n eightBitStoreIntoGBMemory(Channel1.memoryLocationNRx1, 0xbf);\n eightBitStoreIntoGBMemory(Channel1.memoryLocationNRx2, 0xf3);\n eightBitStoreIntoGBMemory(Channel1.memoryLocationNRx3, 0xc1);\n eightBitStoreIntoGBMemory(Channel1.memoryLocationNRx4, 0xbf);\n }\n\n // Function to get a sample using the cycle counter on the channel\n static getSampleFromCycleCounter(): i32 {\n let accumulatedCycles: i32 = Channel1.cycleCounter;\n Channel1.cycleCounter = 0;\n return Channel1.getSample(accumulatedCycles);\n }\n\n // Function to reset our timer, useful for GBC double speed mode\n static resetTimer(): void {\n Channel1.frequencyTimer = (2048 - Channel1.frequency) * 4;\n\n // TODO: Ensure this is correct for GBC Double Speed Mode\n if (Cpu.GBCDoubleSpeed) {\n Channel1.frequencyTimer = Channel1.frequencyTimer * 2;\n }\n }\n\n static getSample(numberOfCycles: i32): i32 {\n // Decrement our channel timer\n Channel1.frequencyTimer -= numberOfCycles;\n if (Channel1.frequencyTimer <= 0) {\n // Get the amount that overflowed so we don't drop cycles\n let overflowAmount: i32 = abs(Channel1.frequencyTimer);\n\n // Reset our timer\n // A square channel's frequency timer period is set to (2048-frequency)*4.\n // Four duty cycles are available, each waveform taking 8 frequency timer clocks to cycle through:\n Channel1.resetTimer();\n Channel1.frequencyTimer -= overflowAmount;\n\n // Also increment our duty cycle\n // What is duty? https://en.wikipedia.org/wiki/Duty_cycle\n // Duty cycle for square wave: http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Square_Wave\n Channel1.waveFormPositionOnDuty += 1;\n if (Channel1.waveFormPositionOnDuty >= 8) {\n Channel1.waveFormPositionOnDuty = 0;\n }\n }\n\n // Get our ourput volume\n let outputVolume: i32 = 0;\n\n // Finally to set our output volume, the channel must be enabled,\n // Our channel DAC must be enabled, and we must be in an active state\n // Of our duty cycle\n if (Channel1.isEnabled && Channel1.isDacEnabled) {\n outputVolume = Channel1.volume;\n } else {\n // Return silence\n // Since range from -15 - 15, or 0 to 30 for our unsigned\n return 15;\n }\n\n // Get the current sampleValue\n let sample: i32 = 1;\n if (!isDutyCycleClockPositiveOrNegativeForWaveform(Channel1.NRx1Duty, Channel1.waveFormPositionOnDuty)) {\n sample = sample * -1;\n }\n\n sample = sample * outputVolume;\n\n // Square Waves Can range from -15 - 15. Therefore simply add 15\n sample = sample + 15;\n return sample;\n }\n\n //http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Trigger_Event\n static trigger(): void {\n Channel1.isEnabled = true;\n if (Channel1.lengthCounter === 0) {\n Channel1.lengthCounter = 64;\n }\n\n // Reset our timer\n // A square channel's frequency timer period is set to (2048-frequency)*4.\n // Four duty cycles are available, each waveform taking 8 frequency timer clocks to cycle through:\n Channel1.resetTimer();\n\n Channel1.envelopeCounter = Channel1.NRx2EnvelopePeriod;\n\n Channel1.volume = Channel1.NRx2StartingVolume;\n\n // Handle Channel Sweep\n // http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware\n Channel1.sweepShadowFrequency = Channel1.frequency;\n\n // Reset back to the sweep period\n Channel1.sweepCounter = Channel1.NRx0SweepPeriod;\n\n // The internal enabled flag is set if either the sweep period or shift are non-zero, cleared otherwise.\n if (Channel1.NRx0SweepPeriod > 0 && Channel1.NRx0SweepShift > 0) {\n Channel1.isSweepEnabled = true;\n } else {\n Channel1.isSweepEnabled = false;\n }\n\n // If the sweep shift is non-zero, frequency calculation and the overflow check are performed immediately.\n if (Channel1.NRx0SweepShift > 0) {\n calculateSweepAndCheckOverflow();\n }\n\n // Finally if DAC is off, channel is still disabled\n if (!Channel1.isDacEnabled) {\n Channel1.isEnabled = false;\n }\n }\n\n // Function to determine if the current channel would update when getting the sample\n // This is used to accumulate samples\n static willChannelUpdate(numberOfCycles: i32): boolean {\n //Increment our cycle counter\n Channel1.cycleCounter += numberOfCycles;\n\n // Dac enabled status cached by accumulator\n if (Channel1.frequencyTimer - Channel1.cycleCounter > 0) {\n return false;\n }\n\n return true;\n }\n\n static updateSweep(): void {\n // Obscure behavior\n // TODO: The volume envelope and sweep timers treat a period of 0 as 8.\n // Decrement the sweep counter\n Channel1.sweepCounter -= 1;\n\n if (Channel1.sweepCounter <= 0) {\n // Reset back to the sweep period\n Channel1.sweepCounter = Channel1.NRx0SweepPeriod;\n\n // Calculate our sweep\n // When it generates a clock and the sweep's internal enabled flag is set and the sweep period is not zero,\n // a new frequency is calculated and the overflow check is performed.\n if (Channel1.isSweepEnabled && Channel1.NRx0SweepPeriod > 0) {\n calculateSweepAndCheckOverflow();\n }\n }\n }\n\n static updateLength(): void {\n if (Channel1.lengthCounter > 0 && Channel1.NRx4LengthEnabled) {\n Channel1.lengthCounter -= 1;\n }\n\n if (Channel1.lengthCounter === 0) {\n Channel1.isEnabled = false;\n }\n }\n\n static updateEnvelope(): void {\n // Obscure behavior\n // TODO: The volume envelope and sweep timers treat a period of 0 as 8.\n\n Channel1.envelopeCounter -= 1;\n if (Channel1.envelopeCounter <= 0) {\n Channel1.envelopeCounter = Channel1.NRx2EnvelopePeriod;\n\n // When the timer generates a clock and the envelope period is NOT zero, a new volume is calculated\n // NOTE: There is some weiirrdd obscure behavior where zero can equal 8, so watch out for that\n // If notes are sustained for too long, this is probably why\n if (Channel1.envelopeCounter !== 0) {\n if (Channel1.NRx2EnvelopeAddMode && Channel1.volume < 15) {\n Channel1.volume += 1;\n } else if (!Channel1.NRx2EnvelopeAddMode && Channel1.volume > 0) {\n Channel1.volume -= 1;\n }\n }\n }\n }\n\n static setFrequency(frequency: i32): void {\n // Get the high and low bits\n let passedFrequencyHighBits: i32 = frequency >> 8;\n let passedFrequencyLowBits: i32 = frequency & 0xff;\n\n // Get the new register 4\n let register4: i32 = eightBitLoadFromGBMemory(Channel1.memoryLocationNRx4);\n // Knock off lower 3 bits, and Or on our high bits\n let newRegister4: i32 = register4 & 0xf8;\n newRegister4 = newRegister4 | passedFrequencyHighBits;\n\n // Set the registers\n eightBitStoreIntoGBMemory(Channel1.memoryLocationNRx3, passedFrequencyLowBits);\n eightBitStoreIntoGBMemory(Channel1.memoryLocationNRx4, newRegister4);\n\n // Save the frequency for ourselves without triggering memory traps\n Channel1.NRx3FrequencyLSB = passedFrequencyLowBits;\n Channel1.NRx4FrequencyMSB = passedFrequencyHighBits;\n Channel1.frequency = (Channel1.NRx4FrequencyMSB << 8) | Channel1.NRx3FrequencyLSB;\n }\n // Done!\n}\n\n// Sweep Specific functions\nfunction calculateSweepAndCheckOverflow(): void {\n let newFrequency: i32 = getNewFrequencyFromSweep();\n // 7FF is the highest value of the frequency: 111 1111 1111\n if (newFrequency <= 0x7ff && Channel1.NRx0SweepShift > 0) {\n // http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware\n // If the new frequency is 2047 or less and the sweep shift is not zero,\n // this new frequency is written back to the shadow frequency and square 1's frequency in NR13 and NR14,\n // then frequency calculation and overflow check are run AGAIN immediately using this new value,\n // but this second new frequency is not written back.\n Channel1.sweepShadowFrequency = newFrequency;\n Channel1.setFrequency(newFrequency);\n\n // Re calculate the new frequency\n newFrequency = getNewFrequencyFromSweep();\n }\n\n // Next check if the new Frequency is above 0x7FF\n // if So, disable our sweep\n if (newFrequency > 0x7ff) {\n Channel1.isEnabled = false;\n }\n}\n\n// Function to determing a new sweep in the current context\nfunction getNewFrequencyFromSweep(): i32 {\n // Start our new frequency, by making it equal to the \"shadow frequency\"\n let newFrequency: i32 = Channel1.sweepShadowFrequency;\n newFrequency = newFrequency >> Channel1.NRx0SweepShift;\n\n // Check for sweep negation\n if (Channel1.NRx0Negate) {\n newFrequency = Channel1.sweepShadowFrequency - newFrequency;\n } else {\n newFrequency = Channel1.sweepShadowFrequency + newFrequency;\n }\n\n return newFrequency;\n}\n","// NOTE: Tons of Copy-pasta btween channels, because Classes cannot be instantiated yet in assemblyscript\n\n// Simple Square Channel\n// http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Square_Wave\nimport { getSaveStateMemoryOffset } from '../core';\nimport { isDutyCycleClockPositiveOrNegativeForWaveform } from './duty';\nimport { Cpu } from '../cpu/index';\nimport {\n eightBitLoadFromGBMemory,\n eightBitStoreIntoGBMemory,\n loadBooleanDirectlyFromWasmMemory,\n storeBooleanDirectlyToWasmMemory\n} from '../memory/index';\nimport { checkBitOnByte, hexLog } from '../helpers/index';\n\nexport class Channel2 {\n // Cycle Counter for our sound accumulator\n static cycleCounter: i32 = 0;\n\n // Squarewave channel with volume envelope functions only.\n\n // NR21 -> Sound length/Wave pattern duty (R/W)\n static readonly memoryLocationNRx1: i32 = 0xff16;\n // DDLL LLLL Duty, Length load (64-L)\n static NRx1Duty: i32 = 0;\n static NRx1LengthLoad: i32 = 0;\n static updateNRx1(value: i32): void {\n Channel2.NRx1Duty = (value >> 6) & 0x03;\n Channel2.NRx1LengthLoad = value & 0x3f;\n\n // Also need to set our length counter. Taken from the old, setChannelLengthCounter\n // Channel length is determined by 64 (or 256 if channel 3), - the length load\n // http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Registers\n // Note, this will be different for channel 3\n Channel2.lengthCounter = 64 - Channel2.NRx1LengthLoad;\n }\n\n // NR22 -> Volume Envelope (R/W)\n static readonly memoryLocationNRx2: i32 = 0xff17;\n // VVVV APPP Starting volume, Envelope add mode, period\n static NRx2StartingVolume: i32 = 0;\n static NRx2EnvelopeAddMode: boolean = false;\n static NRx2EnvelopePeriod: i32 = 0;\n static updateNRx2(value: i32): void {\n Channel2.NRx2StartingVolume = (value >> 4) & 0x0f;\n Channel2.NRx2EnvelopeAddMode = checkBitOnByte(3, value);\n Channel2.NRx2EnvelopePeriod = value & 0x07;\n\n // Also, get our channel is dac enabled\n Channel2.isDacEnabled = (value & 0xf8) > 0;\n }\n\n // NR23 -> Frequency lo (W)\n static readonly memoryLocationNRx3: i32 = 0xff18;\n // FFFF FFFF Frequency LSB\n static NRx3FrequencyLSB: i32 = 0;\n static updateNRx3(value: i32): void {\n Channel2.NRx3FrequencyLSB = value;\n\n // Update Channel Frequency\n let frequency: i32 = (Channel2.NRx4FrequencyMSB << 8) | Channel2.NRx3FrequencyLSB;\n Channel2.frequency = frequency;\n }\n\n // NR24 -> Frequency hi (R/W)\n static readonly memoryLocationNRx4: i32 = 0xff19;\n // TL-- -FFF Trigger, Length enable, Frequency MSB\n static NRx4LengthEnabled: boolean = false;\n static NRx4FrequencyMSB: i32 = 0;\n static updateNRx4(value: i32): void {\n Channel2.NRx4LengthEnabled = checkBitOnByte(6, value);\n Channel2.NRx4FrequencyMSB = value & 0x07;\n\n // Update Channel Frequency\n let frequency: i32 = (Channel2.NRx4FrequencyMSB << 8) | Channel2.NRx3FrequencyLSB;\n Channel2.frequency = frequency;\n }\n\n // Channel Properties\n static readonly channelNumber: i32 = 2;\n static isEnabled: boolean = false;\n static isDacEnabled: boolean = false;\n static frequency: i32 = 0;\n static frequencyTimer: i32 = 0x00;\n static envelopeCounter: i32 = 0x00;\n static lengthCounter: i32 = 0x00;\n static volume: i32 = 0x00;\n\n // Square Wave properties\n static dutyCycle: i32 = 0x00;\n static waveFormPositionOnDuty: i32 = 0x00;\n\n // Save States\n\n static readonly saveStateSlot: i32 = 8;\n\n // Function to save the state of the class\n static saveState(): void {\n storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x00, Channel2.saveStateSlot), Channel2.isEnabled);\n store(getSaveStateMemoryOffset(0x01, Channel2.saveStateSlot), Channel2.frequencyTimer);\n store(getSaveStateMemoryOffset(0x05, Channel2.saveStateSlot), Channel2.envelopeCounter);\n store(getSaveStateMemoryOffset(0x09, Channel2.saveStateSlot), Channel2.lengthCounter);\n store(getSaveStateMemoryOffset(0x0e, Channel2.saveStateSlot), Channel2.volume);\n\n store(getSaveStateMemoryOffset(0x13, Channel2.saveStateSlot), Channel2.dutyCycle);\n store(getSaveStateMemoryOffset(0x14, Channel2.saveStateSlot), Channel2.waveFormPositionOnDuty);\n }\n\n // Function to load the save state from memory\n static loadState(): void {\n Channel2.isEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x00, Channel2.saveStateSlot));\n Channel2.frequencyTimer = load(getSaveStateMemoryOffset(0x01, Channel2.saveStateSlot));\n Channel2.envelopeCounter = load(getSaveStateMemoryOffset(0x05, Channel2.saveStateSlot));\n Channel2.lengthCounter = load(getSaveStateMemoryOffset(0x09, Channel2.saveStateSlot));\n Channel2.volume = load(getSaveStateMemoryOffset(0x0e, Channel2.saveStateSlot));\n\n Channel2.dutyCycle = load(getSaveStateMemoryOffset(0x13, Channel2.saveStateSlot));\n Channel2.waveFormPositionOnDuty = load(getSaveStateMemoryOffset(0x14, Channel2.saveStateSlot));\n }\n\n static initialize(): void {\n eightBitStoreIntoGBMemory(Channel2.memoryLocationNRx1 - 1, 0xff);\n eightBitStoreIntoGBMemory(Channel2.memoryLocationNRx1, 0x3f);\n eightBitStoreIntoGBMemory(Channel2.memoryLocationNRx2, 0x00);\n eightBitStoreIntoGBMemory(Channel2.memoryLocationNRx3, 0x00);\n eightBitStoreIntoGBMemory(Channel2.memoryLocationNRx4, 0xb8);\n }\n\n // Function to get a sample using the cycle counter on the channel\n static getSampleFromCycleCounter(): i32 {\n let accumulatedCycles: i32 = Channel2.cycleCounter;\n Channel2.cycleCounter = 0;\n return Channel2.getSample(accumulatedCycles);\n }\n\n // Function to reset our timer, useful for GBC double speed mode\n static resetTimer(): void {\n Channel2.frequencyTimer = (2048 - Channel2.frequency) * 4;\n\n // TODO: Ensure this is correct for GBC Double Speed Mode\n if (Cpu.GBCDoubleSpeed) {\n Channel2.frequencyTimer = Channel2.frequencyTimer * 2;\n }\n }\n\n static getSample(numberOfCycles: i32): i32 {\n // Decrement our channel timer\n Channel2.frequencyTimer -= numberOfCycles;\n if (Channel2.frequencyTimer <= 0) {\n // Get the amount that overflowed so we don't drop cycles\n let overflowAmount: i32 = abs(Channel2.frequencyTimer);\n\n // Reset our timer\n // A square channel's frequency timer period is set to (2048-frequency)*4.\n // Four duty cycles are available, each waveform taking 8 frequency timer clocks to cycle through:\n Channel2.resetTimer();\n Channel2.frequencyTimer -= overflowAmount;\n\n // Also increment our duty cycle\n // What is duty? https://en.wikipedia.org/wiki/Duty_cycle\n // Duty cycle for square wave: http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Square_Wave\n Channel2.waveFormPositionOnDuty += 1;\n if (Channel2.waveFormPositionOnDuty >= 8) {\n Channel2.waveFormPositionOnDuty = 0;\n }\n }\n\n // Get our ourput volume\n let outputVolume: i32 = 0;\n\n // Finally to set our output volume, the channel must be enabled,\n // Our channel DAC must be enabled, and we must be in an active state\n // Of our duty cycle\n if (Channel2.isEnabled && Channel2.isDacEnabled) {\n outputVolume = Channel2.volume;\n } else {\n // Return silence\n // Since range from -15 - 15, or 0 to 30 for our unsigned\n return 15;\n }\n\n // Get the current sampleValue\n let sample: i32 = 1;\n if (!isDutyCycleClockPositiveOrNegativeForWaveform(Channel2.NRx1Duty, Channel2.waveFormPositionOnDuty)) {\n sample = sample * -1;\n }\n\n sample = sample * outputVolume;\n\n // Square Waves Can range from -15 - 15. Therefore simply add 15\n sample = sample + 15;\n return sample;\n }\n\n //http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Trigger_Event\n static trigger(): void {\n Channel2.isEnabled = true;\n if (Channel2.lengthCounter === 0) {\n Channel2.lengthCounter = 64;\n }\n\n // Reset our timer\n // A square channel's frequency timer period is set to (2048-frequency)*4.\n // Four duty cycles are available, each waveform taking 8 frequency timer clocks to cycle through:\n Channel2.resetTimer();\n\n Channel2.envelopeCounter = Channel2.NRx2EnvelopePeriod;\n\n Channel2.volume = Channel2.NRx2StartingVolume;\n\n // Finally if DAC is off, channel is still disabled\n if (!Channel2.isDacEnabled) {\n Channel2.isEnabled = false;\n }\n }\n\n // Function to determine if the current channel would update when getting the sample\n // This is used to accumulate samples\n static willChannelUpdate(numberOfCycles: i32): boolean {\n //Increment our cycle counter\n Channel2.cycleCounter += numberOfCycles;\n\n // Dac enabled status cached by accumulator\n if (Channel2.frequencyTimer - Channel2.cycleCounter > 0) {\n return false;\n }\n\n return true;\n }\n\n static updateLength(): void {\n if (Channel2.lengthCounter > 0 && Channel2.NRx4LengthEnabled) {\n Channel2.lengthCounter -= 1;\n }\n\n if (Channel2.lengthCounter === 0) {\n Channel2.isEnabled = false;\n }\n }\n\n static updateEnvelope(): void {\n // Obscure behavior\n // TODO: The volume envelope and sweep timers treat a period of 0 as 8.\n\n Channel2.envelopeCounter -= 1;\n if (Channel2.envelopeCounter <= 0) {\n Channel2.envelopeCounter = Channel2.NRx2EnvelopePeriod;\n\n // When the timer generates a clock and the envelope period is NOT zero, a new volume is calculated\n // NOTE: There is some weiirrdd obscure behavior where zero can equal 8, so watch out for that\n if (Channel2.envelopeCounter !== 0) {\n if (Channel2.NRx2EnvelopeAddMode && Channel2.volume < 15) {\n Channel2.volume += 1;\n } else if (!Channel2.NRx2EnvelopeAddMode && Channel2.volume > 0) {\n Channel2.volume -= 1;\n }\n }\n }\n }\n\n static setFrequency(frequency: i32): void {\n // Get the high and low bits\n let passedFrequencyHighBits: i32 = frequency >> 8;\n let passedFrequencyLowBits: i32 = frequency & 0xff;\n\n // Get the new register 4\n let register4: i32 = eightBitLoadFromGBMemory(Channel2.memoryLocationNRx4);\n // Knock off lower 3 bits, and Or on our high bits\n let newRegister4: i32 = register4 & 0xf8;\n newRegister4 = newRegister4 | passedFrequencyHighBits;\n\n // Set the registers\n eightBitStoreIntoGBMemory(Channel2.memoryLocationNRx3, passedFrequencyLowBits);\n eightBitStoreIntoGBMemory(Channel2.memoryLocationNRx4, newRegister4);\n\n // Save the frequency for ourselves without triggering memory traps\n Channel2.NRx3FrequencyLSB = passedFrequencyLowBits;\n Channel2.NRx4FrequencyMSB = passedFrequencyHighBits;\n Channel2.frequency = (Channel2.NRx4FrequencyMSB << 8) | Channel2.NRx3FrequencyLSB;\n }\n // Done!\n}\n","// NOTE: Tons of Copy-pasta btween channels, because Classes cannot be instantiated yet in assemblyscript\n\n// Wave Channel\n// http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Wave_Channel\nimport { getSaveStateMemoryOffset } from '../core';\nimport { isDutyCycleClockPositiveOrNegativeForWaveform } from './duty';\nimport { Cpu } from '../cpu/index';\nimport {\n eightBitLoadFromGBMemory,\n eightBitStoreIntoGBMemory,\n loadBooleanDirectlyFromWasmMemory,\n storeBooleanDirectlyToWasmMemory\n} from '../memory/index';\nimport { checkBitOnByte, hexLog } from '../helpers/index';\n\nexport class Channel3 {\n // Cycle Counter for our sound accumulator\n static cycleCounter: i32 = 0;\n\n // Voluntary Wave channel with 32 4-bit programmable samples, played in sequence.\n // NR30 -> Sound on/off (R/W)\n static readonly memoryLocationNRx0: i32 = 0xff1a;\n // E--- ---- DAC power\n static updateNRx0(value: i32): void {\n Channel3.isDacEnabled = checkBitOnByte(7, value);\n }\n\n // NR31 -> Sound length (R/W)\n static readonly memoryLocationNRx1: i32 = 0xff1b;\n // LLLL LLLL Length load (256-L)\n static NRx1LengthLoad: i32 = 0;\n static updateNRx1(value: i32): void {\n Channel3.NRx1LengthLoad = value;\n\n // Also need to set our length counter. Taken from the old, setChannelLengthCounter\n // Channel length is determined by 64 (or 256 if channel 3), - the length load\n // http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Registers\n // Note, this will be different for channel 3\n // Supposed to be 256, so subtracting 255 and then adding 1 if that makes sense\n Channel3.lengthCounter = 256 - Channel3.NRx1LengthLoad;\n }\n\n // NR32 -> Select ouput level (R/W)\n static readonly memoryLocationNRx2: i32 = 0xff1c;\n // -VV- ---- Volume code (00=0%, 01=100%, 10=50%, 11=25%)\n static NRx2VolumeCode: i32 = 0;\n static updateNRx2(value: i32): void {\n Channel3.NRx2VolumeCode = (value >> 5) & 0x0f;\n }\n\n // NR33 -> Frequency lower data (W)\n static readonly memoryLocationNRx3: i32 = 0xff1d;\n // FFFF FFFF Frequency LSB\n static NRx3FrequencyLSB: i32 = 0;\n static updateNRx3(value: i32): void {\n Channel3.NRx3FrequencyLSB = value;\n\n // Update Channel Frequency\n let frequency: i32 = (Channel3.NRx4FrequencyMSB << 8) | Channel3.NRx3FrequencyLSB;\n Channel3.frequency = frequency;\n }\n\n // NR34 -> Frequency higher data (R/W)\n static readonly memoryLocationNRx4: i32 = 0xff1e;\n // TL-- -FFF Trigger, Length enable, Frequency MSB\n static NRx4LengthEnabled: boolean = false;\n static NRx4FrequencyMSB: i32 = 0;\n static updateNRx4(value: i32): void {\n Channel3.NRx4LengthEnabled = checkBitOnByte(6, value);\n Channel3.NRx4FrequencyMSB = value & 0x07;\n\n // Update Channel Frequency\n let frequency: i32 = (Channel3.NRx4FrequencyMSB << 8) | Channel3.NRx3FrequencyLSB;\n Channel3.frequency = frequency;\n }\n\n // Our wave table location\n static readonly memoryLocationWaveTable: i32 = 0xff30;\n\n // Channel Properties\n static readonly channelNumber: i32 = 3;\n static isEnabled: boolean = false;\n static isDacEnabled: boolean = false;\n static frequency: i32 = 0;\n static frequencyTimer: i32 = 0x00;\n static lengthCounter: i32 = 0x00;\n static waveTablePosition: i32 = 0x00;\n static volumeCode: i32 = 0x00;\n static volumeCodeChanged: boolean = false;\n\n // Save States\n\n static readonly saveStateSlot: i32 = 9;\n\n // Function to save the state of the class\n static saveState(): void {\n storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x00, Channel3.saveStateSlot), Channel3.isEnabled);\n store(getSaveStateMemoryOffset(0x01, Channel3.saveStateSlot), Channel3.frequencyTimer);\n store(getSaveStateMemoryOffset(0x05, Channel3.saveStateSlot), Channel3.lengthCounter);\n store(getSaveStateMemoryOffset(0x09, Channel3.saveStateSlot), Channel3.waveTablePosition);\n }\n\n // Function to load the save state from memory\n static loadState(): void {\n Channel3.isEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x00, Channel3.saveStateSlot));\n Channel3.frequencyTimer = load(getSaveStateMemoryOffset(0x01, Channel3.saveStateSlot));\n Channel3.lengthCounter = load(getSaveStateMemoryOffset(0x05, Channel3.saveStateSlot));\n Channel3.waveTablePosition = load(getSaveStateMemoryOffset(0x09, Channel3.saveStateSlot));\n }\n\n static initialize(): void {\n eightBitStoreIntoGBMemory(Channel3.memoryLocationNRx0, 0x7f);\n eightBitStoreIntoGBMemory(Channel3.memoryLocationNRx1, 0xff);\n eightBitStoreIntoGBMemory(Channel3.memoryLocationNRx2, 0x9f);\n eightBitStoreIntoGBMemory(Channel3.memoryLocationNRx3, 0x00);\n eightBitStoreIntoGBMemory(Channel3.memoryLocationNRx4, 0xb8);\n\n // The volume code changed\n Channel3.volumeCodeChanged = true;\n }\n\n // Function to get a sample using the cycle counter on the channel\n static getSampleFromCycleCounter(): i32 {\n let accumulatedCycles: i32 = Channel3.cycleCounter;\n Channel3.cycleCounter = 0;\n return Channel3.getSample(accumulatedCycles);\n }\n\n // Function to reset our timer, useful for GBC double speed mode\n static resetTimer(): void {\n Channel3.frequencyTimer = (2048 - Channel3.frequency) * 2;\n\n // TODO: Ensure this is correct for GBC Double Speed Mode\n if (Cpu.GBCDoubleSpeed) {\n Channel3.frequencyTimer = Channel3.frequencyTimer * 2;\n }\n }\n\n static getSample(numberOfCycles: i32): i32 {\n // Decrement our channel timer\n Channel3.frequencyTimer -= numberOfCycles;\n if (Channel3.frequencyTimer <= 0) {\n // Get the amount that overflowed so we don't drop cycles\n let overflowAmount: i32 = abs(Channel3.frequencyTimer);\n\n // Reset our timer\n // A wave channel's frequency timer period is set to (2048-frequency) * 2.\n // http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Wave_Channel\n Channel3.resetTimer();\n Channel3.frequencyTimer -= overflowAmount;\n\n // Advance the wave table position, and loop back if needed\n Channel3.waveTablePosition += 1;\n if (Channel3.waveTablePosition >= 32) {\n Channel3.waveTablePosition = 0;\n }\n }\n\n // Get our ourput volume\n let outputVolume: i32 = 0;\n let volumeCode: i32 = Channel3.volumeCode;\n\n // Finally to set our output volume, the channel must be enabled,\n // Our channel DAC must be enabled, and we must be in an active state\n // Of our duty cycle\n if (Channel3.isEnabled && Channel3.isDacEnabled) {\n // Get our volume code\n if (Channel3.volumeCodeChanged) {\n volumeCode = eightBitLoadFromGBMemory(Channel3.memoryLocationNRx2);\n volumeCode = volumeCode >> 5;\n volumeCode = volumeCode & 0x0f;\n Channel3.volumeCode = volumeCode;\n Channel3.volumeCodeChanged = false;\n }\n } else {\n // Return silence\n // Since range from -15 - 15, or 0 to 30 for our unsigned\n return 15;\n }\n\n // Get the current sample\n let sample: i32 = 0;\n\n // Will Find the position, and knock off any remainder\n let positionIndexToAdd: i32 = Channel3.waveTablePosition / 2;\n let memoryLocationWaveSample: i32 = Channel3.memoryLocationWaveTable + positionIndexToAdd;\n\n sample = eightBitLoadFromGBMemory(memoryLocationWaveSample);\n\n // Need to grab the top or lower half for the correct sample\n if (Channel3.waveTablePosition % 2 === 0) {\n // First sample\n sample = sample >> 4;\n sample = sample & 0x0f;\n } else {\n // Second Samples\n sample = sample & 0x0f;\n }\n\n // Shift our sample and set our volume depending on the volume code\n // Since we can't multiply by float, simply divide by 4, 2, 1\n // http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Wave_Channel\n switch (volumeCode) {\n case 0:\n sample = sample >> 4;\n break;\n case 1:\n // Dont Shift sample\n outputVolume = 1;\n break;\n case 2:\n sample = sample >> 1;\n outputVolume = 2;\n break;\n default:\n sample = sample >> 2;\n outputVolume = 4;\n break;\n }\n\n // Spply out output volume\n if (outputVolume > 0) {\n sample = sample / outputVolume;\n } else {\n sample = 0;\n }\n\n // Square Waves Can range from -15 - 15. Therefore simply add 15\n sample = sample + 15;\n return sample;\n }\n\n //http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Trigger_Event\n static trigger(): void {\n Channel3.isEnabled = true;\n if (Channel3.lengthCounter === 0) {\n Channel3.lengthCounter = 256;\n }\n\n // Reset our timer\n // A wave channel's frequency timer period is set to (2048-frequency)*2.\n Channel3.resetTimer();\n\n // Reset our wave table position\n Channel3.waveTablePosition = 0;\n\n // Finally if DAC is off, channel is still disabled\n if (!Channel3.isDacEnabled) {\n Channel3.isEnabled = false;\n }\n }\n\n // Function to determine if the current channel would update when getting the sample\n // This is used to accumulate samples\n static willChannelUpdate(numberOfCycles: i32): boolean {\n //Increment our cycle counter\n Channel3.cycleCounter += numberOfCycles;\n\n // Dac enabled status cached by accumulator\n if (Channel3.frequencyTimer - Channel3.cycleCounter > 0 && !Channel3.volumeCodeChanged) {\n return false;\n }\n\n return true;\n }\n\n static updateLength(): void {\n if (Channel3.lengthCounter > 0 && Channel3.NRx4LengthEnabled) {\n Channel3.lengthCounter -= 1;\n }\n\n if (Channel3.lengthCounter === 0) {\n Channel3.isEnabled = false;\n }\n }\n}\n","// NOTE: Tons of Copy-pasta btween channels, because Classes cannot be instantiated yet in assemblyscript\n\n// Noise Channel\n// http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Noise_Channel\nimport { getSaveStateMemoryOffset } from '../core';\nimport { isDutyCycleClockPositiveOrNegativeForWaveform } from './duty';\nimport { Cpu } from '../cpu/index';\nimport {\n eightBitLoadFromGBMemory,\n eightBitStoreIntoGBMemoryWithTraps,\n eightBitStoreIntoGBMemory,\n loadBooleanDirectlyFromWasmMemory,\n storeBooleanDirectlyToWasmMemory\n} from '../memory/index';\nimport { checkBitOnByte, hexLog } from '../helpers/index';\n\nexport class Channel4 {\n // Cycle Counter for our sound accumulator\n static cycleCounter: i32 = 0;\n\n // Channel 4\n // 'white noise' channel with volume envelope functions.\n // NR41 -> Sound length (R/W)\n static readonly memoryLocationNRx1: i32 = 0xff20;\n // --LL LLLL Length load (64-L)\n static NRx1LengthLoad: i32 = 0;\n static updateNRx1(value: i32): void {\n Channel4.NRx1LengthLoad = value & 0x3f;\n\n // Also need to set our length counter. Taken from the old, setChannelLengthCounter\n // Channel length is determined by 64 (or 256 if channel 3), - the length load\n // http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Registers\n // Note, this will be different for channel 3\n Channel4.lengthCounter = 64 - Channel4.NRx1LengthLoad;\n }\n\n // NR42 -> Volume Envelope (R/W)\n static readonly memoryLocationNRx2: i32 = 0xff21;\n // VVVV APPP Starting volume, Envelope add mode, period\n static NRx2StartingVolume: i32 = 0;\n static NRx2EnvelopeAddMode: boolean = false;\n static NRx2EnvelopePeriod: i32 = 0;\n static updateNRx2(value: i32): void {\n Channel4.NRx2StartingVolume = (value >> 4) & 0x0f;\n Channel4.NRx2EnvelopeAddMode = checkBitOnByte(3, value);\n Channel4.NRx2EnvelopePeriod = value & 0x07;\n\n // Also, get our channel is dac enabled\n Channel4.isDacEnabled = (value & 0xf8) > 0;\n }\n\n // NR43 -> Polynomial Counter (R/W)\n static readonly memoryLocationNRx3: i32 = 0xff22;\n // SSSS WDDD Clock shift, Width mode of LFSR, Divisor code\n static NRx3ClockShift: i32 = 0;\n static NRx3WidthMode: boolean = false;\n static NRx3DivisorCode: i32 = 0;\n static updateNRx3(value: i32): void {\n Channel4.NRx3ClockShift = value >> 4;\n Channel4.NRx3WidthMode = checkBitOnByte(3, value);\n Channel4.NRx3DivisorCode = value & 0x07;\n\n // Also, get our divisor\n switch (Channel4.NRx3DivisorCode) {\n case 0:\n Channel4.divisor = 8;\n return;\n case 1:\n Channel4.divisor = 16;\n return;\n case 2:\n Channel4.divisor = 32;\n return;\n case 3:\n Channel4.divisor = 48;\n return;\n case 4:\n Channel4.divisor = 64;\n return;\n case 5:\n Channel4.divisor = 80;\n return;\n case 6:\n Channel4.divisor = 96;\n return;\n case 7:\n Channel4.divisor = 112;\n return;\n }\n }\n\n // NR44 -> Trigger, Length Enable\n static readonly memoryLocationNRx4: i32 = 0xff23;\n // TL-- ---- Trigger, Length enable\n static NRx4LengthEnabled: boolean = false;\n static updateNRx4(value: i32): void {\n Channel4.NRx4LengthEnabled = checkBitOnByte(6, value);\n }\n\n // Channel Properties\n static readonly channelNumber: i32 = 4;\n static isEnabled: boolean = false;\n static isDacEnabled: boolean = false;\n static frequencyTimer: i32 = 0x00;\n static envelopeCounter: i32 = 0x00;\n static lengthCounter: i32 = 0x00;\n static volume: i32 = 0x00;\n static divisor: i32 = 0;\n\n // Noise properties\n // NOTE: Is only 15 bits\n static linearFeedbackShiftRegister: i32 = 0x00;\n\n // Save States\n\n static readonly saveStateSlot: i32 = 10;\n\n // Function to save the state of the class\n static saveState(): void {\n storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x00, Channel4.saveStateSlot), Channel4.isEnabled);\n store(getSaveStateMemoryOffset(0x01, Channel4.saveStateSlot), Channel4.frequencyTimer);\n store(getSaveStateMemoryOffset(0x05, Channel4.saveStateSlot), Channel4.envelopeCounter);\n store(getSaveStateMemoryOffset(0x09, Channel4.saveStateSlot), Channel4.lengthCounter);\n store(getSaveStateMemoryOffset(0x0e, Channel4.saveStateSlot), Channel4.volume);\n store(getSaveStateMemoryOffset(0x13, Channel4.saveStateSlot), Channel4.linearFeedbackShiftRegister);\n }\n\n // Function to load the save state from memory\n static loadState(): void {\n Channel4.isEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x00, Channel4.saveStateSlot));\n Channel4.frequencyTimer = load(getSaveStateMemoryOffset(0x01, Channel4.saveStateSlot));\n Channel4.envelopeCounter = load(getSaveStateMemoryOffset(0x05, Channel4.saveStateSlot));\n Channel4.lengthCounter = load(getSaveStateMemoryOffset(0x09, Channel4.saveStateSlot));\n Channel4.volume = load(getSaveStateMemoryOffset(0x0e, Channel4.saveStateSlot));\n Channel4.linearFeedbackShiftRegister = load(getSaveStateMemoryOffset(0x13, Channel4.saveStateSlot));\n }\n\n static initialize(): void {\n eightBitStoreIntoGBMemory(Channel4.memoryLocationNRx1 - 1, 0xff);\n eightBitStoreIntoGBMemory(Channel4.memoryLocationNRx1, 0xff);\n eightBitStoreIntoGBMemory(Channel4.memoryLocationNRx2, 0x00);\n eightBitStoreIntoGBMemory(Channel4.memoryLocationNRx3, 0x00);\n eightBitStoreIntoGBMemory(Channel4.memoryLocationNRx4, 0xbf);\n }\n\n // Function to get a sample using the cycle counter on the channel\n static getSampleFromCycleCounter(): i32 {\n let accumulatedCycles: i32 = Channel4.cycleCounter;\n Channel4.cycleCounter = 0;\n return Channel4.getSample(accumulatedCycles);\n }\n\n static getSample(numberOfCycles: i32): i32 {\n // Decrement our channel timer\n Channel4.frequencyTimer -= numberOfCycles;\n\n if (Channel4.frequencyTimer <= 0) {\n // Get the amount that overflowed so we don't drop cycles\n let overflowAmount: i32 = abs(Channel4.frequencyTimer);\n\n // Reset our timer\n Channel4.frequencyTimer = Channel4.getNoiseChannelFrequencyPeriod();\n Channel4.frequencyTimer -= overflowAmount;\n\n // Do some cool stuff with lfsr\n // http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Noise_Channel\n\n // First XOR bit zero and one\n let lfsrBitZero: i32 = Channel4.linearFeedbackShiftRegister & 0x01;\n let lfsrBitOne: i32 = Channel4.linearFeedbackShiftRegister >> 1;\n lfsrBitOne = lfsrBitOne & 0x01;\n let xorLfsrBitZeroOne = lfsrBitZero ^ lfsrBitOne;\n\n // Shift all lsfr bits by one\n Channel4.linearFeedbackShiftRegister = Channel4.linearFeedbackShiftRegister >> 1;\n\n // Place the XOR result on bit 15\n Channel4.linearFeedbackShiftRegister = Channel4.linearFeedbackShiftRegister | (xorLfsrBitZeroOne << 14);\n\n // If the width mode is set, set xor on bit 6, and make lfsr 7 bit\n if (Channel4.NRx3WidthMode) {\n // Make 7 bit, by knocking off lower bits. Want to keeps bits 8 - 16, and then or on 7\n Channel4.linearFeedbackShiftRegister = Channel4.linearFeedbackShiftRegister & ~0x40;\n Channel4.linearFeedbackShiftRegister = Channel4.linearFeedbackShiftRegister | (xorLfsrBitZeroOne << 6);\n }\n }\n\n // Get our ourput volume, set to zero for silence\n let outputVolume: i32 = 0;\n\n // Finally to set our output volume, the channel must be enabled,\n // Our channel DAC must be enabled, and we must be in an active state\n // Of our duty cycle\n if (Channel4.isEnabled && Channel4.isDacEnabled) {\n outputVolume = Channel4.volume;\n } else {\n // Return silence\n // Since range from -15 - 15, or 0 to 30 for our unsigned\n return 15;\n }\n\n // Declare our sample\n let sample: i32 = 0;\n\n // Wave form output is bit zero of lfsr, INVERTED\n if (!checkBitOnByte(0, Channel4.linearFeedbackShiftRegister)) {\n sample = 1;\n } else {\n sample = -1;\n }\n\n sample = sample * outputVolume;\n\n // Noise Can range from -15 - 15. Therefore simply add 15\n sample = sample + 15;\n return sample;\n }\n\n //http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Trigger_Event\n static trigger(): void {\n Channel4.isEnabled = true;\n if (Channel4.lengthCounter === 0) {\n Channel4.lengthCounter = 64;\n }\n\n // Reset our timers\n Channel4.frequencyTimer = Channel4.getNoiseChannelFrequencyPeriod();\n\n Channel4.envelopeCounter = Channel4.NRx2EnvelopePeriod;\n\n Channel4.volume = Channel4.NRx2StartingVolume;\n\n // Noise channel's LFSR bits are all set to 1.\n Channel4.linearFeedbackShiftRegister = 0x7fff;\n\n // Finally if DAC is off, channel is still disabled\n if (!Channel4.isDacEnabled) {\n Channel4.isEnabled = false;\n }\n }\n\n // Function to determine if the current channel would update when getting the sample\n // This is used to accumulate samples\n static willChannelUpdate(numberOfCycles: i32): boolean {\n //Increment our cycle counter\n Channel4.cycleCounter += numberOfCycles;\n\n // Dac enabled status cached by accumulator\n if (Channel4.frequencyTimer - Channel4.cycleCounter > 0) {\n return false;\n }\n\n return true;\n }\n\n static getNoiseChannelFrequencyPeriod(): i32 {\n // Get our divisor from the divisor code, and shift by the clock shift\n let response: i32 = Channel4.divisor << Channel4.NRx3ClockShift;\n if (Cpu.GBCDoubleSpeed) {\n response = response * 2;\n }\n return response;\n }\n\n static updateLength(): void {\n if (Channel4.lengthCounter > 0 && Channel4.NRx4LengthEnabled) {\n Channel4.lengthCounter -= 1;\n }\n\n if (Channel4.lengthCounter === 0) {\n Channel4.isEnabled = false;\n }\n }\n\n static updateEnvelope(): void {\n // Obscure behavior\n // TODO: The volume envelope and sweep timers treat a period of 0 as 8.\n\n Channel4.envelopeCounter -= 1;\n if (Channel4.envelopeCounter <= 0) {\n Channel4.envelopeCounter = Channel4.NRx2EnvelopePeriod;\n\n // When the timer generates a clock and the envelope period is NOT zero, a new volume is calculated\n // NOTE: There is some weiirrdd obscure behavior where zero can equal 8, so watch out for that\n if (Channel4.envelopeCounter !== 0) {\n if (Channel4.NRx2EnvelopeAddMode && Channel4.volume < 15) {\n Channel4.volume += 1;\n } else if (!Channel4.NRx2EnvelopeAddMode && Channel4.volume > 0) {\n Channel4.volume -= 1;\n }\n }\n }\n }\n // Done!\n}\n","import { Sound, mixChannelSamples, setLeftAndRightOutputForAudioQueue } from './sound';\nimport { Channel1 } from './channel1';\nimport { Channel2 } from './channel2';\nimport { Channel3 } from './channel3';\nimport { Channel4 } from './channel4';\n\n// Another class simply for accumulating samples\n// Default everything to silence\nexport class SoundAccumulator {\n static channel1Sample: i32 = 15;\n static channel2Sample: i32 = 15;\n static channel3Sample: i32 = 15;\n static channel4Sample: i32 = 15;\n static channel1DacEnabled: boolean = false;\n static channel2DacEnabled: boolean = false;\n static channel3DacEnabled: boolean = false;\n static channel4DacEnabled: boolean = false;\n static leftChannelSampleUnsignedByte: i32 = 127;\n static rightChannelSampleUnsignedByte: i32 = 127;\n static mixerVolumeChanged: boolean = false;\n static mixerEnabledChanged: boolean = false;\n\n //If a channel was updated, need to also track if we need to need to mix them again\n static needToRemixSamples: boolean = false;\n}\n\nexport function initializeSoundAccumulator(): void {\n SoundAccumulator.channel1Sample = 15;\n SoundAccumulator.channel2Sample = 15;\n SoundAccumulator.channel3Sample = 15;\n SoundAccumulator.channel4Sample = 15;\n SoundAccumulator.channel1DacEnabled = false;\n SoundAccumulator.channel2DacEnabled = false;\n SoundAccumulator.channel3DacEnabled = false;\n SoundAccumulator.channel4DacEnabled = false;\n SoundAccumulator.leftChannelSampleUnsignedByte = 127;\n SoundAccumulator.rightChannelSampleUnsignedByte = 127;\n SoundAccumulator.mixerVolumeChanged = true;\n SoundAccumulator.mixerEnabledChanged = true;\n SoundAccumulator.needToRemixSamples = false;\n}\n\nexport function accumulateSound(numberOfCycles: i32): void {\n // Check if any of the individual channels will update\n let channel1WillUpdate: boolean = Channel1.willChannelUpdate(numberOfCycles) || didChannelDacChange(Channel1.channelNumber);\n let channel2WillUpdate: boolean = Channel2.willChannelUpdate(numberOfCycles) || didChannelDacChange(Channel2.channelNumber);\n let channel3WillUpdate: boolean = Channel3.willChannelUpdate(numberOfCycles) || didChannelDacChange(Channel3.channelNumber);\n let channel4WillUpdate: boolean = Channel4.willChannelUpdate(numberOfCycles) || didChannelDacChange(Channel4.channelNumber);\n\n if (channel1WillUpdate) {\n SoundAccumulator.channel1Sample = Channel1.getSampleFromCycleCounter();\n }\n if (channel2WillUpdate) {\n SoundAccumulator.channel2Sample = Channel2.getSampleFromCycleCounter();\n }\n if (channel3WillUpdate) {\n SoundAccumulator.channel3Sample = Channel3.getSampleFromCycleCounter();\n }\n if (channel4WillUpdate) {\n SoundAccumulator.channel4Sample = Channel4.getSampleFromCycleCounter();\n }\n\n // If any channel updated, we need to re-mix our samples\n if (channel1WillUpdate || channel2WillUpdate || channel3WillUpdate || channel4WillUpdate) {\n SoundAccumulator.needToRemixSamples = true;\n }\n\n // Do Some downsampling magic\n Sound.downSampleCycleCounter += numberOfCycles * Sound.downSampleCycleMultiplier;\n if (Sound.downSampleCycleCounter >= Sound.maxDownSampleCycles()) {\n // Reset the downsample counter\n // Don't set to zero to catch overflowed cycles\n Sound.downSampleCycleCounter -= Sound.maxDownSampleCycles();\n\n if (SoundAccumulator.needToRemixSamples || SoundAccumulator.mixerVolumeChanged || SoundAccumulator.mixerEnabledChanged) {\n mixChannelSamples(\n SoundAccumulator.channel1Sample,\n SoundAccumulator.channel2Sample,\n SoundAccumulator.channel3Sample,\n SoundAccumulator.channel4Sample\n );\n }\n\n // Finally Simply place the accumulated sample in memory\n // Set our volumes in memory\n // +1 so it can not be zero\n setLeftAndRightOutputForAudioQueue(\n SoundAccumulator.leftChannelSampleUnsignedByte + 1,\n SoundAccumulator.rightChannelSampleUnsignedByte + 1,\n Sound.audioQueueIndex\n );\n Sound.audioQueueIndex += 1;\n\n // Don't allow our audioQueueIndex to overflow into other parts of the wasmBoy memory map\n // https://docs.google.com/spreadsheets/d/17xrEzJk5-sCB9J2mMJcVnzhbE-XH_NvczVSQH9OHvRk/edit#gid=0\n // Not 0xFFFF because we need half of 64kb since we store left and right channel\n if (Sound.audioQueueIndex >= Sound.wasmBoyMemoryMaxBufferSize / 2 - 1) {\n Sound.audioQueueIndex -= 1;\n }\n }\n}\n\n// Function used by SoundAccumulator to find out if a channel Dac Changed\nfunction didChannelDacChange(channelNumber: i32): boolean {\n switch (channelNumber) {\n case Channel1.channelNumber:\n if (SoundAccumulator.channel1DacEnabled !== Channel1.isDacEnabled) {\n SoundAccumulator.channel1DacEnabled = Channel1.isDacEnabled;\n return true;\n }\n return false;\n case Channel2.channelNumber:\n if (SoundAccumulator.channel2DacEnabled !== Channel2.isDacEnabled) {\n SoundAccumulator.channel2DacEnabled = Channel2.isDacEnabled;\n return true;\n }\n return false;\n case Channel3.channelNumber:\n if (SoundAccumulator.channel3DacEnabled !== Channel3.isDacEnabled) {\n SoundAccumulator.channel3DacEnabled = Channel3.isDacEnabled;\n return true;\n }\n return false;\n case Channel4.channelNumber:\n if (SoundAccumulator.channel4DacEnabled !== Channel4.isDacEnabled) {\n SoundAccumulator.channel4DacEnabled = Channel4.isDacEnabled;\n return true;\n }\n return false;\n }\n return false;\n}\n","import { getSaveStateMemoryOffset } from '../core';\nimport { Cpu } from '../cpu/index';\nimport {\n eightBitLoadFromGBMemory,\n eightBitStoreIntoGBMemory,\n loadBooleanDirectlyFromWasmMemory,\n storeBooleanDirectlyToWasmMemory\n} from '../memory/index';\nimport { requestTimerInterrupt } from '../interrupts/index';\nimport { checkBitOnByte, hexLog } from '../helpers/index';\n\nexport class Timers {\n // Current cycles\n // This will be used for batch processing\n static currentCycles: i32 = 0;\n\n // Number of cycles to run in each batch process\n static batchProcessCycles(): i32 {\n return 256;\n }\n\n static readonly memoryLocationDividerRegister: i32 = 0xff04; // DIV\n static dividerRegister: i32 = 0;\n static updateDividerRegister(value: i32): void {\n Timers.dividerRegister = 0;\n eightBitStoreIntoGBMemory(Timers.memoryLocationDividerRegister, 0);\n\n // Also, mooneye tests, resetting DIV resets the timer\n Timers.cycleCounter = 0;\n Timers.timerCounter = Timers.timerModulo;\n }\n static readonly memoryLocationTimerCounter: i32 = 0xff05; // TIMA\n static timerCounter: i32 = 0;\n static updateTimerCounter(value: i32): void {\n Timers.timerCounter = value;\n }\n static readonly memoryLocationTimerModulo: i32 = 0xff06; // TMA\n static timerModulo: i32 = 0;\n static updateTimerModulo(value: i32): void {\n Timers.timerModulo = value;\n }\n\n static readonly memoryLocationTimerControl: i32 = 0xff07; // TAC\n // Bit 2 - Timer Stop (0=Stop, 1=Start)\n // Bits 1-0 - Input Clock Select\n // 00: 4096 Hz (~4194 Hz SGB)\n // 01: 262144 Hz (~268400 Hz SGB)\n // 10: 65536 Hz (~67110 Hz SGB)\n // 11: 16384 Hz (~16780 Hz SGB)\n static timerEnabled: boolean = false;\n static timerInputClock: i32 = 0;\n static updateTimerControl(value: i32): void {\n Timers.timerEnabled = checkBitOnByte(2, value);\n if (!Timers.timerEnabled) {\n return;\n }\n\n Timers.timerInputClock = value & 0x03;\n\n // Set our new current max, and reset the cycle counter\n Timers.cycleCounter = 0;\n Timers.currentMaxCycleCount = getFrequencyFromInputClockSelect();\n }\n\n // Cycle counter. This is used to determine if we should increment the REAL timer\n // I know this is weird, but it's all to make sure the emulation is in sync :p\n static cycleCounter: i32 = 0x00;\n static currentMaxCycleCount: i32 = 256;\n\n // Another timer, that doesn't fire intterupts, but jsut counts to 255, and back to zero :p\n // In Cgb mode, this still counts at the same rate since the CPU doubles and so does this counter\n // RealBoy Blog Post:\n // we also know that we have to increment the DIV register 16384 times per second.\n // Recall that the CPU frequency is 4194304 Hz, which means that every second the CPU produces 4194304 cycles.\n // We want to know the amount of cycles required for the DIV register to be incremented;\n // we get that 4194304/16384=256 CPU cycles are required before incrementing the DIV register.\n static dividerRegisterCycleCounter: i32 = 0x00;\n static dividerRegisterMaxCycleCount(): i32 {\n return 256;\n }\n\n // Save States\n\n static readonly saveStateSlot: i32 = 5;\n\n // Function to save the state of the class\n // TODO: Save state for new properties on Timers\n static saveState(): void {\n store(getSaveStateMemoryOffset(0x00, Timers.saveStateSlot), Timers.cycleCounter);\n store(getSaveStateMemoryOffset(0x04, Timers.saveStateSlot), Timers.currentMaxCycleCount);\n store(getSaveStateMemoryOffset(0x08, Timers.saveStateSlot), Timers.dividerRegisterCycleCounter);\n\n eightBitStoreIntoGBMemory(Timers.memoryLocationDividerRegister, Timers.dividerRegister);\n eightBitStoreIntoGBMemory(Timers.memoryLocationTimerCounter, Timers.timerCounter);\n }\n\n // Function to load the save state from memory\n static loadState(): void {\n Timers.cycleCounter = load(getSaveStateMemoryOffset(0x00, Timers.saveStateSlot));\n Timers.currentMaxCycleCount = load(getSaveStateMemoryOffset(0x04, Timers.saveStateSlot));\n Timers.dividerRegisterCycleCounter = load(getSaveStateMemoryOffset(0x08, Timers.saveStateSlot));\n\n Timers.dividerRegister = eightBitLoadFromGBMemory(Timers.memoryLocationDividerRegister);\n Timers.updateTimerCounter(eightBitLoadFromGBMemory(Timers.memoryLocationTimerCounter));\n Timers.updateTimerModulo(eightBitLoadFromGBMemory(Timers.memoryLocationTimerModulo));\n Timers.updateTimerControl(eightBitLoadFromGBMemory(Timers.memoryLocationTimerControl));\n }\n}\n\nexport function initializeTimers(): void {\n // Reset stateful Variables\n Timers.currentCycles = 0;\n Timers.dividerRegister = 0;\n Timers.timerCounter = 0;\n Timers.timerModulo = 0;\n Timers.timerEnabled = false;\n Timers.timerInputClock = 0;\n Timers.cycleCounter = 0;\n Timers.dividerRegisterCycleCounter = 0;\n\n if (Cpu.GBCEnabled) {\n eightBitStoreIntoGBMemory(0xff04, 0x2f);\n Timers.dividerRegister = 0x2f;\n // 0xFF05 -> 0xFF06 = 0x00\n eightBitStoreIntoGBMemory(0xff07, 0xf8);\n Timers.updateTimerControl(0xf8);\n } else {\n eightBitStoreIntoGBMemory(0xff04, 0xab);\n Timers.dividerRegister = 0xab;\n // 0xFF05 -> 0xFF06 = 0x00\n eightBitStoreIntoGBMemory(0xff07, 0xf8);\n Timers.updateTimerControl(0xf8);\n }\n}\n\n// Batch Process Timers\n// Only checked on writes\n// Function to batch process our Timers after we skipped so many cycles\nexport function batchProcessTimers(): void {\n // Get our current batch process cycles\n // This will depend on the least amount of cycles we need to update\n // Something\n let batchProcessCycles: i32 = Timers.batchProcessCycles();\n if (Timers.timerEnabled && Timers.currentMaxCycleCount < batchProcessCycles) {\n batchProcessCycles = Timers.currentMaxCycleCount;\n }\n\n if (Timers.currentCycles < batchProcessCycles) {\n return;\n }\n\n while (Timers.currentCycles >= batchProcessCycles) {\n updateTimers(batchProcessCycles);\n Timers.currentCycles = Timers.currentCycles - batchProcessCycles;\n }\n}\n\nexport function updateTimers(numberOfCycles: i32): void {\n _checkDividerRegister(numberOfCycles);\n\n if (!Timers.timerEnabled) {\n return;\n }\n\n // Add our cycles our cycle counter\n Timers.cycleCounter += numberOfCycles;\n\n while (Timers.cycleCounter >= Timers.currentMaxCycleCount) {\n // Reset our cycle counters\n // Not setting to zero as we do not want to drop cycles\n Timers.cycleCounter -= Timers.currentMaxCycleCount;\n\n if (Timers.timerCounter >= 255) {\n // Store Timer Modulator inside of TIMA\n Timers.timerCounter = Timers.timerModulo;\n\n // Fire off timer interrupt\n requestTimerInterrupt();\n } else {\n Timers.timerCounter += 1;\n }\n }\n}\n\n// Function to update our divider register\nfunction _checkDividerRegister(numberOfCycles: i32): void {\n // Every 256 clock cycles need to increment\n Timers.dividerRegisterCycleCounter += numberOfCycles;\n\n if (Timers.dividerRegisterCycleCounter >= Timers.dividerRegisterMaxCycleCount()) {\n // Reset the cycle counter\n // - 255 to catch any overflow with the cycles\n Timers.dividerRegisterCycleCounter -= Timers.dividerRegisterMaxCycleCount();\n\n Timers.dividerRegister += 1;\n\n if (Timers.dividerRegister > 0xff) {\n Timers.dividerRegister = 0;\n }\n }\n}\n\n// Function to get a cycle count froma passed Timer clock\nfunction getFrequencyFromInputClockSelect(): i32 {\n // Returns value equivalent to\n // Cpu.CLOCK_SPEED / timc frequency\n // TIMC -> 16382\n // Default to 0x03\n let cycleCount: i32 = 256;\n if (Cpu.GBCDoubleSpeed) {\n cycleCount = 512;\n }\n switch (Timers.timerInputClock) {\n case 0x00:\n // TIMC -> 4096\n cycleCount = 1024;\n if (Cpu.GBCDoubleSpeed) {\n cycleCount = 2048;\n }\n return cycleCount;\n case 0x01:\n // TIMC -> 262144\n // TODO: Fix tests involving the 16 cycle timer mode. This is the reason why blargg tests break\n cycleCount = 16;\n if (Cpu.GBCDoubleSpeed) {\n cycleCount = 32;\n }\n return cycleCount;\n case 0x02:\n // TIMC -> 65536\n cycleCount = 64;\n if (Cpu.GBCDoubleSpeed) {\n cycleCount = 126;\n }\n return cycleCount;\n }\n\n return cycleCount;\n}\n","import { getCarryFlag } from '../cpu/index';\nimport { u8Portable } from '../portable/portable';\n\n// Grouped registers\n// possible overload these later to performace actions\n// AF, BC, DE, HL\nexport function concatenateBytes(highByte: i32, lowByte: i32): i32 {\n //https://stackoverflow.com/questions/38298412/convert-two-bytes-into-signed-16-bit-integer-in-javascript\n return ((highByte & 0xff) << 8) | (lowByte & 0xff);\n}\n\nexport function splitHighByte(groupedByte: i32): i32 {\n return (groupedByte & 0xff00) >> 8;\n}\n\nexport function splitLowByte(groupedByte: i32): i32 {\n return groupedByte & 0x00ff;\n}\n\nexport function rotateByteLeft(value: u8): u8 {\n // Rotate left\n // https://stackoverflow.com/questions/19204750/how-do-i-perform-a-circular-rotation-of-a-byte\n // 4-bit example:\n // 1010 -> 0100 | 0001\n return u8Portable((value << 1) | (value >> 7));\n}\n\nexport function rotateByteLeftThroughCarry(value: u8): u8 {\n // Example: https://github.com/nakardo/node-gameboy/blob/master/lib/cpu/opcodes.js\n // Through carry meaning, instead of raotating the bit that gets dropped off, but the carry there instead\n return u8Portable((value << 1) | getCarryFlag());\n}\n\nexport function rotateByteRight(value: u8): u8 {\n // Rotate right\n // 4-bit example:\n // 1010 -> 0101 | 0000\n return u8Portable((value >> 1) | (value << 7));\n}\n\nexport function rotateByteRightThroughCarry(value: u8): u8 {\n // Example: https://github.com/nakardo/node-gameboy/blob/master/lib/cpu/opcodes.js\n // Through carry meaning, instead of raotating the bit that gets dropped off, put the carry there instead\n return u8Portable((value >> 1) | (getCarryFlag() << 7));\n}\n\nexport function setBitOnByte(bitPosition: i32, byte: i32): i32 {\n return byte | (0x01 << bitPosition);\n}\n\nexport function resetBitOnByte(bitPosition: i32, byte: i32): i32 {\n return byte & ~(0x01 << bitPosition);\n}\n\nexport function checkBitOnByte(bitPosition: i32, byte: i32): boolean {\n // Perforamnce improvements\n // https://github.com/AssemblyScript/assemblyscript/issues/40\n return (byte & (1 << bitPosition)) != 0;\n}\n\nnamespace env {\n export declare function log(message: string, arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i32): void;\n export declare function hexLog(arg0: i32, arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i32): void;\n export declare function performanceTimestamp(id: i32, value: i32): void;\n}\n\nexport function log(\n message: string,\n arg0: i32 = -9999,\n arg1: i32 = -9999,\n arg2: i32 = -9999,\n arg3: i32 = -9999,\n arg4: i32 = -9999,\n arg5: i32 = -9999\n): void {\n env.log(message, arg0, arg1, arg2, arg3, arg4, arg5);\n}\n\nexport function hexLog(\n arg0: i32 = -9999,\n arg1: i32 = -9999,\n arg2: i32 = -9999,\n arg3: i32 = -9999,\n arg4: i32 = -9999,\n arg5: i32 = -9999\n): void {\n env.hexLog(arg0, arg1, arg2, arg3, arg4, arg5);\n}\n\nexport function performanceTimestamp(id: i32 = -9999, value: i32 = -9999): void {\n env.performanceTimestamp(id, value);\n}\n","// Imports\nimport { Cpu } from './index';\nimport { handleCbOpcode } from './cbOpcodes';\nimport {\n setZeroFlag,\n getZeroFlag,\n setSubtractFlag,\n getSubtractFlag,\n setHalfCarryFlag,\n getHalfCarryFlag,\n setCarryFlag,\n getCarryFlag,\n checkAndSetEightBitCarryFlag,\n checkAndSetEightBitHalfCarryFlag,\n checkAndSetSixteenBitFlagsAddOverflow\n} from './flags';\nimport {\n addARegister,\n addAThroughCarryRegister,\n subARegister,\n subAThroughCarryRegister,\n andARegister,\n xorARegister,\n orARegister,\n cpARegister,\n relativeJump\n} from './instructions';\nimport { Config } from '../config';\nimport {\n log,\n hexLog,\n performanceTimestamp,\n rotateByteLeft,\n rotateByteLeftThroughCarry,\n rotateByteRight,\n rotateByteRightThroughCarry,\n concatenateBytes,\n splitHighByte,\n splitLowByte,\n checkBitOnByte,\n resetBitOnByte,\n setBitOnByte\n} from '../helpers/index';\nimport {\n Memory,\n eightBitStoreIntoGBMemoryWithTraps,\n sixteenBitStoreIntoGBMemoryWithTraps,\n eightBitLoadFromGBMemoryWithTraps,\n eightBitLoadFromGBMemory,\n sixteenBitLoadFromGBMemory\n} from '../memory/index';\nimport { Timers, batchProcessTimers, updateTimers } from '../timers/index';\nimport { Interrupts, setInterrupts, checkInterrupts } from '../interrupts/index';\nimport { Graphics, updateGraphics, batchProcessGraphics } from '../graphics/index';\nimport { Sound, updateSound } from '../sound/index';\nimport { u8Portable, u16Portable, i8Portable } from '../portable/portable';\n\n// Take in any opcode, and decode it, and return the number of cycles\n// Program counter can be gotten from getProgramCounter();\n// Setting return value to i32 instead of u16, as we want to return a negative number on error\n// https://rednex.github.io/rgbds/gbz80.7.html\n// http://pastraiser.com/cpu/gameboy/gameboyopcodes.html\nexport function executeOpcode(opcode: i32): i32 {\n // Initialize our number of cycles\n // Return -1 if no opcode was found, representing an error\n let numberOfCycles: i32 = -1;\n\n // Always implement the program counter by one\n // Any other value can just subtract or add however much offset before reaching this line\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n\n // Split our opcode into a high nibble to speed up performance\n // Running 255 if statements is slow, even in wasm haha!\n let opcodeHighNibble: i32 = opcode & 0xf0;\n opcodeHighNibble = opcodeHighNibble >> 4;\n\n // NOTE: @binji rule of thumb: it takes 4 cpu cycles to read one byte\n // Therefore isntructions that use more than just the opcode (databyte one and two) will take at least\n // 8 cyckles to use getDataByteOne(), and two cycles to use the concatented\n\n // Not using a switch statement to avoid cannot redeclare this variable errors\n // And it would be a ton of work :p\n\n switch (opcodeHighNibble) {\n case 0x00:\n return handleOpcode0x(opcode);\n case 0x01:\n return handleOpcode1x(opcode);\n case 0x02:\n return handleOpcode2x(opcode);\n case 0x03:\n return handleOpcode3x(opcode);\n case 0x04:\n return handleOpcode4x(opcode);\n case 0x05:\n return handleOpcode5x(opcode);\n case 0x06:\n return handleOpcode6x(opcode);\n case 0x07:\n return handleOpcode7x(opcode);\n case 0x08:\n return handleOpcode8x(opcode);\n case 0x09:\n return handleOpcode9x(opcode);\n case 0x0a:\n return handleOpcodeAx(opcode);\n case 0x0b:\n return handleOpcodeBx(opcode);\n case 0x0c:\n return handleOpcodeCx(opcode);\n case 0x0d:\n return handleOpcodeDx(opcode);\n case 0x0e:\n return handleOpcodeEx(opcode);\n default:\n return handleOpcodeFx(opcode);\n }\n}\n\n// Functions to access the next operands of a opcode, reffering to them as \"dataBytes\"\nfunction getDataByteOne(): u8 {\n return eightBitLoadFromGBMemory(Cpu.programCounter);\n}\n\nfunction getDataByteTwo(): u8 {\n return eightBitLoadFromGBMemory(u16Portable(Cpu.programCounter + 1));\n}\n// Get our concatenated databyte one and getDataByteTwo()\n// Find and replace with : getConcatenatedDataByte()\nfunction getConcatenatedDataByte(): u16 {\n return concatenateBytes(getDataByteTwo(), getDataByteOne());\n}\n\nfunction handleOpcode0x(opcode: i32): i32 {\n switch (opcode) {\n case 0x00:\n // NOP\n // 1 4\n // No Operation\n return 4;\n case 0x01:\n // LD BC,d16\n // 3 12\n\n Cpu.registerB = splitHighByte(getConcatenatedDataByte());\n Cpu.registerC = splitLowByte(getConcatenatedDataByte());\n Cpu.programCounter = u16Portable(Cpu.programCounter + 2);\n return 12;\n case 0x02:\n // LD (BC),A\n // 1 8\n // () means load into address pointed by BC\n eightBitStoreIntoGBMemoryWithTraps(concatenateBytes(Cpu.registerB, Cpu.registerC), Cpu.registerA);\n return 8;\n case 0x03:\n // INC BC\n // 1 8\n let registerBC3: u16 = concatenateBytes(Cpu.registerB, Cpu.registerC);\n registerBC3++;\n Cpu.registerB = splitHighByte(registerBC3);\n Cpu.registerC = splitLowByte(registerBC3);\n return 8;\n case 0x04:\n // INC B\n // 1 4\n // Z 0 H -\n checkAndSetEightBitHalfCarryFlag(Cpu.registerB, 1);\n Cpu.registerB = u8Portable(Cpu.registerB + 1);\n if (Cpu.registerB === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(0);\n return 4;\n case 0x05:\n // DEC B\n // 1 4\n // Z 1 H -\n checkAndSetEightBitHalfCarryFlag(Cpu.registerB, -1);\n Cpu.registerB = u8Portable(Cpu.registerB - 1);\n if (Cpu.registerB === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(1);\n return 4;\n case 0x06:\n // LD B,d8\n // 2 8\n Cpu.registerB = getDataByteOne();\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n case 0x07:\n // RLCA\n // 1 4\n // 0 0 0 C\n // Check for the carry\n if ((Cpu.registerA & 0x80) === 0x80) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n Cpu.registerA = rotateByteLeft(Cpu.registerA);\n // Set all other flags to zero\n setZeroFlag(0);\n setSubtractFlag(0);\n setHalfCarryFlag(0);\n return 4;\n case 0x08:\n // LD (a16),SP\n // 3 20\n // Load the stack pointer into the 16 bit address represented by the two data bytes\n sixteenBitStoreIntoGBMemoryWithTraps(getConcatenatedDataByte(), Cpu.stackPointer);\n Cpu.programCounter = u16Portable(Cpu.programCounter + 2);\n return 20;\n case 0x09:\n // ADD HL,BC\n // 1 8\n // - 0 H C\n let registerHL: u16 = concatenateBytes(Cpu.registerH, Cpu.registerL);\n let registerBC9: u16 = concatenateBytes(Cpu.registerB, Cpu.registerC);\n checkAndSetSixteenBitFlagsAddOverflow(registerHL, registerBC9, false);\n let result: u16 = u16Portable((registerHL + registerBC9));\n Cpu.registerH = splitHighByte(result);\n Cpu.registerL = splitLowByte(result);\n setSubtractFlag(0);\n return 8;\n case 0x0a:\n // LD A,(BC)\n // 1 8\n Cpu.registerA = eightBitLoadFromGBMemoryWithTraps(concatenateBytes(Cpu.registerB, Cpu.registerC));\n return 8;\n case 0x0b:\n // DEC BC\n // 1 8\n let registerBCB: u16 = concatenateBytes(Cpu.registerB, Cpu.registerC);\n registerBCB = u16Portable(registerBCB - 1);\n Cpu.registerB = splitHighByte(registerBCB);\n Cpu.registerC = splitLowByte(registerBCB);\n return 8;\n case 0x0c:\n // INC C\n // 1 4\n // Z 0 H -\n checkAndSetEightBitHalfCarryFlag(Cpu.registerC, 1);\n Cpu.registerC = u8Portable(Cpu.registerC + 1);\n if (Cpu.registerC === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(0);\n return 4;\n case 0x0d:\n // DEC C\n // 1 4\n // Z 1 H -\n checkAndSetEightBitHalfCarryFlag(Cpu.registerC, -1);\n Cpu.registerC = u8Portable(Cpu.registerC - 1);\n if (Cpu.registerC === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(1);\n return 4;\n case 0x0e:\n // LD C,d8\n // 2 8\n Cpu.registerC = getDataByteOne();\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n case 0x0f:\n // RRCA\n // 1 4\n // 0 0 0 C\n // Check for the last bit, to see if it will be carried\n if ((Cpu.registerA & 0x01) > 0) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n Cpu.registerA = rotateByteRight(Cpu.registerA);\n // Set all other flags to zero\n setZeroFlag(0);\n setSubtractFlag(0);\n setHalfCarryFlag(0);\n return 4;\n }\n return -1;\n}\n\nfunction handleOpcode1x(opcode: i32): i32 {\n switch (opcode) {\n case 0x10:\n // STOP 0\n // 2 4\n // Enter CPU very low power mode. Also used to switch between double and normal speed CPU modes in GBC.\n // Meaning Don't Decode anymore opcodes , or updated the LCD until joypad interrupt (or when button is pressed if I am wrong)\n // See HALT\n\n // If we are in gameboy color mode, set the new speed\n if (Cpu.GBCEnabled) {\n let speedSwitch: i32 = eightBitLoadFromGBMemoryWithTraps(Cpu.memoryLocationSpeedSwitch);\n if (checkBitOnByte(0, speedSwitch)) {\n // Reset the prepare bit\n speedSwitch = resetBitOnByte(0, speedSwitch);\n\n // Switch to the new mode, and set the speed switch to the OTHER speed, to represent our new speed\n if (!checkBitOnByte(7, speedSwitch)) {\n Cpu.GBCDoubleSpeed = true;\n speedSwitch = setBitOnByte(7, speedSwitch);\n } else {\n Cpu.GBCDoubleSpeed = false;\n speedSwitch = resetBitOnByte(7, speedSwitch);\n }\n\n // Store the final speed switch\n eightBitStoreIntoGBMemoryWithTraps(Cpu.memoryLocationSpeedSwitch, speedSwitch);\n\n // Cycle accurate gameboy docs says this takes 76 clocks\n return 76;\n }\n }\n\n // NOTE: This breaks Blarggs CPU tests, therefore, need to implement STOP at somepoint to get around this\n //Cpu.isStopped = true;\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 4;\n case 0x11:\n // LD DE,d16\n // 3 12\n Cpu.registerD = splitHighByte(getConcatenatedDataByte());\n Cpu.registerE = splitLowByte(getConcatenatedDataByte());\n Cpu.programCounter = u16Portable(Cpu.programCounter + 2);\n return 12;\n case 0x12:\n // LD (DE),A\n // 1 8\n eightBitStoreIntoGBMemoryWithTraps(concatenateBytes(Cpu.registerD, Cpu.registerE), Cpu.registerA);\n return 8;\n case 0x13:\n // INC DE\n // 1 8\n let registerDE3 = concatenateBytes(Cpu.registerD, Cpu.registerE);\n registerDE3 = u16Portable(registerDE3 + 1);\n Cpu.registerD = splitHighByte(registerDE3);\n Cpu.registerE = splitLowByte(registerDE3);\n return 8;\n case 0x14:\n // INC D\n // 1 4\n // Z 0 H -\n checkAndSetEightBitHalfCarryFlag(Cpu.registerD, 1);\n Cpu.registerD = u8Portable(Cpu.registerD + 1);\n if (Cpu.registerD === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(0);\n return 4;\n case 0x15:\n // DEC D\n // 1 4\n // Z 1 H -\n checkAndSetEightBitHalfCarryFlag(Cpu.registerD, -1);\n Cpu.registerD = u8Portable(Cpu.registerD - 1);\n if (Cpu.registerD === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(1);\n return 4;\n case 0x16:\n // LD D,d8\n // 2 8\n Cpu.registerD = getDataByteOne();\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n case 0x17:\n // RLA\n // 1 4\n // 0 0 0 C\n // Check for the carry\n // setting has first bit since we need to use carry\n let hasHighbit = false;\n if ((Cpu.registerA & 0x80) === 0x80) {\n hasHighbit = true;\n }\n Cpu.registerA = rotateByteLeftThroughCarry(Cpu.registerA);\n // OR the carry flag to the end\n if (hasHighbit) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n // Set all other flags to zero\n setZeroFlag(0);\n setSubtractFlag(0);\n setHalfCarryFlag(0);\n return 4;\n case 0x18:\n // JR r8\n // 2 12\n // NOTE: Discoved dataByte is signed\n // However the relative Jump Function handles this\n\n relativeJump(getDataByteOne());\n return 12;\n // Relative Jump Function Handles program counter\n case 0x19:\n // ADD HL,DE\n // 1 8\n // - 0 H C\n let registerHL: u16 = concatenateBytes(Cpu.registerH, Cpu.registerL);\n let registerDE9: u16 = concatenateBytes(Cpu.registerD, Cpu.registerE);\n checkAndSetSixteenBitFlagsAddOverflow(registerHL, registerDE9, false);\n let result: u16 = u16Portable((registerHL + registerDE9));\n Cpu.registerH = splitHighByte(result);\n Cpu.registerL = splitLowByte(result);\n setSubtractFlag(0);\n return 8;\n case 0x1a:\n // LD A,(DE)\n // 1 8\n let registerDEA: u16 = concatenateBytes(Cpu.registerD, Cpu.registerE);\n Cpu.registerA = eightBitLoadFromGBMemoryWithTraps(registerDEA);\n return 8;\n case 0x1b:\n // DEC DE\n // 1 8\n let registerDEB: u16 = concatenateBytes(Cpu.registerD, Cpu.registerE);\n registerDEB = u16Portable(registerDEB - 1);\n Cpu.registerD = splitHighByte(registerDEB);\n Cpu.registerE = splitLowByte(registerDEB);\n return 8;\n case 0x1c:\n // INC E\n // 1 4\n // Z 0 H -\n checkAndSetEightBitHalfCarryFlag(Cpu.registerE, 1);\n Cpu.registerE = u8Portable(Cpu.registerE + 1);\n if (Cpu.registerE === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(0);\n return 4;\n case 0x1d:\n // DEC E\n // 1 4\n // Z 1 H -\n checkAndSetEightBitHalfCarryFlag(Cpu.registerE, -1);\n Cpu.registerE = u8Portable(Cpu.registerE - 1);\n if (Cpu.registerE === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(1);\n return 4;\n case 0x1e:\n // LD E,d8\n // 2 8\n Cpu.registerE = getDataByteOne();\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n case 0x1f:\n // RRA\n // 1 4\n // 0 0 0 C\n // Check for the carry\n // setting has low bit since we need to use carry\n let hasLowBit = false;\n if ((Cpu.registerA & 0x01) === 0x01) {\n hasLowBit = true;\n }\n Cpu.registerA = rotateByteRightThroughCarry(Cpu.registerA);\n\n if (hasLowBit) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n // Set all other flags to zero\n setZeroFlag(0);\n setSubtractFlag(0);\n setHalfCarryFlag(0);\n return 4;\n }\n\n return -1;\n}\n\nfunction handleOpcode2x(opcode: i32): i32 {\n switch (opcode) {\n case 0x20:\n // JR NZ,r8\n // 2 12/8\n // NOTE: NZ stands for not [flag], so in this case, not zero flag\n // Also, / means, if condition. so if met, 12 cycles, otherwise 8 cycles\n if (getZeroFlag() === 0) {\n relativeJump(getDataByteOne());\n return 12;\n // Relative Jump Funciton handles program counter\n } else {\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n }\n case 0x21:\n // LD HL,d16\n // 3 12\n let sixteenBitDataByte = getConcatenatedDataByte();\n Cpu.registerH = splitHighByte(sixteenBitDataByte);\n Cpu.registerL = splitLowByte(sixteenBitDataByte);\n Cpu.programCounter = u16Portable(Cpu.programCounter + 2);\n return 12;\n case 0x22:\n // LD (HL+),A\n // 1 8\n let registerHL2: u16 = concatenateBytes(Cpu.registerH, Cpu.registerL);\n eightBitStoreIntoGBMemoryWithTraps(registerHL2, Cpu.registerA);\n registerHL2 = u16Portable(registerHL2 + 1);\n Cpu.registerH = splitHighByte(registerHL2);\n Cpu.registerL = splitLowByte(registerHL2);\n return 8;\n case 0x23:\n // INC HL\n // 1 8\n let registerHL3 = concatenateBytes(Cpu.registerH, Cpu.registerL);\n registerHL3 = u16Portable(registerHL3 + 1);\n Cpu.registerH = splitHighByte(registerHL3);\n Cpu.registerL = splitLowByte(registerHL3);\n return 8;\n case 0x24:\n // INC H\n // 1 4\n // Z 0 H -\n checkAndSetEightBitHalfCarryFlag(Cpu.registerH, 1);\n Cpu.registerH = u8Portable(Cpu.registerH + 1);\n if (Cpu.registerH === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(0);\n return 4;\n case 0x25:\n // DEC H\n // 1 4\n // Z 1 H -\n checkAndSetEightBitHalfCarryFlag(Cpu.registerH, -1);\n Cpu.registerH = u8Portable(Cpu.registerH - 1);\n if (Cpu.registerH === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(1);\n return 4;\n case 0x26:\n // LD H,d8\n // 2 8\n Cpu.registerH = getDataByteOne();\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n case 0x27:\n // DAA\n // 1 4\n // Z - 0 C\n let adjustedRegister: u8 = 0;\n let adjustment: u8 = 0;\n\n if (getHalfCarryFlag() > 0) {\n adjustment = adjustment | 0x06;\n }\n if (getCarryFlag() > 0) {\n adjustment = adjustment | 0x60;\n }\n\n if (getSubtractFlag() > 0) {\n adjustedRegister = u8Portable(Cpu.registerA - adjustment);\n } else {\n if ((Cpu.registerA & 0x0f) > 0x09) {\n adjustment = adjustment | 0x06;\n }\n if (Cpu.registerA > 0x99) {\n adjustment = adjustment | 0x60;\n }\n adjustedRegister = u8Portable(Cpu.registerA + adjustment);\n }\n\n // Now set our flags to the correct values\n if (adjustedRegister === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n if ((adjustment & 0x60) !== 0) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n setHalfCarryFlag(0);\n\n Cpu.registerA = adjustedRegister;\n return 4;\n case 0x28:\n // JR Z,r8\n // 2 12/8\n if (getZeroFlag() > 0) {\n relativeJump(getDataByteOne());\n return 12;\n // Relative Jump funciton handles pogram counter\n } else {\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n }\n case 0x29:\n // ADD HL,HL\n // 1 8\n // - 0 H C\n let registerHL9: u16 = concatenateBytes(Cpu.registerH, Cpu.registerL);\n checkAndSetSixteenBitFlagsAddOverflow(registerHL9, registerHL9, false);\n registerHL9 = u16Portable(registerHL9 * 2);\n Cpu.registerH = splitHighByte(registerHL9);\n Cpu.registerL = splitLowByte(registerHL9);\n setSubtractFlag(0);\n return 8;\n case 0x2a:\n // LD A,(HL+)\n // 1 8\n let registerHLA: u16 = concatenateBytes(Cpu.registerH, Cpu.registerL);\n Cpu.registerA = eightBitLoadFromGBMemoryWithTraps(registerHLA);\n registerHLA = u16Portable(registerHLA + 1);\n Cpu.registerH = splitHighByte(registerHLA);\n Cpu.registerL = splitLowByte(registerHLA);\n return 8;\n case 0x2b:\n // DEC HL\n // 1 8\n let registerHLB = concatenateBytes(Cpu.registerH, Cpu.registerL);\n registerHLB = u16Portable(registerHLB - 1);\n Cpu.registerH = splitHighByte(registerHLB);\n Cpu.registerL = splitLowByte(registerHLB);\n return 8;\n case 0x2c:\n // INC L\n // 1 4\n // Z 0 H -\n checkAndSetEightBitHalfCarryFlag(Cpu.registerL, 1);\n Cpu.registerL = u8Portable(Cpu.registerL + 1);\n if (Cpu.registerL === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(0);\n return 4;\n case 0x2d:\n // DEC L\n // 1 4\n // Z 1 H -\n checkAndSetEightBitHalfCarryFlag(Cpu.registerL, -1);\n Cpu.registerL = u8Portable(Cpu.registerL - 1);\n if (Cpu.registerL === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(1);\n return 4;\n case 0x2e:\n // LD L,d8\n // 2 8\n Cpu.registerL = getDataByteOne();\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n case 0x2f:\n // CPL\n // 1 4\n // - 1 1 -\n Cpu.registerA = ~Cpu.registerA;\n setSubtractFlag(1);\n setHalfCarryFlag(1);\n return 4;\n }\n return -1;\n}\n\nfunction handleOpcode3x(opcode: i32): i32 {\n switch (opcode) {\n case 0x30:\n // JR NC,r8\n // 2 12 / 8\n if (getCarryFlag() === 0) {\n relativeJump(getDataByteOne());\n return 12;\n // Relative Jump function handles program counter\n } else {\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n }\n case 0x31:\n // LD SP,d16\n // 3 12\n Cpu.stackPointer = getConcatenatedDataByte();\n Cpu.programCounter = u16Portable(Cpu.programCounter + 2);\n return 12;\n case 0x32:\n // LD (HL-),A\n // 1 8\n let registerHL2: u16 = concatenateBytes(Cpu.registerH, Cpu.registerL);\n eightBitStoreIntoGBMemoryWithTraps(registerHL2, Cpu.registerA);\n registerHL2 = u16Portable(registerHL2 - 1);\n Cpu.registerH = splitHighByte(registerHL2);\n Cpu.registerL = splitLowByte(registerHL2);\n return 8;\n case 0x33:\n // INC SP\n // 1 8\n Cpu.stackPointer = u16Portable(Cpu.stackPointer + 1);\n return 8;\n case 0x34:\n // INC (HL)\n // 1 12\n // Z 0 H -\n let registerHL4: u16 = concatenateBytes(Cpu.registerH, Cpu.registerL);\n let valueAtHL4: u8 = eightBitLoadFromGBMemoryWithTraps(registerHL4);\n // Creating a varible for this to fix assemblyscript overflow bug\n // Requires explicit casting\n // https://github.com/AssemblyScript/assemblyscript/issues/26\n let incrementer: u8 = 1;\n checkAndSetEightBitHalfCarryFlag(valueAtHL4, incrementer);\n valueAtHL4 = u8Portable(valueAtHL4 + incrementer);\n\n if (valueAtHL4 === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(0);\n eightBitStoreIntoGBMemoryWithTraps(registerHL4, valueAtHL4);\n return 12;\n case 0x35:\n // DEC (HL)\n // 1 12\n // Z 1 H -\n let registerHL5: u16 = concatenateBytes(Cpu.registerH, Cpu.registerL);\n let valueAtHL5: u8 = eightBitLoadFromGBMemoryWithTraps(registerHL5);\n // NOTE: This opcode may not overflow correctly,\n // Please see previous opcode\n checkAndSetEightBitHalfCarryFlag(valueAtHL5, -1);\n valueAtHL5 = u8Portable(valueAtHL5 - 1);\n if (valueAtHL5 === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(1);\n eightBitStoreIntoGBMemoryWithTraps(registerHL5, valueAtHL5);\n return 12;\n case 0x36:\n // LD (HL),d8\n // 2 12\n eightBitStoreIntoGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL), getDataByteOne());\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 12;\n case 0x37:\n // SCF\n // 1 4\n // - 0 0 1\n // Simply set the carry flag\n setSubtractFlag(0);\n setHalfCarryFlag(0);\n setCarryFlag(1);\n return 4;\n case 0x38:\n // JR C,r8\n // 2 12/8\n if (getCarryFlag() === 1) {\n relativeJump(getDataByteOne());\n return 12;\n // Relative Jump Funciton handles program counter\n } else {\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n }\n case 0x39:\n // ADD HL,SP\n // 1 8\n // - 0 H C\n let registerHL9: u16 = concatenateBytes(Cpu.registerH, Cpu.registerL);\n checkAndSetSixteenBitFlagsAddOverflow(registerHL9, Cpu.stackPointer, false);\n let result: u16 = u16Portable((registerHL9 + Cpu.stackPointer));\n Cpu.registerH = splitHighByte(result);\n Cpu.registerL = splitLowByte(result);\n setSubtractFlag(0);\n return 8;\n case 0x3a:\n // LD A,(HL-)\n // 1 8\n let registerHLA: u16 = concatenateBytes(Cpu.registerH, Cpu.registerL);\n Cpu.registerA = eightBitLoadFromGBMemoryWithTraps(registerHLA);\n registerHLA = u16Portable(registerHLA - 1);\n Cpu.registerH = splitHighByte(registerHLA);\n Cpu.registerL = splitLowByte(registerHLA);\n return 8;\n case 0x3b:\n // DEC SP\n // 1 8\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 1);\n return 8;\n case 0x3c:\n // INC A\n // 1 4\n // Z 0 H -\n checkAndSetEightBitHalfCarryFlag(Cpu.registerA, 1);\n Cpu.registerA = u8Portable(Cpu.registerA + 1);\n if (Cpu.registerA === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(0);\n return 4;\n case 0x3d:\n // DEC A\n // 1 4\n // Z 1 H -\n checkAndSetEightBitHalfCarryFlag(Cpu.registerA, -1);\n Cpu.registerA = u8Portable(Cpu.registerA - 1);\n if (Cpu.registerA === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(1);\n return 4;\n case 0x3e:\n // LD A,d8\n // 2 8\n Cpu.registerA = getDataByteOne();\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n case 0x3f:\n // CCF\n // 1 4\n // - 0 0 C\n setSubtractFlag(0);\n setHalfCarryFlag(0);\n if (getCarryFlag() > 0) {\n setCarryFlag(0);\n } else {\n setCarryFlag(1);\n }\n return 4;\n }\n return -1;\n}\n\nfunction handleOpcode4x(opcode: i32): i32 {\n switch (opcode) {\n case 0x40:\n // LD B,B\n // 1 4\n // Load B into B, Do nothing\n return 4;\n case 0x41:\n // LD B,C\n // 1 4\n Cpu.registerB = Cpu.registerC;\n return 4;\n case 0x42:\n // LD B,D\n // 1 4\n Cpu.registerB = Cpu.registerD;\n return 4;\n case 0x43:\n // LD B,E\n // 1 4\n Cpu.registerB = Cpu.registerE;\n return 4;\n case 0x44:\n // LD B,H\n // 1 4\n Cpu.registerB = Cpu.registerH;\n return 4;\n case 0x45:\n // LD B,L\n // 1 4\n Cpu.registerB = Cpu.registerL;\n return 4;\n case 0x46:\n // LD B,(HL)\n // 1 8\n Cpu.registerB = eightBitLoadFromGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL));\n return 8;\n case 0x47:\n // LD B,A\n // 1 4\n Cpu.registerB = Cpu.registerA;\n return 4;\n case 0x48:\n // LD C,B\n // 1 4\n Cpu.registerC = Cpu.registerB;\n return 4;\n case 0x49:\n // LD C,C\n // 1 4\n // Do nothing\n return 4;\n case 0x4a:\n // LD C,D\n // 1 4\n Cpu.registerC = Cpu.registerD;\n return 4;\n case 0x4b:\n // LD C,E\n // 1 4\n Cpu.registerC = Cpu.registerE;\n return 4;\n case 0x4c:\n // LD C,H\n // 1 4\n Cpu.registerC = Cpu.registerH;\n return 4;\n case 0x4d:\n // LD C,L\n // 1 4\n Cpu.registerC = Cpu.registerL;\n return 4;\n case 0x4e:\n // LD C,(HL)\n // 1 8\n Cpu.registerC = eightBitLoadFromGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL));\n return 8;\n case 0x4f:\n // LD C,A\n // 1 4\n Cpu.registerC = Cpu.registerA;\n return 4;\n }\n return -1;\n}\n\nfunction handleOpcode5x(opcode: i32): i32 {\n switch (opcode) {\n case 0x50:\n // LD D,B\n // 1 4\n Cpu.registerD = Cpu.registerB;\n return 4;\n case 0x51:\n // LD D,C\n // 1 4\n Cpu.registerD = Cpu.registerC;\n return 4;\n case 0x52:\n // LD D,D\n // 1 4\n // Do Nothing\n return 4;\n case 0x53:\n // LD D,E\n // 1 4\n Cpu.registerD = Cpu.registerE;\n return 4;\n case 0x54:\n // LD D,H\n // 1 4\n Cpu.registerD = Cpu.registerH;\n return 4;\n case 0x55:\n // LD D,L\n // 1 4\n Cpu.registerD = Cpu.registerL;\n return 4;\n case 0x56:\n // LD D,(HL)\n // 1 8\n Cpu.registerD = eightBitLoadFromGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL));\n return 8;\n case 0x57:\n // LD D,A\n // 1 4\n Cpu.registerD = Cpu.registerA;\n return 4;\n case 0x58:\n // LD E,B\n // 1 4\n Cpu.registerE = Cpu.registerB;\n return 4;\n case 0x59:\n // LD E,C\n // 1 4\n Cpu.registerE = Cpu.registerC;\n return 4;\n case 0x5a:\n // LD E,D\n // 1 4\n Cpu.registerE = Cpu.registerD;\n return 4;\n case 0x5b:\n // LD E,E\n // 1 4\n // Do Nothing\n return 4;\n case 0x5c:\n // LD E,H\n // 1 4\n Cpu.registerE = Cpu.registerH;\n return 4;\n case 0x5d:\n // LD E,L\n // 1 4\n Cpu.registerE = Cpu.registerL;\n return 4;\n case 0x5e:\n // LD E,(HL)\n // 1 4\n Cpu.registerE = eightBitLoadFromGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL));\n return 4;\n case 0x5f:\n // LD E,A\n // 1 4\n Cpu.registerE = Cpu.registerA;\n return 4;\n }\n return -1;\n}\n\nfunction handleOpcode6x(opcode: i32): i32 {\n switch (opcode) {\n case 0x60:\n // LD H,B\n // 1 8\n // NOTE: Thanks to @binji for catching that this should be 8 cycles, not 4\n Cpu.registerH = Cpu.registerB;\n return 8;\n case 0x61:\n // LD H,C\n // 1 4\n Cpu.registerH = Cpu.registerC;\n return 4;\n case 0x62:\n // LD H,D\n // 1 4\n Cpu.registerH = Cpu.registerD;\n return 4;\n case 0x63:\n // LD H,E\n // 1 4\n Cpu.registerH = Cpu.registerE;\n return 4;\n case 0x64:\n // LD H,H\n // 1 4\n Cpu.registerH = Cpu.registerH;\n return 4;\n case 0x65:\n // LD H,L\n // 1 4\n Cpu.registerH = Cpu.registerL;\n return 4;\n case 0x66:\n // LD H,(HL)\n // 1 8\n Cpu.registerH = eightBitLoadFromGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL));\n return 8;\n case 0x67:\n // LD H,A\n // 1 4\n Cpu.registerH = Cpu.registerA;\n return 4;\n case 0x68:\n // LD L,B\n // 1 4\n Cpu.registerL = Cpu.registerB;\n return 4;\n case 0x69:\n // LD L,C\n // 1 4\n Cpu.registerL = Cpu.registerC;\n return 4;\n case 0x6a:\n // LD L,D\n // 1 4\n Cpu.registerL = Cpu.registerD;\n return 4;\n case 0x6b:\n // LD L,E\n // 1 4\n Cpu.registerL = Cpu.registerE;\n return 4;\n case 0x6c:\n // LD L,H\n // 1 4\n Cpu.registerL = Cpu.registerH;\n return 4;\n case 0x6d:\n // LD L,L\n // 1 4\n Cpu.registerL = Cpu.registerL;\n return 4;\n case 0x6e:\n // LD L,(HL)\n // 1 8\n Cpu.registerL = eightBitLoadFromGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL));\n return 8;\n case 0x6f:\n // LD L,A\n // 1 4\n Cpu.registerL = Cpu.registerA;\n return 4;\n }\n return -1;\n}\n\nfunction handleOpcode7x(opcode: i32): i32 {\n switch (opcode) {\n case 0x70:\n // LD (HL),B\n // 1 8\n eightBitStoreIntoGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL), Cpu.registerB);\n return 8;\n case 0x71:\n // LD (HL),C\n // 1 8\n eightBitStoreIntoGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL), Cpu.registerC);\n return 8;\n case 0x72:\n // LD (HL),D\n // 1 8\n eightBitStoreIntoGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL), Cpu.registerD);\n return 8;\n case 0x73:\n // LD (HL),E\n // 1 8\n eightBitStoreIntoGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL), Cpu.registerE);\n return 8;\n case 0x74:\n // LD (HL),H\n // 1 8\n eightBitStoreIntoGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL), Cpu.registerH);\n return 8;\n case 0x75:\n // LD (HL),L\n // 1 8\n eightBitStoreIntoGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL), Cpu.registerL);\n return 8;\n case 0x76:\n // HALT\n // 1 4\n // Enter CPU very low power mode\n // Meaning Don't Decode anymore opcodes until an interrupt occurs\n // Still need to do timers and things\n\n // Can't Halt during an HDMA\n // https://gist.github.com/drhelius/3394856\n if (!Memory.isHblankHdmaActive) {\n Cpu.isHalted = true;\n }\n return 4;\n case 0x77:\n // LD (HL),A\n // 1 8\n eightBitStoreIntoGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL), Cpu.registerA);\n return 8;\n case 0x78:\n // LD A,B\n // 1 4\n Cpu.registerA = Cpu.registerB;\n return 4;\n case 0x79:\n // LD A,C\n // 1 4\n Cpu.registerA = Cpu.registerC;\n return 4;\n case 0x7a:\n // LD A,D\n // 1 4\n Cpu.registerA = Cpu.registerD;\n return 4;\n case 0x7b:\n // LD A,E\n // 1 4\n Cpu.registerA = Cpu.registerE;\n return 4;\n case 0x7c:\n // LD A,H\n // 1 4\n Cpu.registerA = Cpu.registerH;\n return 4;\n case 0x7d:\n // LD A,L\n // 1 4\n Cpu.registerA = Cpu.registerL;\n return 4;\n case 0x7e:\n // LD A,(HL)\n // 1 8\n // NOTE: Thanks to @binji for catching that this should be 8 cycles, not 4\n Cpu.registerA = eightBitLoadFromGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL));\n return 8;\n case 0x7f:\n // LD A,A\n // 1 4\n // Do Nothing\n return 4;\n }\n return -1;\n}\n\nfunction handleOpcode8x(opcode: i32): i32 {\n switch (opcode) {\n case 0x80:\n // ADD A,B\n // 1 4\n // Z 0 H C\n addARegister(Cpu.registerB);\n return 4;\n case 0x81:\n // ADD A,C\n // 1 4\n // Z 0 H C\n addARegister(Cpu.registerC);\n return 4;\n case 0x82:\n // ADD A,D\n // 1 4\n // Z 0 H C\n addARegister(Cpu.registerD);\n return 4;\n case 0x83:\n // ADD A,E\n // 1 4\n // Z 0 H C\n addARegister(Cpu.registerE);\n return 4;\n case 0x84:\n // ADD A,H\n // 1 4\n // Z 0 H C\n addARegister(Cpu.registerH);\n return 4;\n case 0x85:\n // ADD A,L\n // 1 4\n // Z 0 H C\n addARegister(Cpu.registerL);\n return 4;\n case 0x86:\n // ADD A,(HL)\n // 1 8\n // Z 0 H C\n let valueAtHL6: u8 = eightBitLoadFromGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL));\n addARegister(valueAtHL6);\n return 8;\n case 0x87:\n // ADD A,A\n // 1 4\n // Z 0 H C\n addARegister(Cpu.registerA);\n return 4;\n case 0x88:\n // ADC A,B\n // 1 4\n // Z 0 H C\n addAThroughCarryRegister(Cpu.registerB);\n return 4;\n case 0x89:\n // ADC A,C\n // 1 4\n // Z 0 H C\n addAThroughCarryRegister(Cpu.registerC);\n return 4;\n case 0x8a:\n // ADC A,D\n // 1 4\n // Z 0 H C\n addAThroughCarryRegister(Cpu.registerD);\n return 4;\n case 0x8b:\n // ADC A,E\n // 1 4\n // Z 0 H C\n addAThroughCarryRegister(Cpu.registerE);\n return 4;\n case 0x8c:\n // ADC A,H\n // 1 4\n // Z 0 H C\n addAThroughCarryRegister(Cpu.registerH);\n return 4;\n case 0x8d:\n // ADC A,L\n // 1 4\n // Z 0 H C\n addAThroughCarryRegister(Cpu.registerL);\n return 4;\n case 0x8e:\n // ADC A,(HL)\n // 1 8\n // Z 0 H C\n let valueAtHLE: u8 = eightBitLoadFromGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL));\n addAThroughCarryRegister(valueAtHLE);\n return 8;\n case 0x8f:\n // ADC A,A\n // 1 4\n // Z 0 H C\n addAThroughCarryRegister(Cpu.registerA);\n return 4;\n }\n return -1;\n}\n\nfunction handleOpcode9x(opcode: i32): i32 {\n switch (opcode) {\n case 0x90:\n // SUB B\n // 1 4\n // Z 1 H C\n subARegister(Cpu.registerB);\n return 4;\n case 0x91:\n // SUB C\n // 1 4\n // Z 1 H C\n subARegister(Cpu.registerC);\n return 4;\n case 0x92:\n // SUB D\n // 1 4\n // Z 1 H C\n subARegister(Cpu.registerD);\n return 4;\n case 0x93:\n // SUB E\n // 1 4\n // Z 1 H C\n subARegister(Cpu.registerE);\n return 4;\n case 0x94:\n // SUB H\n // 1 4\n // Z 1 H C\n subARegister(Cpu.registerH);\n return 4;\n case 0x95:\n // SUB L\n // 1 4\n // Z 1 H C\n subARegister(Cpu.registerL);\n return 4;\n case 0x96:\n // SUB (HL)\n // 1 8\n // Z 1 H C\n let valueAtHL6: u8 = eightBitLoadFromGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL));\n subARegister(valueAtHL6);\n return 8;\n case 0x97:\n // SUB A\n // 1 4\n // Z 1 H C\n subARegister(Cpu.registerA);\n return 4;\n case 0x98:\n // SBC A,B\n // 1 4\n // Z 1 H C\n subAThroughCarryRegister(Cpu.registerB);\n return 4;\n case 0x99:\n // SBC A,C\n // 1 4\n // Z 1 H C\n subAThroughCarryRegister(Cpu.registerC);\n return 4;\n case 0x9a:\n // SBC A,D\n // 1 4\n // Z 1 H C\n subAThroughCarryRegister(Cpu.registerD);\n return 4;\n case 0x9b:\n // SBC A,E\n // 1 4\n // Z 1 H C\n subAThroughCarryRegister(Cpu.registerE);\n return 4;\n case 0x9c:\n // SBC A,H\n // 1 4\n // Z 1 H C\n subAThroughCarryRegister(Cpu.registerH);\n return 4;\n case 0x9d:\n // SBC A,L\n // 1 4\n // Z 1 H C\n subAThroughCarryRegister(Cpu.registerL);\n return 4;\n case 0x9e:\n // SBC A,(HL)\n // 1 8\n // Z 1 H C\n let valueAtHLE: u8 = eightBitLoadFromGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL));\n subAThroughCarryRegister(valueAtHLE);\n return 8;\n case 0x9f:\n // SBC A,A\n // 1 4\n // Z 1 H C\n subAThroughCarryRegister(Cpu.registerA);\n return 4;\n }\n return -1;\n}\n\nfunction handleOpcodeAx(opcode: i32): i32 {\n switch (opcode) {\n case 0xa0:\n // AND B\n // 1 4\n // Z 0 1 0\n andARegister(Cpu.registerB);\n return 4;\n case 0xa1:\n // AND C\n // 1 4\n // Z 0 1 0\n andARegister(Cpu.registerC);\n return 4;\n case 0xa2:\n // AND D\n // 1 4\n // Z 0 1 0\n andARegister(Cpu.registerD);\n return 4;\n case 0xa3:\n // AND E\n // 1 4\n // Z 0 1 0\n andARegister(Cpu.registerE);\n return 4;\n case 0xa4:\n // AND H\n // 1 4\n // Z 0 1 0\n andARegister(Cpu.registerH);\n return 4;\n case 0xa5:\n // AND L\n // 1 4\n // Z 0 1 0\n andARegister(Cpu.registerL);\n return 4;\n case 0xa6:\n // AND (HL)\n // 1 8\n // Z 0 1 0\n let valueAtHL6: u8 = eightBitLoadFromGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL));\n andARegister(valueAtHL6);\n return 8;\n case 0xa7:\n // AND A\n // 1 4\n // Z 0 1 0\n // NOTE: & Yourself, does nothing\n andARegister(Cpu.registerA);\n return 4;\n case 0xa8:\n // XOR B\n // 1 4\n // Z 0 0 0\n xorARegister(Cpu.registerB);\n return 4;\n case 0xa9:\n // XOR C\n // 1 4\n // Z 0 0 0\n xorARegister(Cpu.registerC);\n return 4;\n case 0xaa:\n // XOR D\n // 1 4\n // Z 0 0 0\n xorARegister(Cpu.registerD);\n return 4;\n case 0xab:\n // XOR E\n // 1 4\n // Z 0 0 0\n xorARegister(Cpu.registerE);\n return 4;\n case 0xac:\n // XOR H\n // 1 4\n // Z 0 0 0\n xorARegister(Cpu.registerH);\n return 4;\n case 0xad:\n // XOR L\n // 1 4\n // Z 0 0 0\n xorARegister(Cpu.registerL);\n return 4;\n case 0xae:\n // XOR (HL)\n // 1 8\n // Z 0 0 0\n let valueAtHLE: u8 = eightBitLoadFromGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL));\n xorARegister(valueAtHLE);\n return 8;\n case 0xaf:\n // XOR A\n // 1 4\n // Z 0 0 0\n xorARegister(Cpu.registerA);\n return 4;\n }\n return -1;\n}\n\nfunction handleOpcodeBx(opcode: i32): i32 {\n switch (opcode) {\n case 0xb0:\n // OR B\n // 1 4\n // Z 0 0 0\n orARegister(Cpu.registerB);\n return 4;\n case 0xb1:\n // OR C\n // 1 4\n // Z 0 0 0\n orARegister(Cpu.registerC);\n return 4;\n case 0xb2:\n // OR D\n // 1 4\n // Z 0 0 0\n orARegister(Cpu.registerD);\n return 4;\n case 0xb3:\n // OR E\n // 1 4\n // Z 0 0 0\n orARegister(Cpu.registerE);\n return 4;\n case 0xb4:\n // OR H\n // 1 4\n // Z 0 0 0\n orARegister(Cpu.registerH);\n return 4;\n case 0xb5:\n // OR L\n // 1 4\n // Z 0 0 0\n orARegister(Cpu.registerL);\n return 4;\n case 0xb6:\n // OR (HL)\n // 1 8\n // Z 0 0 0\n let valueAtHL6: u8 = eightBitLoadFromGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL));\n orARegister(valueAtHL6);\n return 8;\n case 0xb7:\n // OR A\n // 1 4\n // Z 0 0 0\n orARegister(Cpu.registerA);\n return 4;\n case 0xb8:\n // CP B\n // 1 4\n // Z 1 H C\n cpARegister(Cpu.registerB);\n return 4;\n case 0xb9:\n // CP C\n // 1 4\n // Z 1 H C\n cpARegister(Cpu.registerC);\n return 4;\n case 0xba:\n // CP D\n // 1 4\n // Z 1 H C\n cpARegister(Cpu.registerD);\n return 4;\n case 0xbb:\n // CP E\n // 1 4\n // Z 1 H C\n cpARegister(Cpu.registerE);\n return 4;\n case 0xbc:\n // CP H\n // 1 4\n // Z 1 H C\n cpARegister(Cpu.registerH);\n return 4;\n case 0xbd:\n // CP L\n // 1 4\n // Z 1 H C\n cpARegister(Cpu.registerL);\n return 4;\n case 0xbe:\n // CP (HL)\n // 1 8\n // Z 1 H C\n let valueAtHLE: u8 = eightBitLoadFromGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL));\n cpARegister(valueAtHLE);\n return 8;\n case 0xbf:\n // CP A\n // 1 4\n // Z 1 H C\n cpARegister(Cpu.registerA);\n return 4;\n }\n return -1;\n}\n\nfunction handleOpcodeCx(opcode: i32): i32 {\n switch (opcode) {\n case 0xc0:\n // RET NZ\n // 1 20/8\n if (getZeroFlag() === 0) {\n Cpu.programCounter = sixteenBitLoadFromGBMemory(Cpu.stackPointer);\n Cpu.stackPointer = u16Portable(Cpu.stackPointer + 2);\n return 20;\n } else {\n return 8;\n }\n case 0xc1:\n // POP BC\n // 1 12\n let registerBC1: i32 = sixteenBitLoadFromGBMemory(Cpu.stackPointer);\n Cpu.stackPointer = u16Portable(Cpu.stackPointer + 2);\n Cpu.registerB = splitHighByte(registerBC1);\n Cpu.registerC = splitLowByte(registerBC1);\n return 12;\n case 0xc2:\n // JP NZ,a16\n // 3 16/12\n if (getZeroFlag() === 0) {\n Cpu.programCounter = getConcatenatedDataByte();\n return 16;\n } else {\n Cpu.programCounter = u16Portable(Cpu.programCounter + 2);\n return 12;\n }\n case 0xc3:\n // JP a16\n // 3 16\n Cpu.programCounter = getConcatenatedDataByte();\n return 16;\n case 0xc4:\n // CALL NZ,a16\n // 3 24/12\n if (getZeroFlag() === 0) {\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 2);\n sixteenBitStoreIntoGBMemoryWithTraps(Cpu.stackPointer, u16Portable(Cpu.programCounter + 2));\n Cpu.programCounter = getConcatenatedDataByte();\n return 24;\n } else {\n Cpu.programCounter = u16Portable(Cpu.programCounter + 2);\n return 12;\n }\n case 0xc5:\n // PUSH BC\n // 1 16\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 2);\n sixteenBitStoreIntoGBMemoryWithTraps(Cpu.stackPointer, concatenateBytes(Cpu.registerB, Cpu.registerC));\n return 16;\n case 0xc6:\n // ADD A,d8\n // 2 8\n // Z 0 H C\n addARegister(getDataByteOne());\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n case 0xc7:\n // RST 00H\n // 1 16\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 2);\n sixteenBitStoreIntoGBMemoryWithTraps(Cpu.stackPointer, Cpu.programCounter);\n Cpu.programCounter = 0x00;\n return 16;\n case 0xc8:\n // RET Z\n // 1 20/8\n if (getZeroFlag() === 1) {\n Cpu.programCounter = sixteenBitLoadFromGBMemory(Cpu.stackPointer);\n Cpu.stackPointer = u16Portable(Cpu.stackPointer + 2);\n return 20;\n } else {\n return 8;\n }\n case 0xc9:\n // RET\n // 1 16\n Cpu.programCounter = sixteenBitLoadFromGBMemory(Cpu.stackPointer);\n Cpu.stackPointer = u16Portable(Cpu.stackPointer + 2);\n return 16;\n case 0xca:\n // JP Z,a16\n // 3 16/12\n if (getZeroFlag() === 1) {\n Cpu.programCounter = getConcatenatedDataByte();\n return 16;\n } else {\n Cpu.programCounter = u16Portable(Cpu.programCounter + 2);\n return 12;\n }\n case 0xcb:\n // PREFIX CB\n // 1 4\n let cbCycles: i32 = handleCbOpcode(getDataByteOne());\n if (cbCycles > 0) {\n cbCycles += 4;\n }\n return cbCycles;\n case 0xcc:\n // CALL Z,a16\n // 3 24/12\n if (getZeroFlag() === 1) {\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 2);\n sixteenBitStoreIntoGBMemoryWithTraps(Cpu.stackPointer, Cpu.programCounter + 2);\n Cpu.programCounter = getConcatenatedDataByte();\n return 24;\n } else {\n Cpu.programCounter = u16Portable(Cpu.programCounter + 2);\n return 12;\n }\n case 0xcd:\n // CALL a16\n // 3 24\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 2);\n sixteenBitStoreIntoGBMemoryWithTraps(Cpu.stackPointer, u16Portable(Cpu.programCounter + 2));\n Cpu.programCounter = getConcatenatedDataByte();\n return 24;\n case 0xce:\n // ADC A,d8\n // 2 8\n // Z 0 H C\n addAThroughCarryRegister(getDataByteOne());\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n case 0xcf:\n // RST 08H\n // 1 16\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 2);\n sixteenBitStoreIntoGBMemoryWithTraps(Cpu.stackPointer, Cpu.programCounter);\n Cpu.programCounter = 0x08;\n return 16;\n }\n return -1;\n}\n\nfunction handleOpcodeDx(opcode: i32): i32 {\n switch (opcode) {\n case 0xd0:\n // RET NC\n // 1 20/8\n if (getCarryFlag() === 0) {\n Cpu.programCounter = sixteenBitLoadFromGBMemory(Cpu.stackPointer);\n Cpu.stackPointer = u16Portable(Cpu.stackPointer + 2);\n return 20;\n } else {\n return 8;\n }\n case 0xd1:\n // POP DE\n // 1 12\n let registerDE1: i32 = sixteenBitLoadFromGBMemory(Cpu.stackPointer);\n Cpu.stackPointer = u16Portable(Cpu.stackPointer + 2);\n Cpu.registerD = splitHighByte(registerDE1);\n Cpu.registerE = splitLowByte(registerDE1);\n return 12;\n case 0xd2:\n // JP NC,a16\n // 3 16/12\n if (getCarryFlag() === 0) {\n Cpu.programCounter = getConcatenatedDataByte();\n return 16;\n } else {\n Cpu.programCounter = u16Portable(Cpu.programCounter + 2);\n return 12;\n }\n /* No Opcode for: 0xD3 */\n case 0xd4:\n // CALL NC,a16\n // 3 24/12\n if (getCarryFlag() === 0) {\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 2);\n sixteenBitStoreIntoGBMemoryWithTraps(Cpu.stackPointer, Cpu.programCounter + 2);\n Cpu.programCounter = getConcatenatedDataByte();\n return 24;\n } else {\n Cpu.programCounter = u16Portable(Cpu.programCounter + 2);\n return 12;\n }\n case 0xd5:\n // PUSH DE\n // 1 16\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 2);\n sixteenBitStoreIntoGBMemoryWithTraps(Cpu.stackPointer, concatenateBytes(Cpu.registerD, Cpu.registerE));\n return 16;\n case 0xd6:\n // SUB d8\n // 2 8\n // Z 1 H C\n subARegister(getDataByteOne());\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n case 0xd7:\n // RST 10H\n // 1 16\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 2);\n sixteenBitStoreIntoGBMemoryWithTraps(Cpu.stackPointer, Cpu.programCounter);\n Cpu.programCounter = 0x10;\n return 16;\n case 0xd8:\n // RET C\n // 1 20/8\n if (getCarryFlag() === 1) {\n Cpu.programCounter = sixteenBitLoadFromGBMemory(Cpu.stackPointer);\n Cpu.stackPointer = u16Portable(Cpu.stackPointer + 2);\n return 20;\n } else {\n return 8;\n }\n case 0xd9:\n // RETI\n // 1 16\n Cpu.programCounter = sixteenBitLoadFromGBMemory(Cpu.stackPointer);\n // Enable interrupts\n setInterrupts(true);\n Cpu.stackPointer = u16Portable(Cpu.stackPointer + 2);\n return 16;\n case 0xda:\n // JP C,a16\n // 3 16/12\n if (getCarryFlag() === 1) {\n Cpu.programCounter = getConcatenatedDataByte();\n return 16;\n } else {\n Cpu.programCounter = u16Portable(Cpu.programCounter + 2);\n return 12;\n }\n /* No Opcode for: 0xDB */\n case 0xdc:\n // CALL C,a16\n // 3 24/12\n if (getCarryFlag() === 1) {\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 2);\n sixteenBitStoreIntoGBMemoryWithTraps(Cpu.stackPointer, u16Portable(Cpu.programCounter + 2));\n Cpu.programCounter = getConcatenatedDataByte();\n return 24;\n } else {\n Cpu.programCounter = u16Portable(Cpu.programCounter + 2);\n return 12;\n }\n /* No Opcode for: 0xDD */\n case 0xde:\n // SBC A,d8\n // 2 8\n // Z 1 H C\n subAThroughCarryRegister(getDataByteOne());\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n case 0xdf:\n // RST 18H\n // 1 16\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 2);\n sixteenBitStoreIntoGBMemoryWithTraps(Cpu.stackPointer, Cpu.programCounter);\n Cpu.programCounter = 0x18;\n return 16;\n }\n return -1;\n}\n\nfunction handleOpcodeEx(opcode: i32): i32 {\n switch (opcode) {\n case 0xe0:\n // LDH (a8),A\n // 2 12\n\n // Store value in high RAM ($FF00 + a8)\n let largeDataByteOne: i32 = getDataByteOne();\n eightBitStoreIntoGBMemoryWithTraps(0xff00 + largeDataByteOne, Cpu.registerA);\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 12;\n case 0xe1:\n // POP HL\n // 1 12\n let registerHL1: i32 = sixteenBitLoadFromGBMemory(Cpu.stackPointer);\n Cpu.stackPointer = u16Portable(Cpu.stackPointer + 2);\n Cpu.registerH = splitHighByte(registerHL1);\n Cpu.registerL = splitLowByte(registerHL1);\n return 12;\n case 0xe2:\n // LD (C),A\n // 1 8\n // NOTE: Table says 2 Program counter,\n // But stepping through the boot rom, should be one\n // Also should change 0xF2\n\n // Store value in high RAM ($FF00 + register c)\n eightBitStoreIntoGBMemoryWithTraps(0xff00 + Cpu.registerC, Cpu.registerA);\n return 8;\n /* No Opcode for: 0xE3, 0xE4 */\n case 0xe5:\n // PUSH HL\n // 1 16\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 2);\n sixteenBitStoreIntoGBMemoryWithTraps(Cpu.stackPointer, concatenateBytes(Cpu.registerH, Cpu.registerL));\n return 16;\n case 0xe6:\n // AND d8\n // 2 8\n // Z 0 1 0\n andARegister(getDataByteOne());\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n case 0xe7:\n // RST 20H\n // 1 16\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 2);\n sixteenBitStoreIntoGBMemoryWithTraps(Cpu.stackPointer, Cpu.programCounter);\n Cpu.programCounter = 0x20;\n return 16;\n case 0xe8:\n // ADD SP, r8\n // 2 16\n // 0 0 H C\n // NOTE: Discoved dataByte is signed\n let signedDataByteOne: i8 = i8Portable(getDataByteOne());\n\n checkAndSetSixteenBitFlagsAddOverflow(Cpu.stackPointer, signedDataByteOne, true);\n Cpu.stackPointer = u16Portable(Cpu.stackPointer + signedDataByteOne);\n setZeroFlag(0);\n setSubtractFlag(0);\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 16;\n case 0xe9:\n // JP HL\n // 1 4\n Cpu.programCounter = concatenateBytes(Cpu.registerH, Cpu.registerL);\n return 4;\n case 0xea:\n // LD (a16),A\n // 3 16\n eightBitStoreIntoGBMemoryWithTraps(getConcatenatedDataByte(), Cpu.registerA);\n Cpu.programCounter = u16Portable(Cpu.programCounter + 2);\n return 16;\n /* No Opcode for: 0xEB, 0xEC, 0xED */\n case 0xee:\n // XOR d8\n // 2 8\n // Z 0 0 0\n xorARegister(getDataByteOne());\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n case 0xef:\n // RST 28H\n // 1 16\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 2);\n sixteenBitStoreIntoGBMemoryWithTraps(Cpu.stackPointer, Cpu.programCounter);\n Cpu.programCounter = 0x28;\n return 16;\n }\n return -1;\n}\n\nfunction handleOpcodeFx(opcode: i32): i32 {\n switch (opcode) {\n case 0xf0:\n // LDH A,(a8)\n // 2 12\n let largeDataByteOne: i32 = getDataByteOne();\n Cpu.registerA = u8Portable(eightBitLoadFromGBMemoryWithTraps(0xff00 + largeDataByteOne));\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 12;\n case 0xf1:\n // POP AF\n // 1 12\n // Z N H C (But No work require, flags are already set)\n let registerAF1: i32 = sixteenBitLoadFromGBMemory(Cpu.stackPointer);\n Cpu.stackPointer = u16Portable(Cpu.stackPointer + 2);\n Cpu.registerA = splitHighByte(registerAF1);\n Cpu.registerF = splitLowByte(registerAF1);\n return 12;\n case 0xf2:\n // LD A,(C)\n // 1 8\n Cpu.registerA = u8Portable(eightBitLoadFromGBMemoryWithTraps(0xff00 + Cpu.registerC));\n return 8;\n case 0xf3:\n // DI\n // 1 4\n setInterrupts(false);\n return 4;\n /* No Opcode for: 0xF4 */\n case 0xf5:\n // PUSH AF\n // 1 16\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 2);\n sixteenBitStoreIntoGBMemoryWithTraps(Cpu.stackPointer, concatenateBytes(Cpu.registerA, Cpu.registerF));\n return 16;\n case 0xf6:\n // OR d8\n // 2 8\n // Z 0 0 0\n orARegister(getDataByteOne());\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n case 0xf7:\n // RST 30H\n // 1 16\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 2);\n sixteenBitStoreIntoGBMemoryWithTraps(Cpu.stackPointer, Cpu.programCounter);\n Cpu.programCounter = 0x30;\n return 16;\n case 0xf8:\n // LD HL,SP+r8\n // 2 12\n // 0 0 H C\n // NOTE: Discoved dataByte is signed\n let signedDataByteOne: i8 = i8Portable(getDataByteOne());\n\n // First, let's handle flags\n setZeroFlag(0);\n setSubtractFlag(0);\n checkAndSetSixteenBitFlagsAddOverflow(Cpu.stackPointer, signedDataByteOne, true);\n let registerHL = u16Portable(Cpu.stackPointer + signedDataByteOne);\n Cpu.registerH = splitHighByte(registerHL);\n Cpu.registerL = splitLowByte(registerHL);\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 12;\n case 0xf9:\n // LD SP,HL\n // 1 8\n Cpu.stackPointer = concatenateBytes(Cpu.registerH, Cpu.registerL);\n return 8;\n case 0xfa:\n // LD A,(a16)\n // 3 16\n Cpu.registerA = eightBitLoadFromGBMemoryWithTraps(getConcatenatedDataByte());\n Cpu.programCounter = u16Portable(Cpu.programCounter + 2);\n return 16;\n case 0xfb:\n // EI\n // 1 4\n setInterrupts(true);\n return 4;\n /* No Opcode for: 0xFC, 0xFD */\n case 0xfe:\n // CP d8\n // 2 8\n // Z 1 H C\n cpARegister(getDataByteOne());\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n return 8;\n case 0xff:\n // RST 38H\n // 1 16\n Cpu.stackPointer = u16Portable(Cpu.stackPointer - 2);\n sixteenBitStoreIntoGBMemoryWithTraps(Cpu.stackPointer, Cpu.programCounter);\n Cpu.programCounter = 0x38;\n return 16;\n }\n return -1;\n}\n","// Portable Code for JS Wasm Benchmarking\n// https://github.com/AssemblyScript/assemblyscript/wiki/Writing-portable-code\n// https://github.com/AssemblyScript/assemblyscript/blob/master/std/portable/index.js\n\nimport { checkBitOnByte, resetBitOnByte, setBitOnByte } from '../helpers/index';\n\nexport function u8Portable(param: u8): u8 {\n return param & 0xff;\n}\n\nexport function u16Portable(param: u16): u16 {\n return param & 0xffff;\n}\n\nexport function i8Portable(param: i8): i8 {\n // JS ints are all i32, therefore, get the sign bit, and then convert accordingly\n // Example: https://blog.michaelyin.info/convert-8bit-byte-to-signed-int/\n let response: i32 = param;\n if (checkBitOnByte(7, response)) {\n response = (256 - param) * -1;\n }\n\n return response;\n}\n\nexport function i32Portable(param: i32): i32 {\n return param | 0;\n}\n","import { Memory } from './memory';\nimport { Graphics, batchProcessGraphics } from '../graphics/graphics';\nimport { Palette, writeColorPaletteToMemory, Lcd } from '../graphics/index';\nimport { batchProcessAudio, SoundRegisterWriteTraps } from '../sound/index';\nimport { Timers, batchProcessTimers } from '../timers/index';\nimport { Interrupts } from '../interrupts/index';\nimport { Joypad } from '../joypad/index';\nimport { handleBanking } from './banking';\nimport { eightBitStoreIntoGBMemory, sixteenBitStoreIntoGBMemory } from './store';\nimport { eightBitLoadFromGBMemoryWithTraps, eightBitLoadFromGBMemory, sixteenBitLoadFromGBMemory } from './load';\nimport { startDmaTransfer, startHdmaTransfer } from './dma';\nimport { checkBitOnByte, hexLog } from '../helpers/index';\n\n// Internal function to trap any modify data trying to be written to Gameboy memory\n// Follows the Gameboy memory map\nexport function checkWriteTraps(offset: i32, value: i32): boolean {\n // Cache globals used multiple times for performance\n let videoRamLocation: i32 = Memory.videoRamLocation;\n let spriteInformationTableLocation: i32 = Memory.spriteInformationTableLocation;\n\n // Handle banking\n if (offset < videoRamLocation) {\n handleBanking(offset, value);\n return false;\n }\n\n // Check the graphics mode to see if we can write to VRAM\n // http://gbdev.gg8.se/wiki/articles/Video_Display#Accessing_VRAM_and_OAM\n if (offset >= videoRamLocation && offset < Memory.cartridgeRamLocation) {\n // Can only read/write from VRAM During Modes 0 - 2\n // See graphics/lcd.ts\n // TODO: This can do more harm than good in a beta emulator,\n // requires precise timing disabling for now\n // if (Graphics.currentLcdMode > 2) {\n // return false;\n // }\n\n // Not batch processing here for performance\n // batchProcessGraphics();\n\n // Allow the original write, and return since we dont need to look anymore\n return true;\n }\n\n // Be sure to copy everything in EchoRam to Work Ram\n // Codeslinger: The ECHO memory region (0xE000-0xFDFF) is quite different because any data written here is also written in the equivelent ram memory region 0xC000-0xDDFF.\n // Hence why it is called echo\n if (offset >= Memory.echoRamLocation && offset < spriteInformationTableLocation) {\n let wramOffset: i32 = offset - 0x2000;\n eightBitStoreIntoGBMemory(wramOffset, value);\n\n // Allow the original write, and return since we dont need to look anymore\n return true;\n }\n\n // Also check for individal writes\n // Can only read/write from OAM During Modes 0 - 1\n // See graphics/lcd.ts\n if (offset >= spriteInformationTableLocation && offset <= Memory.spriteInformationTableLocationEnd) {\n // Can only read/write from OAM During Mode 2\n // See graphics/lcd.ts\n if (Lcd.currentLcdMode < 2) {\n return false;\n }\n // Not batch processing here for performance\n // batchProcessGraphics();\n\n // Allow the original write, and return since we dont need to look anymore\n return true;\n }\n\n if (offset >= Memory.unusableMemoryLocation && offset <= Memory.unusableMemoryEndLocation) {\n return false;\n }\n\n // Sound\n // http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Registers\n if (offset >= 0xff10 && offset <= 0xff26) {\n batchProcessAudio();\n return SoundRegisterWriteTraps(offset, value);\n }\n\n // FF27 - FF2F not used\n // Final Wave Table for Channel 3\n if (offset >= 0xff30 && offset <= 0xff3f) {\n batchProcessAudio();\n }\n\n // Other Memory effects fomr read/write to Lcd/Graphics\n if (offset >= Lcd.memoryLocationLcdControl && offset <= Graphics.memoryLocationWindowX) {\n // Not batch processing here for performance\n // batchProcessGraphics();\n\n if (offset === Lcd.memoryLocationLcdControl) {\n // Shorcut for isLCD Enabled since it gets \"hot\"\n Lcd.updateLcdControl(value);\n return true;\n }\n\n // reset the current scanline if the game tries to write to it\n if (offset === Graphics.memoryLocationScanlineRegister) {\n Graphics.scanlineRegister = 0;\n eightBitStoreIntoGBMemory(offset, 0);\n return false;\n }\n\n // Cache our coincidence compare\n if (offset === Lcd.memoryLocationCoincidenceCompare) {\n Lcd.coincidenceCompare = value;\n return true;\n }\n\n // Do the direct memory access transfer for spriteInformationTable\n // Check the graphics mode to see if we can write to VRAM\n // http://gbdev.gg8.se/wiki/articles/Video_Display#Accessing_VRAM_and_OAM\n if (offset === Graphics.memoryLocationDmaTransfer) {\n // otherwise, perform a DMA transfer\n // And allow the original write\n startDmaTransfer(value);\n return true;\n }\n\n // Scroll and Window XY\n switch (offset) {\n case Graphics.memoryLocationScrollX:\n Graphics.scrollX = value;\n return true;\n case Graphics.memoryLocationScrollY:\n Graphics.scrollY = value;\n return true;\n case Graphics.memoryLocationWindowX:\n Graphics.windowX = value;\n return true;\n case Graphics.memoryLocationWindowY:\n Graphics.windowY = value;\n return true;\n }\n\n // Allow the original write, and return since we dont need to look anymore\n return true;\n }\n\n // Do an HDMA\n if (offset === Memory.memoryLocationHdmaTrigger) {\n startHdmaTransfer(value);\n return false;\n }\n\n // Don't allow banking if we are doing an Hblank HDM transfer\n // https://gist.github.com/drhelius/3394856\n if (offset === Memory.memoryLocationGBCWRAMBank || offset === Memory.memoryLocationGBCVRAMBank) {\n if (Memory.isHblankHdmaActive) {\n if (\n (Memory.hblankHdmaSource >= 0x4000 && Memory.hblankHdmaSource <= 0x7fff) ||\n (Memory.hblankHdmaSource >= 0xd000 && Memory.hblankHdmaSource <= 0xdfff)\n ) {\n return false;\n }\n }\n }\n\n // Handle GBC Pallete Write\n if (offset >= Palette.memoryLocationBackgroundPaletteIndex && offset <= Palette.memoryLocationSpritePaletteData) {\n // Incremeenting the palette handled by the write\n writeColorPaletteToMemory(offset, value);\n return true;\n }\n\n // Handle timer writes\n if (offset >= Timers.memoryLocationDividerRegister && offset <= Timers.memoryLocationTimerControl) {\n // Batch Process\n batchProcessTimers();\n\n switch (offset) {\n case Timers.memoryLocationDividerRegister:\n Timers.updateDividerRegister(value);\n return false;\n case Timers.memoryLocationTimerCounter:\n Timers.updateTimerCounter(value);\n return true;\n case Timers.memoryLocationTimerModulo:\n Timers.updateTimerModulo(value);\n return true;\n case Timers.memoryLocationTimerControl:\n Timers.updateTimerControl(value);\n return true;\n }\n\n return true;\n }\n\n // Handle Joypad writes for HW reg caching\n if (offset === Joypad.memoryLocationJoypadRegister) {\n Joypad.updateJoypad(value);\n }\n\n // Handle Interrupt writes\n if (offset === Interrupts.memoryLocationInterruptRequest) {\n Interrupts.updateInterruptRequested(value);\n return true;\n }\n if (offset === Interrupts.memoryLocationInterruptEnabled) {\n Interrupts.updateInterruptEnabled(value);\n return true;\n }\n\n // Allow the original write\n return true;\n}\n","// Funcitons for setting and checking the LCD\nimport { Graphics } from './graphics';\n// Assembly script really not feeling the reexport\nimport { eightBitLoadFromGBMemory } from '../memory/load';\nimport { eightBitStoreIntoGBMemory } from '../memory/store';\nimport { updateHblankHdma } from '../memory/index';\nimport { requestLcdInterrupt, requestVBlankInterrupt } from '../interrupts/index';\nimport { checkBitOnByte, setBitOnByte, resetBitOnByte, hexLog } from '../helpers/index';\n\nexport class Lcd {\n // Memory Locations\n // Also known at STAT\n // LCD Status (0xFF41) bits Explanation\n // 0 0 000 0 00\n // |Coicedence Interrupt| |Mode Interrupts| |coincidence flag| | Mode |\n // Modes:\n // 0 or 00: H-Blank\n // 1 or 01: V-Blank\n // 2 or 10: Searching Sprites Atts\n // 3 or 11: Transfering Data to LCD Driver\n static readonly memoryLocationLcdStatus: i32 = 0xff41;\n static currentLcdMode: i32 = 0;\n\n static readonly memoryLocationCoincidenceCompare: i32 = 0xff45;\n static coincidenceCompare: i32 = 0;\n\n // Also known as LCDC\n // http://www.codeslinger.co.uk/pages/projects/gameboy/graphics.html\n // Bit 7 - LCD Display Enable (0=Off, 1=On)\n // Bit 6 - Window Tile Map Display Select (0=9800-9BFF, 1=9C00-9FFF)\n // Bit 5 - Window Display Enable (0=Off, 1=On)\n // Bit 4 - BG & Window Tile Data Select (0=8800-97FF, 1=8000-8FFF)\n // Bit 3 - BG Tile Map Display Select (0=9800-9BFF, 1=9C00-9FFF)\n // Bit 2 - OBJ (Sprite) Size (0=8x8, 1=8x16)\n // Bit 1 - OBJ (Sprite) Display Enable (0=Off, 1=On)\n // Bit 0 - BG Display (for CGB see below) (0=Off, 1=On\n static readonly memoryLocationLcdControl: i32 = 0xff40;\n // Decoupled LCDC for caching\n static enabled: boolean = true;\n static windowTileMapDisplaySelect: boolean = false;\n static windowDisplayEnabled: boolean = false;\n static bgWindowTileDataSelect: boolean = false;\n static bgTileMapDisplaySelect: boolean = false;\n static tallSpriteSize: boolean = false;\n static spriteDisplayEnable: boolean = false;\n static bgDisplayEnabled: boolean = false;\n\n // Functions called in write traps to update our hardware registers\n static updateLcdControl(value: i32): void {\n Lcd.enabled = checkBitOnByte(7, value);\n Lcd.windowTileMapDisplaySelect = checkBitOnByte(6, value);\n Lcd.windowDisplayEnabled = checkBitOnByte(5, value);\n Lcd.bgWindowTileDataSelect = checkBitOnByte(4, value);\n Lcd.bgTileMapDisplaySelect = checkBitOnByte(3, value);\n Lcd.tallSpriteSize = checkBitOnByte(2, value);\n Lcd.spriteDisplayEnable = checkBitOnByte(1, value);\n Lcd.bgDisplayEnabled = checkBitOnByte(0, value);\n }\n}\n\n// Pass in the lcd status for performance\nexport function setLcdStatus(): void {\n // Check if the Lcd was disabled\n if (!Lcd.enabled) {\n // Reset scanline cycle counter\n Graphics.scanlineCycleCounter = 0;\n Graphics.scanlineRegister = 0;\n eightBitStoreIntoGBMemory(Graphics.memoryLocationScanlineRegister, 0);\n\n // Set to mode 0\n // https://www.reddit.com/r/EmuDev/comments/4w6479/gb_dr_mario_level_generation_issues/\n let lcdStatus: i32 = eightBitLoadFromGBMemory(Lcd.memoryLocationLcdStatus);\n lcdStatus = resetBitOnByte(1, lcdStatus);\n lcdStatus = resetBitOnByte(0, lcdStatus);\n Lcd.currentLcdMode = 0;\n\n // Store the status in memory\n eightBitStoreIntoGBMemory(Lcd.memoryLocationLcdStatus, lcdStatus);\n return;\n }\n\n // Get our current scanline, and lcd mode\n let scanlineRegister: i32 = Graphics.scanlineRegister;\n let lcdMode: i32 = Lcd.currentLcdMode;\n\n // Default to H-Blank\n let newLcdMode: i32 = 0;\n\n // Find our newLcd mode\n if (scanlineRegister >= 144) {\n // VBlank mode\n newLcdMode = 1;\n } else {\n if (Graphics.scanlineCycleCounter >= Graphics.MIN_CYCLES_SPRITES_LCD_MODE()) {\n // Searching Sprites Atts\n newLcdMode = 2;\n } else if (Graphics.scanlineCycleCounter >= Graphics.MIN_CYCLES_TRANSFER_DATA_LCD_MODE()) {\n // Transferring data to lcd\n newLcdMode = 3;\n }\n }\n\n if (lcdMode !== newLcdMode) {\n // Get our lcd status\n let lcdStatus: i32 = eightBitLoadFromGBMemory(Lcd.memoryLocationLcdStatus);\n\n // Save our lcd mode\n Lcd.currentLcdMode = newLcdMode;\n\n let shouldRequestInterrupt: boolean = false;\n\n // Set our LCD Statuc accordingly\n switch (newLcdMode) {\n case 0x00:\n lcdStatus = resetBitOnByte(0, lcdStatus);\n lcdStatus = resetBitOnByte(1, lcdStatus);\n shouldRequestInterrupt = checkBitOnByte(3, lcdStatus);\n break;\n case 0x01:\n lcdStatus = resetBitOnByte(1, lcdStatus);\n lcdStatus = setBitOnByte(0, lcdStatus);\n shouldRequestInterrupt = checkBitOnByte(4, lcdStatus);\n break;\n case 0x02:\n lcdStatus = resetBitOnByte(0, lcdStatus);\n lcdStatus = setBitOnByte(1, lcdStatus);\n shouldRequestInterrupt = checkBitOnByte(5, lcdStatus);\n break;\n case 0x03:\n lcdStatus = setBitOnByte(0, lcdStatus);\n lcdStatus = setBitOnByte(1, lcdStatus);\n break;\n }\n\n // Check if we want to request an interrupt, and we JUST changed modes\n if (shouldRequestInterrupt) {\n requestLcdInterrupt();\n }\n\n // Check for updating the Hblank HDMA\n if (newLcdMode === 0) {\n // Update the Hblank DMA, will simply return if not active\n updateHblankHdma();\n }\n\n // Check for requesting a VBLANK interrupt\n if (newLcdMode === 1) {\n requestVBlankInterrupt();\n }\n\n // Check for the coincidence flag\n // Need to check on every mode, and not just HBLANK, as checking on hblank breaks shantae, which checks on vblank\n let coincidenceCompare: i32 = Lcd.coincidenceCompare;\n if ((newLcdMode === 0 || newLcdMode === 1) && scanlineRegister === coincidenceCompare) {\n lcdStatus = setBitOnByte(2, lcdStatus);\n if (checkBitOnByte(6, lcdStatus)) {\n requestLcdInterrupt();\n }\n } else {\n lcdStatus = resetBitOnByte(2, lcdStatus);\n }\n\n // Finally, save our status\n eightBitStoreIntoGBMemory(Lcd.memoryLocationLcdStatus, lcdStatus);\n }\n}\n","// Functions to help with Handling Duty on Square Channels\n// http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Square_Wave\n\nimport { checkBitOnByte } from '../helpers/index';\n\n// Since there are no 2d arrays, we will use a byte to represent duty cycles (wave form from percentages)\nexport function isDutyCycleClockPositiveOrNegativeForWaveform(channelDuty: i32, waveFormPositionOnDuty: i32): boolean {\n // Get our Wave Form According to the Duty\n // Default to a duty of 1\n // http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Square_Wave\n switch (channelDuty) {\n case 0x01:\n // 1000 0001\n return checkBitOnByte(waveFormPositionOnDuty, 0x81);\n case 0x02:\n // 1000 0111\n return checkBitOnByte(waveFormPositionOnDuty, 0x87);\n case 0x03:\n // 0111 1110\n return checkBitOnByte(waveFormPositionOnDuty, 0x7e);\n default:\n // 0000 0001\n return checkBitOnByte(waveFormPositionOnDuty, 0x01);\n }\n}\n","// Functions involved in R/W of sound registers\n// Information of bits on every register can be found at: https://gist.github.com/drhelius/3652407\n// Passing channel number to make things simpler than passing around memory addresses, to avoid bugs in choosing the wrong address\n\nimport { Sound } from './sound';\nimport { SoundAccumulator } from './accumulator';\nimport { Channel1 } from './channel1';\nimport { Channel2 } from './channel2';\nimport { Channel3 } from './channel3';\nimport { Channel4 } from './channel4';\nimport { eightBitLoadFromGBMemory, eightBitStoreIntoGBMemory } from '../memory/index';\nimport { checkBitOnByte, setBitOnByte, resetBitOnByte, hexLog } from '../helpers/index';\n\n// Function to check and handle writes to sound registers\nexport function SoundRegisterWriteTraps(offset: i32, value: i32): boolean {\n if (offset !== Sound.memoryLocationNR52 && !Sound.NR52IsSoundEnabled) {\n // Block all writes to any sound register EXCEPT NR52!\n // This is under the assumption that the check for\n // offset >= 0xFF10 && offset <= 0xFF26\n // is done in writeTraps.ts (which it is)\n // NOTE: Except on DMG, length can still be written (whatever that means)\n return false;\n }\n\n switch (offset) {\n // Handle NRx0 on Channels\n case Channel1.memoryLocationNRx0:\n Channel1.updateNRx0(value);\n return true;\n case Channel3.memoryLocationNRx0:\n Channel3.updateNRx0(value);\n return true;\n // Handle NRx1 (Length Counter) on Channels\n case Channel1.memoryLocationNRx1:\n Channel1.updateNRx1(value);\n return true;\n case Channel2.memoryLocationNRx1:\n Channel2.updateNRx1(value);\n return true;\n case Channel3.memoryLocationNRx1:\n Channel3.updateNRx1(value);\n return true;\n case Channel4.memoryLocationNRx1:\n Channel4.updateNRx1(value);\n return true;\n // Handle NRx2 (Envelope / Volume) on Channels\n case Channel1.memoryLocationNRx2:\n Channel1.updateNRx2(value);\n return true;\n case Channel2.memoryLocationNRx2:\n Channel2.updateNRx2(value);\n return true;\n case Channel3.memoryLocationNRx2:\n // Check if channel 3's volume code was written too\n // This is handcy to know for accumulation of samples\n Channel3.volumeCodeChanged = true;\n Channel3.updateNRx2(value);\n return true;\n case Channel4.memoryLocationNRx2:\n Channel4.updateNRx2(value);\n return true;\n // Handle NRx3 (Frequency / Noise Properties) on Channels\n case Channel1.memoryLocationNRx3:\n Channel1.updateNRx3(value);\n return true;\n case Channel2.memoryLocationNRx3:\n Channel2.updateNRx3(value);\n return true;\n case Channel3.memoryLocationNRx3:\n Channel3.updateNRx3(value);\n return true;\n case Channel4.memoryLocationNRx3:\n Channel4.updateNRx3(value);\n return true;\n // Check our NRx4 registers to trap our trigger bits\n case Channel1.memoryLocationNRx4:\n if (checkBitOnByte(7, value)) {\n Channel1.updateNRx4(value);\n Channel1.trigger();\n }\n return true;\n case Channel2.memoryLocationNRx4:\n if (checkBitOnByte(7, value)) {\n Channel2.updateNRx4(value);\n Channel2.trigger();\n }\n return true;\n case Channel3.memoryLocationNRx4:\n if (checkBitOnByte(7, value)) {\n Channel3.updateNRx4(value);\n Channel3.trigger();\n }\n return true;\n case Channel4.memoryLocationNRx4:\n if (checkBitOnByte(7, value)) {\n Channel4.updateNRx4(value);\n Channel4.trigger();\n }\n return true;\n // Tell the sound accumulator if volumes changes\n case Sound.memoryLocationNR50:\n Sound.updateNR50(value);\n SoundAccumulator.mixerVolumeChanged = true;\n return true;\n // Tell the sound accumulator if volumes changes\n case Sound.memoryLocationNR51:\n Sound.updateNR51(value);\n SoundAccumulator.mixerEnabledChanged = true;\n return true;\n case Sound.memoryLocationNR52:\n // Reset all registers except NR52\n Sound.updateNR52(value);\n if (!checkBitOnByte(7, value)) {\n for (let i: i32 = 0xff10; i < 0xff26; i++) {\n eightBitStoreIntoGBMemory(i, 0x00);\n }\n }\n return true;\n }\n\n // We did not handle the write, Allow the write\n return true;\n}\n\n// http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Registers\nexport function SoundRegisterReadTraps(offset: i32): i32 {\n // TODO: OR All Registers\n\n // This will fix bugs in orcale of ages :)\n if (offset === Sound.memoryLocationNR52) {\n // Get our registerNR52\n let registerNR52: i32 = eightBitLoadFromGBMemory(Sound.memoryLocationNR52);\n\n // Knock off lower 7 bits\n registerNR52 = registerNR52 & 0x80;\n\n // Set our lower 4 bits to our channel isEnabled statuses\n if (Channel1.isEnabled) {\n setBitOnByte(0, registerNR52);\n } else {\n resetBitOnByte(0, registerNR52);\n }\n\n if (Channel2.isEnabled) {\n setBitOnByte(1, registerNR52);\n } else {\n resetBitOnByte(1, registerNR52);\n }\n\n if (Channel3.isEnabled) {\n setBitOnByte(2, registerNR52);\n } else {\n resetBitOnByte(2, registerNR52);\n }\n\n if (Channel4.isEnabled) {\n setBitOnByte(3, registerNR52);\n } else {\n resetBitOnByte(3, registerNR52);\n }\n\n // Or from the table\n registerNR52 = registerNR52 | 0x70;\n\n return registerNR52;\n }\n\n return -1;\n}\n","import { Memory } from './memory';\nimport { Graphics, batchProcessGraphics } from '../graphics/graphics';\nimport { Palette, Lcd } from '../graphics/index';\nimport { batchProcessAudio, SoundRegisterReadTraps } from '../sound/index';\nimport { eightBitStoreIntoGBMemory } from './store';\nimport { eightBitLoadFromGBMemory } from './load';\nimport { Joypad, getJoypadState } from '../joypad/index';\nimport { Timers } from '../timers/index';\nimport { hexLog } from '../helpers/index';\n\n// Returns -1 if no trap found, otherwise returns a value that should be fed for the address\nexport function checkReadTraps(offset: i32): i32 {\n // Cache globals used multiple times for performance\n let videoRamLocation: i32 = Memory.videoRamLocation;\n\n // Try to break early for most common scenario\n if (offset < videoRamLocation) {\n return -1;\n }\n\n // Check the graphics mode to see if we can read VRAM\n // http://gbdev.gg8.se/wiki/articles/Video_Display#Accessing_VRAM_and_OAM\n if (offset >= videoRamLocation && offset < Memory.cartridgeRamLocation) {\n // Can only read/write from VRAM During Modes 0 - 2\n // See graphics/lcd.ts\n // TODO: This can do more harm than good in a beta emulator,\n // requres precise timing, disabling for now\n // if (Graphics.currentLcdMode > 2) {\n // return 0xFF;\n // }\n\n return -1;\n }\n\n // ECHO Ram, E000\tFDFF\tMirror of C000~DDFF (ECHO RAM)\n // http://gbdev.gg8.se/wiki/articles/Memory_Map\n if (offset >= Memory.echoRamLocation && offset < Memory.spriteInformationTableLocation) {\n // Simply return the mirror'd value\n return eightBitLoadFromGBMemory(offset - 0x2000);\n }\n\n // Check for individal writes\n // Can only read/write from OAM During Modes 0 - 1\n // See graphics/lcd.ts\n if (offset >= Memory.spriteInformationTableLocation && offset <= Memory.spriteInformationTableLocationEnd) {\n // Can only read/write from OAM During Mode 2\n // See graphics/lcd.ts\n if (Lcd.currentLcdMode < 2) {\n return 0xff;\n }\n\n // Not batch processing here for performance\n // batchProcessGraphics();\n\n return -1;\n }\n\n // Graphics\n // Not batch processing here for performance\n // batchProcessGraphics();\n if (offset === Graphics.memoryLocationScanlineRegister) {\n eightBitStoreIntoGBMemory(offset, Graphics.scanlineRegister);\n return Graphics.scanlineRegister;\n }\n\n // Sound\n // http://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Registers\n // TODO: Put these bounds on the Sound Class\n if (offset >= 0xff10 && offset <= 0xff26) {\n batchProcessAudio();\n return SoundRegisterReadTraps(offset);\n }\n // FF27 - FF2F not used\n // Final Wave Table for Channel 3\n if (offset >= 0xff30 && offset <= 0xff3f) {\n batchProcessAudio();\n return -1;\n }\n\n // Timers\n if (offset === Timers.memoryLocationDividerRegister) {\n eightBitStoreIntoGBMemory(offset, Timers.dividerRegister);\n return Timers.dividerRegister;\n }\n if (offset === Timers.memoryLocationTimerCounter) {\n eightBitStoreIntoGBMemory(offset, Timers.timerCounter);\n return Timers.timerCounter;\n }\n\n // Joypad\n if (offset === Joypad.memoryLocationJoypadRegister) {\n return getJoypadState();\n }\n\n return -1;\n}\n","import { Cpu } from '../cpu/index';\nimport { eightBitStoreIntoGBMemory } from '../memory/store';\nimport { eightBitLoadFromGBMemory } from '../memory/load';\nimport { requestJoypadInterrupt } from '../interrupts/index';\nimport { checkBitOnByte, setBitOnByte, resetBitOnByte, hexLog } from '../helpers/index';\n\n// http://www.codeslinger.co.uk/pages/projects/gameboy/joypad.html\n// Joypad Register\n// Taken from pandocs\n// Bit 7 - Not used\n// Bit 6 - Not used\n// Bit 5 - P15 Select Button Keys (0=Select)\n// Bit 4 - P14 Select Direction Keys (0=Select)\n// Bit 3 - P13 Input Down or Start (0=Pressed) (Read Only)\n// Bit 2 - P12 Input Up or Select (0=Pressed) (Read Only)\n// Bit 1 - P11 Input Left or Button B (0=Pressed) (Read Only)\n// Bit 0 - P10 Input Right or Button A (0=Pressed) (Read Only)\n\n// Button Ids will be the following:\n// UP - 0\n// RIGHT - 1\n// DOWN - 2\n// LEFT - 3\n// A - 4\n// B - 5\n// SELECT - 6\n// START - 7\n\nexport class Joypad {\n static up: boolean = false;\n static down: boolean = false;\n static left: boolean = false;\n static right: boolean = false;\n static a: boolean = false;\n static b: boolean = false;\n static select: boolean = false;\n static start: boolean = false;\n\n static readonly memoryLocationJoypadRegister: i32 = 0xff00;\n // Cache some values on the Joypad register\n static joypadRegisterFlipped: i32 = 0;\n static isDpadType: boolean = false;\n static isButtonType: boolean = false;\n static updateJoypad(value: i32): void {\n Joypad.joypadRegisterFlipped = value ^ 0xff;\n Joypad.isDpadType = checkBitOnByte(4, Joypad.joypadRegisterFlipped);\n Joypad.isButtonType = checkBitOnByte(5, Joypad.joypadRegisterFlipped);\n }\n\n // Save States\n // Not doing anything for Joypad for now\n\n static readonly saveStateSlot: i32 = 3;\n\n // Function to save the state of the class\n static saveState(): void {}\n\n // Function to load the save state from memory\n static loadState(): void {\n Joypad.updateJoypad(eightBitLoadFromGBMemory(Joypad.memoryLocationJoypadRegister));\n }\n}\n\nexport function getJoypadState(): i32 {\n // Get the joypad register\n let joypadRegister: i32 = Joypad.joypadRegisterFlipped;\n\n if (Joypad.isDpadType) {\n // D-pad buttons\n\n // Up\n if (Joypad.up) {\n joypadRegister = resetBitOnByte(2, joypadRegister);\n } else {\n joypadRegister = setBitOnByte(2, joypadRegister);\n }\n\n // Right\n if (Joypad.right) {\n joypadRegister = resetBitOnByte(0, joypadRegister);\n } else {\n joypadRegister = setBitOnByte(0, joypadRegister);\n }\n\n // Down\n if (Joypad.down) {\n joypadRegister = resetBitOnByte(3, joypadRegister);\n } else {\n joypadRegister = setBitOnByte(3, joypadRegister);\n }\n\n // Left\n if (Joypad.left) {\n joypadRegister = resetBitOnByte(1, joypadRegister);\n } else {\n joypadRegister = setBitOnByte(1, joypadRegister);\n }\n } else if (Joypad.isButtonType) {\n // A\n if (Joypad.a) {\n joypadRegister = resetBitOnByte(0, joypadRegister);\n } else {\n joypadRegister = setBitOnByte(0, joypadRegister);\n }\n\n // B\n if (Joypad.b) {\n joypadRegister = resetBitOnByte(1, joypadRegister);\n } else {\n joypadRegister = setBitOnByte(1, joypadRegister);\n }\n\n // Select\n if (Joypad.select) {\n joypadRegister = resetBitOnByte(2, joypadRegister);\n } else {\n joypadRegister = setBitOnByte(2, joypadRegister);\n }\n\n // Start\n if (Joypad.start) {\n joypadRegister = resetBitOnByte(3, joypadRegister);\n } else {\n joypadRegister = setBitOnByte(3, joypadRegister);\n }\n }\n\n // Set the top 4 bits to on\n joypadRegister = joypadRegister | 0xf0;\n\n return joypadRegister;\n}\n\nexport function setJoypadState(up: i32, right: i32, down: i32, left: i32, a: i32, b: i32, select: i32, start: i32): void {\n if (up > 0) {\n _pressJoypadButton(0);\n } else {\n _releaseJoypadButton(0);\n }\n\n if (right > 0) {\n _pressJoypadButton(1);\n } else {\n _releaseJoypadButton(1);\n }\n\n if (down > 0) {\n _pressJoypadButton(2);\n } else {\n _releaseJoypadButton(2);\n }\n\n if (left > 0) {\n _pressJoypadButton(3);\n } else {\n _releaseJoypadButton(3);\n }\n\n if (a > 0) {\n _pressJoypadButton(4);\n } else {\n _releaseJoypadButton(4);\n }\n\n if (b > 0) {\n _pressJoypadButton(5);\n } else {\n _releaseJoypadButton(5);\n }\n\n if (select > 0) {\n _pressJoypadButton(6);\n } else {\n _releaseJoypadButton(6);\n }\n\n if (start > 0) {\n _pressJoypadButton(7);\n } else {\n _releaseJoypadButton(7);\n }\n}\n\nfunction _pressJoypadButton(buttonId: i32): void {\n // Un stop the CPU\n Cpu.isStopped = false;\n\n // Check if the button state changed from not pressed\n let isButtonStateChanging: boolean = false;\n if (!_getJoypadButtonStateFromButtonId(buttonId)) {\n isButtonStateChanging = true;\n }\n\n // Set our joypad state\n _setJoypadButtonStateFromButtonId(buttonId, true);\n\n // If the button state is changing, check for an interrupt\n if (isButtonStateChanging) {\n // Determine if it is a button or a dpad button\n let isDpadTypeButton = false;\n if (buttonId <= 3) {\n isDpadTypeButton = true;\n }\n\n // Determine if we should request an interrupt\n let joypadRegister: i32 = Joypad.joypadRegisterFlipped;\n\n let shouldRequestInterrupt = false;\n\n // Check if the game is looking for a dpad type button press\n if (Joypad.isDpadType && isDpadTypeButton) {\n shouldRequestInterrupt = true;\n }\n\n // Check if the game is looking for a button type button press\n if (Joypad.isButtonType && !isDpadTypeButton) {\n shouldRequestInterrupt = true;\n }\n\n // Finally, request the interrupt, if the button state actually changed\n if (shouldRequestInterrupt) {\n requestJoypadInterrupt();\n }\n }\n}\n\nfunction _releaseJoypadButton(buttonId: i32): void {\n // Set our joypad state\n _setJoypadButtonStateFromButtonId(buttonId, false);\n}\n\nfunction _getJoypadButtonStateFromButtonId(buttonId: i32): boolean {\n switch (buttonId) {\n case 0:\n return Joypad.up;\n case 1:\n return Joypad.right;\n case 2:\n return Joypad.down;\n case 3:\n return Joypad.left;\n case 4:\n return Joypad.a;\n case 5:\n return Joypad.b;\n case 6:\n return Joypad.select;\n case 7:\n return Joypad.start;\n default:\n return false;\n }\n}\n\nfunction _setJoypadButtonStateFromButtonId(buttonId: i32, isPressed: boolean): void {\n switch (buttonId) {\n case 0:\n Joypad.up = isPressed;\n break;\n case 1:\n Joypad.right = isPressed;\n break;\n case 2:\n Joypad.down = isPressed;\n break;\n case 3:\n Joypad.left = isPressed;\n break;\n case 4:\n Joypad.a = isPressed;\n break;\n case 5:\n Joypad.b = isPressed;\n break;\n case 6:\n Joypad.select = isPressed;\n break;\n case 7:\n Joypad.start = isPressed;\n break;\n }\n}\n","import { Cpu } from '../cpu/index';\nimport { getSaveStateMemoryOffset } from '../core';\nimport {\n eightBitLoadFromGBMemory,\n eightBitStoreIntoGBMemory,\n sixteenBitStoreIntoGBMemory,\n loadBooleanDirectlyFromWasmMemory,\n storeBooleanDirectlyToWasmMemory\n} from '../memory/index';\nimport { setBitOnByte, resetBitOnByte, checkBitOnByte, hexLog } from '../helpers/index';\n\nexport class Interrupts {\n static masterInterruptSwitch: boolean = false;\n // According to mooneye, interrupts are not handled until AFTER\n // Next instruction\n // https://github.com/Gekkio/mooneye-gb/blob/master/docs/accuracy.markdown\n static masterInterruptSwitchDelay: boolean = false;\n\n // Biut position for each part of the interrupts HW registers\n static readonly bitPositionVBlankInterrupt: i32 = 0;\n static readonly bitPositionLcdInterrupt: i32 = 1;\n static readonly bitPositionTimerInterrupt: i32 = 2;\n static readonly bitPositionJoypadInterrupt: i32 = 4;\n\n static readonly memoryLocationInterruptEnabled: i32 = 0xffff;\n // Cache which Interrupts are enabled\n static interruptsEnabledValue: i32 = 0;\n static isVBlankInterruptEnabled: boolean = false;\n static isLcdInterruptEnabled: boolean = false;\n static isTimerInterruptEnabled: boolean = false;\n static isJoypadInterruptEnabled: boolean = false;\n static updateInterruptEnabled(value: i32): void {\n Interrupts.isVBlankInterruptEnabled = checkBitOnByte(Interrupts.bitPositionVBlankInterrupt, value);\n Interrupts.isLcdInterruptEnabled = checkBitOnByte(Interrupts.bitPositionLcdInterrupt, value);\n Interrupts.isTimerInterruptEnabled = checkBitOnByte(Interrupts.bitPositionTimerInterrupt, value);\n Interrupts.isJoypadInterruptEnabled = checkBitOnByte(Interrupts.bitPositionJoypadInterrupt, value);\n\n Interrupts.interruptsEnabledValue = value;\n }\n\n static readonly memoryLocationInterruptRequest: i32 = 0xff0f; // A.K.A interrupt Flag (IF)\n // Cache which Interrupts are requested\n static interruptsRequestedValue: i32 = 0;\n static isVBlankInterruptRequested: boolean = false;\n static isLcdInterruptRequested: boolean = false;\n static isTimerInterruptRequested: boolean = false;\n static isJoypadInterruptRequested: boolean = false;\n static updateInterruptRequested(value: i32): void {\n Interrupts.isVBlankInterruptRequested = checkBitOnByte(Interrupts.bitPositionVBlankInterrupt, value);\n Interrupts.isLcdInterruptRequested = checkBitOnByte(Interrupts.bitPositionLcdInterrupt, value);\n Interrupts.isTimerInterruptRequested = checkBitOnByte(Interrupts.bitPositionTimerInterrupt, value);\n Interrupts.isJoypadInterruptRequested = checkBitOnByte(Interrupts.bitPositionJoypadInterrupt, value);\n\n Interrupts.interruptsRequestedValue = value;\n }\n\n // Function to return if we have any pending interrupts\n static areInterruptsPending(): boolean {\n return (Interrupts.interruptsRequestedValue & Interrupts.interruptsEnabledValue) > 0;\n }\n\n // Save States\n static readonly saveStateSlot: i32 = 2;\n\n // Function to save the state of the class\n static saveState(): void {\n storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x00, Interrupts.saveStateSlot), Interrupts.masterInterruptSwitch);\n storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x01, Interrupts.saveStateSlot), Interrupts.masterInterruptSwitchDelay);\n }\n\n // Function to load the save state from memory\n static loadState(): void {\n Interrupts.masterInterruptSwitch = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x00, Interrupts.saveStateSlot));\n Interrupts.masterInterruptSwitchDelay = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x01, Interrupts.saveStateSlot));\n\n Interrupts.updateInterruptEnabled(eightBitLoadFromGBMemory(Interrupts.memoryLocationInterruptEnabled));\n Interrupts.updateInterruptRequested(eightBitLoadFromGBMemory(Interrupts.memoryLocationInterruptRequest));\n }\n}\n\nexport function checkInterrupts(): i32 {\n if (Interrupts.masterInterruptSwitch && Interrupts.interruptsEnabledValue > 0 && Interrupts.interruptsRequestedValue > 0) {\n // Boolean to track if interrupts were handled\n // Interrupt handling requires 20 cycles\n // https://github.com/Gekkio/mooneye-gb/blob/master/docs/accuracy.markdown#what-is-the-exact-timing-of-cpu-servicing-an-interrupt\n let wasInterruptHandled: boolean = false;\n\n // Check our interrupts\n if (Interrupts.isVBlankInterruptEnabled && Interrupts.isVBlankInterruptRequested) {\n _handleInterrupt(Interrupts.bitPositionVBlankInterrupt);\n wasInterruptHandled = true;\n } else if (Interrupts.isLcdInterruptEnabled && Interrupts.isLcdInterruptRequested) {\n _handleInterrupt(Interrupts.bitPositionLcdInterrupt);\n wasInterruptHandled = true;\n } else if (Interrupts.isTimerInterruptEnabled && Interrupts.isTimerInterruptRequested) {\n _handleInterrupt(Interrupts.bitPositionTimerInterrupt);\n wasInterruptHandled = true;\n } else if (Interrupts.isJoypadInterruptEnabled && Interrupts.isJoypadInterruptRequested) {\n _handleInterrupt(Interrupts.bitPositionJoypadInterrupt);\n wasInterruptHandled = true;\n }\n\n // Interrupt handling requires 20 cycles, TCAGBD\n if (wasInterruptHandled) {\n let intteruptHandlerCycles: i32 = 20;\n if (Cpu.isHalted) {\n // If the CPU was halted, now is the time to un-halt\n // Should be done here when the jump occurs according to:\n // https://www.reddit.com/r/EmuDev/comments/6fmjch/gb_glitches_in_links_awakening_and_pok%C3%A9mon_gold/\n Cpu.isHalted = false;\n intteruptHandlerCycles += 4;\n }\n return intteruptHandlerCycles;\n }\n }\n\n return 0;\n}\n\nfunction _handleInterrupt(bitPosition: i32): void {\n // Disable the master switch\n setInterrupts(false);\n\n // Disable the bit on the interruptRequest\n let interruptRequest = eightBitLoadFromGBMemory(Interrupts.memoryLocationInterruptRequest);\n interruptRequest = resetBitOnByte(bitPosition, interruptRequest);\n Interrupts.interruptsRequestedValue = interruptRequest;\n eightBitStoreIntoGBMemory(Interrupts.memoryLocationInterruptRequest, interruptRequest);\n\n // Push the programCounter onto the stacks\n Cpu.stackPointer = Cpu.stackPointer - 2;\n sixteenBitStoreIntoGBMemory(Cpu.stackPointer, Cpu.programCounter);\n\n // Jump to the correct interrupt location\n // Also puiggyback off of the switch to reset our HW Register caching\n // http://www.codeslinger.co.uk/pages/projects/gameboy/interupts.html\n switch (bitPosition) {\n case Interrupts.bitPositionVBlankInterrupt:\n Interrupts.isVBlankInterruptRequested = false;\n Cpu.programCounter = 0x40;\n break;\n case Interrupts.bitPositionLcdInterrupt:\n Interrupts.isLcdInterruptRequested = false;\n Cpu.programCounter = 0x48;\n break;\n case Interrupts.bitPositionTimerInterrupt:\n Interrupts.isTimerInterruptRequested = false;\n Cpu.programCounter = 0x50;\n break;\n case Interrupts.bitPositionJoypadInterrupt:\n Interrupts.isJoypadInterruptRequested = false;\n Cpu.programCounter = 0x60;\n break;\n }\n}\n\nfunction _requestInterrupt(bitPosition: i32): void {\n let interruptRequest = eightBitLoadFromGBMemory(Interrupts.memoryLocationInterruptRequest);\n\n // Pass to set the correct interrupt bit on interruptRequest\n interruptRequest = setBitOnByte(bitPosition, interruptRequest);\n\n Interrupts.interruptsRequestedValue = interruptRequest;\n\n eightBitStoreIntoGBMemory(Interrupts.memoryLocationInterruptRequest, interruptRequest);\n}\n\nexport function setInterrupts(value: boolean): void {\n Interrupts.masterInterruptSwitch = value;\n}\n\nexport function requestVBlankInterrupt(): void {\n Interrupts.isVBlankInterruptRequested = true;\n _requestInterrupt(Interrupts.bitPositionVBlankInterrupt);\n}\n\nexport function requestLcdInterrupt(): void {\n Interrupts.isLcdInterruptRequested = true;\n _requestInterrupt(Interrupts.bitPositionLcdInterrupt);\n}\n\nexport function requestTimerInterrupt(): void {\n Interrupts.isTimerInterruptRequested = true;\n _requestInterrupt(Interrupts.bitPositionTimerInterrupt);\n}\n\nexport function requestJoypadInterrupt(): void {\n Interrupts.isJoypadInterruptRequested = true;\n _requestInterrupt(Interrupts.bitPositionJoypadInterrupt);\n}\n","import { Cpu } from './index';\nimport { u8Portable, u16Portable } from '../portable/portable';\n\n// Set flag bit on on register F. For instance set zero flag to zero -> (7, 0)\nfunction setFlagBit(flagBit: u8, flagValue: i32): u8 {\n let bitwiseOperand: u8 = u8Portable(1 << flagBit);\n if (flagValue > 0) {\n Cpu.registerF = Cpu.registerF | bitwiseOperand;\n } else {\n // XOR out the two ones\n bitwiseOperand = 0xff ^ bitwiseOperand;\n Cpu.registerF = Cpu.registerF & bitwiseOperand;\n }\n\n return Cpu.registerF;\n}\n\n// Overload the set flag bit for ease of use\nexport function setZeroFlag(value: i32): void {\n setFlagBit(7, value);\n}\n\nexport function setSubtractFlag(value: i32): void {\n setFlagBit(6, value);\n}\n\nexport function setHalfCarryFlag(value: i32): void {\n setFlagBit(5, value);\n}\n\nexport function setCarryFlag(value: i32): void {\n setFlagBit(4, value);\n}\n\n// Getters for flags\nexport function getZeroFlag(): u8 {\n return (Cpu.registerF >> 7) & 0x01;\n}\n\nexport function getSubtractFlag(): u8 {\n return (Cpu.registerF >> 6) & 0x01;\n}\n\nexport function getHalfCarryFlag(): u8 {\n return (Cpu.registerF >> 5) & 0x01;\n}\n\nexport function getCarryFlag(): u8 {\n return (Cpu.registerF >> 4) & 0x01;\n}\n\n// Must be run before the register actually performs the add\n// amountToAdd i16, since max number can be an u8\nexport function checkAndSetEightBitHalfCarryFlag(value: u8, amountToAdd: i32): void {\n if (amountToAdd >= 0) {\n // https://robdor.com/2016/08/10/gameboy-emulator-half-carry-flag/\n let result: u8 = u8Portable(((value) & 0x0f) + ((amountToAdd) & 0x0f)) & 0x10;\n if (result !== 0x00) {\n setHalfCarryFlag(1);\n } else {\n setHalfCarryFlag(0);\n }\n } else {\n // From: https://github.com/djhworld/gomeboycolor/blob/master/src/cpu/index.go\n // CTRL+F \"subBytes(a, b byte)\"\n if ((abs(amountToAdd) & 0x0f) > (value & 0x0f)) {\n setHalfCarryFlag(1);\n } else {\n setHalfCarryFlag(0);\n }\n }\n}\n\nexport function checkAndSetEightBitCarryFlag(value: u8, amountToAdd: i32): void {\n if (amountToAdd >= 0) {\n let result: u8 = u8Portable(value + amountToAdd);\n if (value > result) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n } else {\n if (abs(amountToAdd) > value) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n }\n}\n\n// Function to handle 16 bit addition overflow, and set the carry flags accordingly\n// i32 on valueTwo to support passing signed immedaite values\nexport function checkAndSetSixteenBitFlagsAddOverflow(valueOne: u16, valueTwo: i32, useStackPointerBits: boolean): void {\n // need to differentiate between HL and SP\n // HL carries are at 11 and 15, SP carries are at 3 and 7 :p\n if (useStackPointerBits) {\n // Logic from : https://github.com/nakardo/node-gameboy/blob/master/lib/cpu/opcodes.js\n // CTRL+F add_sp_n\n // using the stack pointer bits means we can safely assume the value is signed\n let signedValueOne: i32 = valueOne;\n let result: i32 = signedValueOne + valueTwo;\n\n let flagXor: i32 = signedValueOne ^ valueTwo ^ result;\n\n if ((flagXor & 0x10) !== 0) {\n setHalfCarryFlag(1);\n } else {\n setHalfCarryFlag(0);\n }\n\n if ((flagXor & 0x100) !== 0) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n } else {\n // Logic from: https://github.com/djhworld/gomeboycolor/blob/master/src/cpu/index.go\n // CTRL+F addWords\n // Value two is not signed\n let result: u16 = u16Portable(valueOne + valueTwo);\n\n // Check the carry flag by allowing the overflow\n if (result < valueOne) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n\n // To check for half carry flag (bit 15), by XOR'ing valyes, and and'ing the bit in question\n let halfCarryXor: u16 = valueOne ^ (valueTwo) ^ (result);\n let halfCarryAnd: u16 = u16Portable(halfCarryXor & 0x1000);\n if (halfCarryAnd !== 0x00) {\n setHalfCarryFlag(1);\n } else {\n setHalfCarryFlag(0);\n }\n }\n}\n","// Imports\nimport { Cpu } from './index';\nimport {\n setZeroFlag,\n getZeroFlag,\n setSubtractFlag,\n getSubtractFlag,\n setHalfCarryFlag,\n getHalfCarryFlag,\n setCarryFlag,\n getCarryFlag,\n checkAndSetEightBitCarryFlag,\n checkAndSetEightBitHalfCarryFlag,\n checkAndSetSixteenBitFlagsAddOverflow\n} from './flags';\nimport {\n rotateByteLeft,\n rotateByteLeftThroughCarry,\n rotateByteRight,\n rotateByteRightThroughCarry,\n concatenateBytes,\n hexLog\n} from '../helpers/index';\nimport { u8Portable, u16Portable, i8Portable } from '../portable/portable';\n\n// General Logic Instructions\n// Such as the ones found on the CB table and 0x40 - 0xBF\n// NOTE: Only CB table uses these for now, was mostly me realizing that I messed up, trying to be all cute and verbose :p\n// NOTE: TODO: Refactor honestly shouldn't take that long, and may happen once assembly script is improved\nexport function addARegister(register: u8): void {\n checkAndSetEightBitHalfCarryFlag(Cpu.registerA, register);\n checkAndSetEightBitCarryFlag(Cpu.registerA, register);\n Cpu.registerA = u8Portable(Cpu.registerA + register);\n if (Cpu.registerA === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(0);\n}\n\nexport function addAThroughCarryRegister(register: u8): void {\n // Handling flags manually as they require some special overflow\n // From: https://github.com/nakardo/node-gameboy/blob/master/lib/cpu/opcodes.js\n // CTRL+F adc\n let result: u8 = u8Portable(Cpu.registerA + register + getCarryFlag());\n if ((u8Portable(Cpu.registerA ^ register ^ result) & 0x10) != 0) {\n setHalfCarryFlag(1);\n } else {\n setHalfCarryFlag(0);\n }\n\n let overflowedResult: u16 = u16Portable(Cpu.registerA + register + getCarryFlag());\n if ((overflowedResult & 0x100) > 0) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n\n Cpu.registerA = result;\n if (Cpu.registerA === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(0);\n}\n\nexport function subARegister(register: u8): void {\n // Need to convert the register on one line, and flip the sign on another\n let negativeRegister: i32 = register;\n negativeRegister = negativeRegister * -1;\n\n checkAndSetEightBitHalfCarryFlag(Cpu.registerA, negativeRegister);\n checkAndSetEightBitCarryFlag(Cpu.registerA, negativeRegister);\n Cpu.registerA = u8Portable(Cpu.registerA - register);\n if (Cpu.registerA === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(1);\n}\n\nexport function subAThroughCarryRegister(register: u8): void {\n // Handling flags manually as they require some special overflow\n // From: https://github.com/nakardo/node-gameboy/blob/master/lib/cpu/opcodes.js\n // CTRL+F adc\n let result: u8 = u8Portable(Cpu.registerA - register - getCarryFlag());\n\n let carryRegisterCheck = u8Portable((Cpu.registerA ^ register ^ result) & 0x10);\n if (carryRegisterCheck != 0) {\n setHalfCarryFlag(1);\n } else {\n setHalfCarryFlag(0);\n }\n\n let overflowedResult: u16 = u16Portable(Cpu.registerA - register - getCarryFlag());\n if ((overflowedResult & 0x100) > 0) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n\n Cpu.registerA = result;\n if (Cpu.registerA === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(1);\n}\n\nexport function andARegister(register: u8): void {\n Cpu.registerA = Cpu.registerA & register;\n if (Cpu.registerA === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(0);\n setHalfCarryFlag(1);\n setCarryFlag(0);\n}\n\nexport function xorARegister(register: u8): void {\n Cpu.registerA = u8Portable(Cpu.registerA ^ register);\n if (Cpu.registerA === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(0);\n setHalfCarryFlag(0);\n setCarryFlag(0);\n}\n\nexport function orARegister(register: u8): void {\n Cpu.registerA = Cpu.registerA | register;\n if (Cpu.registerA === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(0);\n setHalfCarryFlag(0);\n setCarryFlag(0);\n}\n\nexport function cpARegister(register: u8): void {\n // 0xB8 - 0xBF\n // CP B\n // 1 4\n // Z 1 H C\n let negativeRegister: i32 = register;\n negativeRegister = negativeRegister * -1;\n checkAndSetEightBitHalfCarryFlag(Cpu.registerA, negativeRegister);\n checkAndSetEightBitCarryFlag(Cpu.registerA, negativeRegister);\n let tempResult: i32 = Cpu.registerA + negativeRegister;\n if (tempResult === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n setSubtractFlag(1);\n}\n\nexport function rotateRegisterLeft(register: u8): u8 {\n // RLC register 8-bit\n // Z 0 0 C\n if ((register & 0x80) === 0x80) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n register = rotateByteLeft(register);\n if (register === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n\n // Set all other flags to zero\n setSubtractFlag(0);\n setHalfCarryFlag(0);\n\n // Return the register\n return register;\n}\n\nexport function rotateRegisterRight(register: u8): u8 {\n // RLC register 8-bit\n // Z 0 0 C\n // Check for the last bit, to see if it will be carried\n if ((register & 0x01) > 0) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n register = rotateByteRight(register);\n\n if (register === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n\n setSubtractFlag(0);\n setHalfCarryFlag(0);\n\n // Return the register\n return register;\n}\n\nexport function rotateRegisterLeftThroughCarry(register: u8): u8 {\n // RL register 8-bit\n // Z 0 0 C\n // setting has first bit since we need to use carry\n let hasHighbit = false;\n if ((register & 0x80) === 0x80) {\n hasHighbit = true;\n }\n register = rotateByteLeftThroughCarry(register);\n\n if (hasHighbit) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n\n if (register === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n\n setSubtractFlag(0);\n setHalfCarryFlag(0);\n\n return register;\n}\n\nexport function rotateRegisterRightThroughCarry(register: u8): u8 {\n // RR register 8-bit\n // Z 0 0 C\n let hasLowBit = false;\n if ((register & 0x01) === 0x01) {\n hasLowBit = true;\n }\n register = rotateByteRightThroughCarry(register);\n\n if (hasLowBit) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n\n if (register === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n\n setSubtractFlag(0);\n setHalfCarryFlag(0);\n\n return register;\n}\n\nexport function shiftLeftRegister(register: u8): u8 {\n // SLA register 8-bit\n // Z 0 0 C\n let hasHighbit = false;\n if ((register & 0x80) === 0x80) {\n hasHighbit = true;\n }\n\n register = u8Portable(register << 1);\n\n if (hasHighbit) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n\n if (register === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n\n setSubtractFlag(0);\n setHalfCarryFlag(0);\n\n return register;\n}\n\nexport function shiftRightArithmeticRegister(register: u8): u8 {\n // SRA register 8-bit\n // Z 0 0 C\n // NOTE: This C flag may need to be set to 0;\n // This preserves the MSB (Most significant bit)\n let hasHighbit = false;\n if ((register & 0x80) === 0x80) {\n hasHighbit = true;\n }\n\n let hasLowbit = false;\n if ((register & 0x01) === 0x01) {\n hasLowbit = true;\n }\n\n register = u8Portable(register >> 1);\n\n if (hasHighbit) {\n register = register | 0x80;\n }\n\n if (register === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n\n setSubtractFlag(0);\n setHalfCarryFlag(0);\n\n if (hasLowbit) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n\n return register;\n}\n\nexport function swapNibblesOnRegister(register: u8): u8 {\n // SWAP register 8-bit\n // Z 0 0 0\n let highNibble = register & 0xf0;\n let lowNibble = register & 0x0f;\n register = u8Portable((lowNibble << 4) | (highNibble >> 4));\n\n if (register === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n\n setSubtractFlag(0);\n setHalfCarryFlag(0);\n setCarryFlag(0);\n\n return register;\n}\n\nexport function shiftRightLogicalRegister(register: u8): u8 {\n // SRA register 8-bit\n // Z 0 0 C\n // NOTE: This C flag may need to be set to 0;\n // This does NOT preserve MSB (most significant bit)\n\n let hasLowbit = false;\n if ((register & 0x01) === 0x01) {\n hasLowbit = true;\n }\n\n register = u8Portable(register >> 1);\n\n if (register === 0) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n\n setSubtractFlag(0);\n setHalfCarryFlag(0);\n\n if (hasLowbit) {\n setCarryFlag(1);\n } else {\n setCarryFlag(0);\n }\n\n return register;\n}\n\nexport function testBitOnRegister(bitPosition: u8, register: u8): u8 {\n // BIT bitPosition ,register 8-bit\n // Z 0 1 -\n\n let testByte: u8 = 0x01 << bitPosition;\n let result = register & testByte;\n if (result === 0x00) {\n setZeroFlag(1);\n } else {\n setZeroFlag(0);\n }\n\n setSubtractFlag(0);\n setHalfCarryFlag(1);\n\n return register;\n}\n\nexport function setBitOnRegister(bitPosition: u8, bitValue: i32, register: u8): u8 {\n // RES 0,B or SET 0,B depending on bit value\n\n if (bitValue > 0) {\n let setByte: u8 = 0x01 << bitPosition;\n register = register | setByte;\n } else {\n // NOT (byte we want)\n // 0000 0100 becomes 1111 1011\n let setByte: u8 = ~(0x01 << bitPosition);\n register = register & setByte;\n }\n\n return register;\n}\n\n// Private function for our relative jumps\nexport function relativeJump(value: u8): void {\n // Need to convert the value to i8, since in this case, u8 can be negative\n let relativeJumpOffset: i8 = i8Portable(value);\n\n Cpu.programCounter = u16Portable(Cpu.programCounter + relativeJumpOffset);\n // Realtive jump, using bgb debugger\n // and my debugger shows,\n // on JR you need to jump to the relative jump offset,\n // However, if the jump fails (such as conditional), only jump +2 in total\n\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n}\n","// Imports\nimport { Cpu } from './index';\nimport {\n rotateRegisterLeft,\n rotateRegisterRight,\n rotateRegisterLeftThroughCarry,\n rotateRegisterRightThroughCarry,\n shiftLeftRegister,\n shiftRightArithmeticRegister,\n swapNibblesOnRegister,\n shiftRightLogicalRegister,\n testBitOnRegister,\n setBitOnRegister\n} from './instructions';\nimport { concatenateBytes, performanceTimestamp } from '../helpers/index';\nimport {\n eightBitStoreIntoGBMemoryWithTraps,\n sixteenBitStoreIntoGBMemoryWithTraps,\n eightBitLoadFromGBMemoryWithTraps,\n sixteenBitLoadFromGBMemory\n} from '../memory/index';\nimport { u8Portable, u16Portable } from '../portable/portable';\n\n// Handle CB Opcodes\n// NOTE: Program stpes and cycles are standardized depending on the register type\n// NOTE: Doing some funny stuff to get around not having arrays or objects\nexport function handleCbOpcode(cbOpcode: i32): i32 {\n let numberOfCycles: i32 = -1;\n let handledOpcode = false;\n\n // The result of our cb logic instruction\n let instructionRegisterValue: u8 = 0;\n let instructionRegisterResult: u8 = 0;\n\n // Get our register number by modulo 0x08 (number of registers)\n // cbOpcode % 0x08\n let registerNumber = cbOpcode % 0x08;\n\n // NOTE: registerNumber = register on CB table. Cpu.registerB = 0, Cpu.registerC = 1....Cpu.registerA = 7\n switch (registerNumber) {\n case 0:\n instructionRegisterValue = Cpu.registerB;\n break;\n case 1:\n instructionRegisterValue = Cpu.registerC;\n break;\n case 2:\n instructionRegisterValue = Cpu.registerD;\n break;\n case 3:\n instructionRegisterValue = Cpu.registerE;\n break;\n case 4:\n instructionRegisterValue = Cpu.registerH;\n break;\n case 5:\n instructionRegisterValue = Cpu.registerL;\n break;\n case 6:\n // Value at register HL\n instructionRegisterValue = eightBitLoadFromGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL));\n break;\n case 7:\n instructionRegisterValue = Cpu.registerA;\n break;\n }\n\n // Grab the high nibble to perform skips to speed up performance\n let opcodeHighNibble = cbOpcode & 0xf0;\n opcodeHighNibble = opcodeHighNibble >> 4;\n\n // Send to the correct function\n switch (opcodeHighNibble) {\n case 0x00:\n if (cbOpcode <= 0x07) {\n // RLC register 8-bit\n // Z 0 0 C\n instructionRegisterResult = rotateRegisterLeft(instructionRegisterValue);\n handledOpcode = true;\n } else if (cbOpcode <= 0x0f) {\n // RRC register 8-bit\n // Z 0 0 C\n instructionRegisterResult = rotateRegisterRight(instructionRegisterValue);\n handledOpcode = true;\n }\n break;\n case 0x01:\n if (cbOpcode <= 0x17) {\n // RL register 8-bit\n // Z 0 0 C\n instructionRegisterResult = rotateRegisterLeftThroughCarry(instructionRegisterValue);\n handledOpcode = true;\n } else if (cbOpcode <= 0x1f) {\n // RR register 8-bit\n // Z 0 0 C\n instructionRegisterResult = rotateRegisterRightThroughCarry(instructionRegisterValue);\n handledOpcode = true;\n }\n break;\n case 0x02:\n if (cbOpcode <= 0x27) {\n // SLA register 8-bit\n // Z 0 0 C\n instructionRegisterResult = shiftLeftRegister(instructionRegisterValue);\n handledOpcode = true;\n } else if (cbOpcode <= 0x2f) {\n // SRA register 8-bit\n // Z 0 0 0\n instructionRegisterResult = shiftRightArithmeticRegister(instructionRegisterValue);\n handledOpcode = true;\n }\n break;\n case 0x03:\n if (cbOpcode <= 0x37) {\n // SWAP register 8-bit\n // Z 0 0 0\n instructionRegisterResult = swapNibblesOnRegister(instructionRegisterValue);\n handledOpcode = true;\n } else if (cbOpcode <= 0x3f) {\n // SRL B\n // Z 0 0 C\n instructionRegisterResult = shiftRightLogicalRegister(instructionRegisterValue);\n handledOpcode = true;\n }\n break;\n case 0x04:\n if (cbOpcode <= 0x47) {\n // BIT 0,register 8-bit\n // Z 0 1 -\n //TODO: Optimize this not to do logic of setting register back\n instructionRegisterResult = testBitOnRegister(0, instructionRegisterValue);\n handledOpcode = true;\n } else if (cbOpcode <= 0x4f) {\n // BIT 1,register 8-bit\n // Z 0 1 -\n instructionRegisterResult = testBitOnRegister(1, instructionRegisterValue);\n handledOpcode = true;\n }\n break;\n case 0x05:\n if (cbOpcode <= 0x57) {\n // BIT 2,register 8-bit\n // Z 0 1 -\n instructionRegisterResult = testBitOnRegister(2, instructionRegisterValue);\n handledOpcode = true;\n } else if (cbOpcode <= 0x5f) {\n // BIT 3,register 8-bit\n // Z 0 1 -\n instructionRegisterResult = testBitOnRegister(3, instructionRegisterValue);\n handledOpcode = true;\n }\n break;\n case 0x06:\n if (cbOpcode <= 0x67) {\n // BIT 4,register 8-bit\n // Z 0 1 -\n instructionRegisterResult = testBitOnRegister(4, instructionRegisterValue);\n handledOpcode = true;\n } else if (cbOpcode <= 0x6f) {\n // BIT 5,register 8-bit\n // Z 0 1 -\n instructionRegisterResult = testBitOnRegister(5, instructionRegisterValue);\n handledOpcode = true;\n }\n break;\n case 0x07:\n if (cbOpcode <= 0x77) {\n // BIT 6,register 8-bit\n // Z 0 1 -\n instructionRegisterResult = testBitOnRegister(6, instructionRegisterValue);\n handledOpcode = true;\n } else if (cbOpcode <= 0x7f) {\n // BIT 7,register 8-bit\n // Z 0 1 -\n instructionRegisterResult = testBitOnRegister(7, instructionRegisterValue);\n handledOpcode = true;\n }\n break;\n case 0x08:\n if (cbOpcode <= 0x87) {\n // Res 0,register 8-bit\n // - - - -\n instructionRegisterResult = setBitOnRegister(0, 0, instructionRegisterValue);\n handledOpcode = true;\n } else if (cbOpcode <= 0x8f) {\n // Res 1,register 8-bit\n // - - - -\n instructionRegisterResult = setBitOnRegister(1, 0, instructionRegisterValue);\n handledOpcode = true;\n }\n break;\n case 0x09:\n if (cbOpcode <= 0x97) {\n // Res 2,register 8-bit\n // - - - -\n instructionRegisterResult = setBitOnRegister(2, 0, instructionRegisterValue);\n handledOpcode = true;\n } else if (cbOpcode <= 0x9f) {\n // Res 3,register 8-bit\n // - - - -\n instructionRegisterResult = setBitOnRegister(3, 0, instructionRegisterValue);\n handledOpcode = true;\n }\n break;\n case 0x0a:\n if (cbOpcode <= 0xa7) {\n // Res 4,register 8-bit\n // - - - -\n instructionRegisterResult = setBitOnRegister(4, 0, instructionRegisterValue);\n handledOpcode = true;\n } else if (cbOpcode <= 0xaf) {\n // Res 5,register 8-bit\n // - - - -\n instructionRegisterResult = setBitOnRegister(5, 0, instructionRegisterValue);\n handledOpcode = true;\n }\n break;\n case 0x0b:\n if (cbOpcode <= 0xb7) {\n // Res 6,register 8-bit\n // - - - -\n instructionRegisterResult = setBitOnRegister(6, 0, instructionRegisterValue);\n handledOpcode = true;\n } else if (cbOpcode <= 0xbf) {\n // Res 7,register 8-bit\n // - - - -\n instructionRegisterResult = setBitOnRegister(7, 0, instructionRegisterValue);\n handledOpcode = true;\n }\n break;\n case 0x0c:\n if (cbOpcode <= 0xc7) {\n // SET 0,register 8-bit\n // - - - -\n instructionRegisterResult = setBitOnRegister(0, 1, instructionRegisterValue);\n handledOpcode = true;\n } else if (cbOpcode <= 0xcf) {\n // SET 1,register 8-bit\n // - - - -\n instructionRegisterResult = setBitOnRegister(1, 1, instructionRegisterValue);\n handledOpcode = true;\n }\n break;\n case 0x0d:\n if (cbOpcode <= 0xd7) {\n // SET 2,register 8-bit\n // - - - -\n instructionRegisterResult = setBitOnRegister(2, 1, instructionRegisterValue);\n handledOpcode = true;\n } else if (cbOpcode <= 0xdf) {\n // SET 3,register 8-bit\n // - - - -\n instructionRegisterResult = setBitOnRegister(3, 1, instructionRegisterValue);\n handledOpcode = true;\n }\n break;\n case 0x0e:\n if (cbOpcode <= 0xe7) {\n // SET 4,register 8-bit\n // - - - -\n instructionRegisterResult = setBitOnRegister(4, 1, instructionRegisterValue);\n handledOpcode = true;\n } else if (cbOpcode <= 0xef) {\n // SET 5,register 8-bit\n // - - - -\n instructionRegisterResult = setBitOnRegister(5, 1, instructionRegisterValue);\n handledOpcode = true;\n }\n break;\n case 0x0f:\n if (cbOpcode <= 0xf7) {\n // SET 6,register 8-bit\n // - - - -\n instructionRegisterResult = setBitOnRegister(6, 1, instructionRegisterValue);\n handledOpcode = true;\n } else if (cbOpcode <= 0xff) {\n // SET 7,register 8-bit\n // - - - -\n instructionRegisterResult = setBitOnRegister(7, 1, instructionRegisterValue);\n handledOpcode = true;\n }\n break;\n }\n\n // Finally Pass back into the correct register\n switch (registerNumber) {\n case 0:\n Cpu.registerB = instructionRegisterResult;\n break;\n case 1:\n Cpu.registerC = instructionRegisterResult;\n break;\n case 2:\n Cpu.registerD = instructionRegisterResult;\n break;\n case 3:\n Cpu.registerE = instructionRegisterResult;\n break;\n case 4:\n Cpu.registerH = instructionRegisterResult;\n break;\n case 5:\n Cpu.registerL = instructionRegisterResult;\n break;\n case 6:\n // Value at register HL\n eightBitStoreIntoGBMemoryWithTraps(concatenateBytes(Cpu.registerH, Cpu.registerL), instructionRegisterResult);\n break;\n case 7:\n Cpu.registerA = instructionRegisterResult;\n break;\n }\n\n // Increase program counter, as all CB codes take two bytes\n // Program counter will really increase by two since opcodes handles the other\n Cpu.programCounter = u16Portable(Cpu.programCounter + 1);\n\n // Finally our number of cycles\n // Set if we handled the opcode\n if (handledOpcode) {\n // Next if register number was 6 (HL), number of cycles is 16\n numberOfCycles = 8;\n if (registerNumber === 6) {\n numberOfCycles = 16;\n }\n }\n\n // Return our number of cycles\n return numberOfCycles;\n}\n","// Functions for rendering the background\nimport { FRAME_LOCATION } from '../constants';\nimport { Cpu } from '../cpu/index';\nimport { Config } from '../config';\nimport { Graphics, loadFromVramBank, setPixelOnFrame, getRgbPixelStart } from './graphics';\nimport { getMonochromeColorFromPalette, getRgbColorFromPalette, getColorComponentFromRgb } from './palette';\nimport { addPriorityforPixel, getPriorityforPixel } from './priority';\nimport { TileCache, drawPixelsFromLineOfTile, getTileDataAddress } from './tiles';\n// Assembly script really not feeling the reexport\n// using Skip Traps, because LCD has unrestricted access\n// http://gbdev.gg8.se/wiki/articles/Video_Display#LCD_OAM_DMA_Transfers\nimport { eightBitLoadFromGBMemory } from '../memory/load';\nimport { Memory } from '../memory/memory';\nimport { hexLog, checkBitOnByte, setBitOnByte, resetBitOnByte } from '../helpers/index';\nimport { u8Portable } from '../portable/portable';\n\nexport function renderBackground(scanlineRegister: i32, tileDataMemoryLocation: i32, tileMapMemoryLocation: i32): void {\n // NOTE: Camera is reffering to what you can see inside the 160x144 viewport of the entire rendered 256x256 map.\n\n // Get our scrollX and scrollY (u16 to play nice with assemblyscript)\n // let scrollX: i32 = eightBitLoadFromGBMemory(Graphics.memoryLocationScrollX);\n // let scrollY: i32 = eightBitLoadFromGBMemory(Graphics.memoryLocationScrollY);\n let scrollX: i32 = Graphics.scrollX;\n let scrollY: i32 = Graphics.scrollY;\n\n // Get our current pixel y positon on the 160x144 camera (Row that the scanline draws across)\n // this is done by getting the current scroll Y position,\n // and adding it do what Y Value the scanline is drawing on the camera.\n let pixelYPositionInMap: i32 = scanlineRegister + scrollY;\n\n // Gameboy camera will \"wrap\" around the background map,\n // meaning that if the pixelValue is 350, then we need to subtract 256 (decimal) to get it's actual value\n // pixel values (scrollX and scrollY) range from 0x00 - 0xFF\n if (pixelYPositionInMap >= 0x100) {\n pixelYPositionInMap -= 0x100;\n }\n\n // Draw the Background scanline\n drawBackgroundWindowScanline(scanlineRegister, tileDataMemoryLocation, tileMapMemoryLocation, pixelYPositionInMap, 0, scrollX);\n}\n\nexport function renderWindow(scanlineRegister: i32, tileDataMemoryLocation: i32, tileMapMemoryLocation: i32): void {\n // Get our windowX and windowY\n // let windowX: i32 = eightBitLoadFromGBMemory(Graphics.memoryLocationWindowX);\n // let windowY: i32 = eightBitLoadFromGBMemory(Graphics.memoryLocationWindowY);\n let windowX: i32 = Graphics.windowX;\n let windowY: i32 = Graphics.windowY;\n\n // NOTE: Camera is reffering to what you can see inside the 160x144 viewport of the entire rendered 256x256 map.\n\n // First ensure that the scanline is greater than our window\n if (scanlineRegister < windowY) {\n // Window is not within the current camera view\n return;\n }\n\n // WindowX is offset by 7\n windowX = windowX - 7;\n\n // Get our current pixel y positon on the 160x144 camera (Row that the scanline draws across)\n let pixelYPositionInMap: i32 = scanlineRegister - windowY;\n\n // xOffset is simply a neagative window x\n let xOffset: i32 = -1 * windowX;\n\n // Draw the Background scanline\n drawBackgroundWindowScanline(scanlineRegister, tileDataMemoryLocation, tileMapMemoryLocation, pixelYPositionInMap, windowX, xOffset);\n}\n\n// Function frankenstein'd together to allow background and window to share the same draw scanline function\nfunction drawBackgroundWindowScanline(\n scanlineRegister: i32,\n tileDataMemoryLocation: i32,\n tileMapMemoryLocation: i32,\n pixelYPositionInMap: i32,\n iStart: i32,\n xOffset: i32\n): void {\n // Get our tile Y position in the map\n let tileYPositionInMap: i32 = pixelYPositionInMap >> 3;\n\n // Loop through x to draw the line like a CRT\n for (let i: i32 = iStart; i < 160; i++) {\n // Get our Current X position of our pixel on the on the 160x144 camera\n // this is done by getting the current scroll X position,\n // and adding it do what X Value the scanline is drawing on the camera.\n let pixelXPositionInMap: i32 = i + xOffset;\n\n // This is to compensate wrapping, same as pixelY\n if (pixelXPositionInMap >= 0x100) {\n pixelXPositionInMap -= 0x100;\n }\n\n // Divide our pixel position by 8 to get our tile.\n // Since, there are 256x256 pixels, and 32x32 tiles.\n // 256 / 8 = 32.\n // Also, bitshifting by 3, do do a division by 8\n // Need to use u16s, as they will be used to compute an address, which will cause weird errors and overflows\n let tileXPositionInMap: i32 = pixelXPositionInMap >> 3;\n\n // Get our tile address on the tileMap\n // NOTE: (tileMap represents where each tile is displayed on the screen)\n // NOTE: (tile map represents the entire map, now just what is within the \"camera\")\n // For instance, if we have y pixel 144. 144 / 8 = 18. 18 * 32 = line address in map memory.\n // And we have x pixel 160. 160 / 8 = 20.\n // * 32, because remember, this is NOT only for the camera, the actual map is 32x32. Therefore, the next tile line of the map, is 32 byte offset.\n // Think like indexing a 2d array, as a 1d array and it make sense :)\n let tileMapAddress: i32 = tileMapMemoryLocation + tileYPositionInMap * 32 + tileXPositionInMap;\n\n // Get the tile Id on the Tile Map\n let tileIdFromTileMap: i32 = loadFromVramBank(tileMapAddress, 0);\n\n // Now that we have our Tile Id, let's check our Tile Cache\n let usedTileCache: boolean = false;\n if (Config.tileCaching) {\n let pixelsDrawn: i32 = drawLineOfTileFromTileCache(\n i,\n scanlineRegister,\n pixelXPositionInMap,\n pixelYPositionInMap,\n tileMapAddress,\n tileDataMemoryLocation,\n tileIdFromTileMap\n );\n // Increment i by 7, not 8 because i will be incremented at end of for loop\n if (pixelsDrawn > 0) {\n i += pixelsDrawn - 1;\n usedTileCache = true;\n }\n }\n\n if (Config.tileRendering && !usedTileCache) {\n let pixelsDrawn: i32 = drawLineOfTileFromTileId(\n i,\n scanlineRegister,\n pixelXPositionInMap,\n pixelYPositionInMap,\n tileMapAddress,\n tileDataMemoryLocation,\n tileIdFromTileMap\n );\n // A line of a tile is 8 pixels wide, therefore increase i by (pixelsDrawn - 1), and then the for loop will increment by 1\n // For a net increment for 8\n if (pixelsDrawn > 0) {\n i += pixelsDrawn - 1;\n }\n } else if (!usedTileCache) {\n if (Cpu.GBCEnabled) {\n // Draw the individual pixel\n drawColorPixelFromTileId(\n i,\n scanlineRegister,\n pixelXPositionInMap,\n pixelYPositionInMap,\n tileMapAddress,\n tileDataMemoryLocation,\n tileIdFromTileMap\n );\n } else {\n // Draw the individual pixel\n drawMonochromePixelFromTileId(\n i,\n scanlineRegister,\n pixelXPositionInMap,\n pixelYPositionInMap,\n tileDataMemoryLocation,\n tileIdFromTileMap\n );\n }\n }\n }\n}\n\n// Function to draw a pixel for the standard GB\nfunction drawMonochromePixelFromTileId(\n xPixel: i32,\n yPixel: i32,\n pixelXPositionInMap: i32,\n pixelYPositionInMap: i32,\n tileDataMemoryLocation: i32,\n tileIdFromTileMap: i32\n): void {\n // Now we can process the the individual bytes that represent the pixel on a tile\n\n // Now get our tileDataAddress for the corresponding tileID we found in the map\n // Read the comments in _getTileDataAddress() to see what's going on.\n // tl;dr if we had the tile map of \"a b c d\", and wanted tileId 2.\n // This funcitons returns the start of memory locaiton for the tile 'c'.\n let tileDataAddress: i32 = getTileDataAddress(tileDataMemoryLocation, tileIdFromTileMap);\n\n // Get the y pixel of the 8 by 8 tile.\n // Simply modulo the scanline.\n // For instance, let's say we are printing the first line of pixels on our camera,\n // And the first line of pixels on our tile.\n // yPixel = 1. 1 % 8 = 1.\n // And for the last line\n // yPixel = 144. 144 % 8 = 0.\n // 0 Represents last line of pixels in a tile, 1 represents first. 1 2 3 4 5 6 7 0.\n // Because remember, we are counting lines on the display NOT including zero\n let pixelYInTile: i32 = pixelYPositionInMap % 8;\n\n // Remember to represent a single line of 8 pixels on a tile, we need two bytes.\n // Therefore, we need to times our modulo by 2, to get the correct line of pixels on the tile.\n // Again, think like you had to map a 2d array as a 1d.\n let byteOneForLineOfTilePixels: i32 = loadFromVramBank(tileDataAddress + pixelYInTile * 2, 0);\n let byteTwoForLineOfTilePixels: i32 = loadFromVramBank(tileDataAddress + pixelYInTile * 2 + 1, 0);\n\n // Same logic as pixelYInTile.\n // However, We need to reverse our byte,\n // As pixel 0 is on byte 7, and pixel 1 is on byte 6, etc...\n // Therefore, is pixelX was 2, then really is need to be 5\n // So 2 - 7 = -5, * 1 = 5\n // Or to simplify, 7 - 2 = 5 haha!\n let pixelXInTile: i32 = pixelXPositionInMap % 8;\n pixelXInTile = 7 - pixelXInTile;\n\n // Now we can get the color for that pixel\n // Colors are represented by getting X position of ByteTwo, and X positon of Byte One\n // To Get the color Id.\n // For example, the result of the color id is 0000 00[xPixelByteTwo][xPixelByteOne]\n // See: How to draw a tile/sprite from memory: http://www.codeslinger.co.uk/pages/projects/gameboy/graphics.html\n let paletteColorId: u8 = 0;\n if (checkBitOnByte(pixelXInTile, byteTwoForLineOfTilePixels)) {\n // Byte one represents the second bit in our color id, so bit shift\n paletteColorId += 1;\n paletteColorId = paletteColorId << 1;\n }\n if (checkBitOnByte(pixelXInTile, byteOneForLineOfTilePixels)) {\n paletteColorId += 1;\n }\n // Not checking u8 Portability overflow here, since it can't be greater than i32 over :p\n\n // Now get the colorId from the pallete, to get our final color\n // Developers could change colorIds to represents different colors\n // in their palette, thus we need to grab the color from there\n //let pixelColorInTileFromPalette: u8 = getColorFromPalette(paletteColorId, Graphics.memoryLocationBackgroundPalette);\n // Moved below for perofrmance\n\n // FINALLY, RENDER THAT PIXEL!\n // Only rendering camera for now, so coordinates are for the camera.\n // Get the rgb value for the color Id, will be repeated into R, G, B\n let monochromeColor: i32 = getMonochromeColorFromPalette(paletteColorId, Graphics.memoryLocationBackgroundPalette);\n setPixelOnFrame(xPixel, yPixel, 0, monochromeColor);\n setPixelOnFrame(xPixel, yPixel, 1, monochromeColor);\n setPixelOnFrame(xPixel, yPixel, 2, monochromeColor);\n\n // Lastly, add the pixel to our background priority map\n // https://github.com/torch2424/wasmBoy/issues/51\n // Bits 0 & 1 will represent the color Id drawn by the BG/Window\n // Bit 2 will represent if the Bg/Window has GBC priority.\n addPriorityforPixel(xPixel, yPixel, paletteColorId);\n}\n\n// Function to draw a pixel from a tile in C O L O R\n// See above for more context on some variables\nfunction drawColorPixelFromTileId(\n xPixel: i32,\n yPixel: i32,\n pixelXPositionInMap: i32,\n pixelYPositionInMap: i32,\n tileMapAddress: i32,\n tileDataMemoryLocation: i32,\n tileIdFromTileMap: i32\n): void {\n // Now get our tileDataAddress for the corresponding tileID we found in the map\n // Read the comments in _getTileDataAddress() to see what's going on.\n // tl;dr if we had the tile map of \"a b c d\", and wanted tileId 2.\n // This funcitons returns the start of memory locaiton for the tile 'c'.\n let tileDataAddress: i32 = getTileDataAddress(tileDataMemoryLocation, tileIdFromTileMap);\n\n // Get the GB Map Attributes\n // Bit 0-2 Background Palette number (BGP0-7)\n // Bit 3 Tile VRAM Bank number (0=Bank 0, 1=Bank 1)\n // Bit 4 Not used\n // Bit 5 Horizontal Flip (0=Normal, 1=Mirror horizontally)\n // Bit 6 Vertical Flip (0=Normal, 1=Mirror vertically)\n // Bit 7 BG-to-OAM Priority (0=Use OAM priority bit, 1=BG Priority)\n let bgMapAttributes: i32 = loadFromVramBank(tileMapAddress, 1);\n\n // See above for explanation\n let pixelYInTile: i32 = pixelYPositionInMap % 8;\n if (checkBitOnByte(6, bgMapAttributes)) {\n // We are mirroring the tile, therefore, we need to opposite byte\n // So if our pixel was 0 our of 8, it wild become 7 :)\n pixelYInTile = 7 - pixelYInTile;\n }\n\n // Remember to represent a single line of 8 pixels on a tile, we need two bytes.\n // Therefore, we need to times our modulo by 2, to get the correct line of pixels on the tile.\n // But we need to load the time from a specific Vram bank\n let vramBankId: i32 = 0;\n if (checkBitOnByte(3, bgMapAttributes)) {\n vramBankId = 1;\n }\n let byteOneForLineOfTilePixels: i32 = loadFromVramBank(tileDataAddress + pixelYInTile * 2, vramBankId);\n let byteTwoForLineOfTilePixels: i32 = loadFromVramBank(tileDataAddress + pixelYInTile * 2 + 1, vramBankId);\n\n // Get our X pixel. Need to NOT reverse it if it was flipped.\n // See above, you have to reverse this normally\n let pixelXInTile: i32 = pixelXPositionInMap % 8;\n if (!checkBitOnByte(5, bgMapAttributes)) {\n pixelXInTile = 7 - pixelXInTile;\n }\n\n // Now we can get the color for that pixel\n // Colors are represented by getting X position of ByteTwo, and X positon of Byte One\n // To Get the color Id.\n // For example, the result of the color id is 0000 00[xPixelByteTwo][xPixelByteOne]\n // See: How to draw a tile/sprite from memory: http://www.codeslinger.co.uk/pages/projects/gameboy/graphics.html\n let paletteColorId: i32 = 0;\n if (checkBitOnByte(pixelXInTile, byteTwoForLineOfTilePixels)) {\n // Byte one represents the second bit in our color id, so bit shift\n paletteColorId += 1;\n paletteColorId = paletteColorId << 1;\n }\n if (checkBitOnByte(pixelXInTile, byteOneForLineOfTilePixels)) {\n paletteColorId += 1;\n }\n\n // Finally lets add some, C O L O R\n // Want the botom 3 bits\n let bgPalette: i32 = bgMapAttributes & 0x07;\n\n // Call the helper function to grab the correct color from the palette\n let rgbColorPalette: i32 = getRgbColorFromPalette(bgPalette, paletteColorId, false);\n\n // Split off into red green and blue\n let red: i32 = getColorComponentFromRgb(0, rgbColorPalette);\n let green: i32 = getColorComponentFromRgb(1, rgbColorPalette);\n let blue: i32 = getColorComponentFromRgb(2, rgbColorPalette);\n\n // Finally Place our colors on the things\n setPixelOnFrame(xPixel, yPixel, 0, red);\n setPixelOnFrame(xPixel, yPixel, 1, green);\n setPixelOnFrame(xPixel, yPixel, 2, blue);\n\n // Lastly, add the pixel to our background priority map\n // https://github.com/torch2424/wasmBoy/issues/51\n // Bits 0 & 1 will represent the color Id drawn by the BG/Window\n // Bit 2 will represent if the Bg/Window has GBC priority.\n addPriorityforPixel(xPixel, yPixel, paletteColorId, checkBitOnByte(7, bgMapAttributes));\n}\n\n// Function to attempt to draw the tile from the tile cache\nfunction drawLineOfTileFromTileCache(\n xPixel: i32,\n yPixel: i32,\n pixelXPositionInMap: i32,\n pixelYPositionInMap: i32,\n tileMapAddress: i32,\n tileDataMemoryLocation: i32,\n tileIdFromTileMap: i32\n): i32 {\n // First, initialize how many pixels we have drawn\n let pixelsDrawn: i32 = 0;\n\n // Check if the current tile matches our tileId\n // TODO: Allow the first line to use the tile cache, for some odd reason it doesn't work when scanline is 0\n if (yPixel > 0 && xPixel > 8 && tileIdFromTileMap === TileCache.tileId && xPixel === TileCache.nextXIndexToPerformCacheCheck) {\n // Was last tile flipped\n let wasLastTileHorizontallyFlipped: boolean = false;\n let isCurrentTileHorizontallyFlipped: boolean = false;\n if (checkBitOnByte(5, eightBitLoadFromGBMemory(tileMapAddress - 1))) {\n wasLastTileHorizontallyFlipped = true;\n }\n if (checkBitOnByte(5, eightBitLoadFromGBMemory(tileMapAddress))) {\n isCurrentTileHorizontallyFlipped = true;\n }\n\n // Simply copy the last 8 pixels from memory to copy the line from the tile\n for (let tileCacheIndex = 0; tileCacheIndex < 8; tileCacheIndex++) {\n // Check if we need to render backwards for flipping\n if (wasLastTileHorizontallyFlipped !== isCurrentTileHorizontallyFlipped) {\n tileCacheIndex = 7 - tileCacheIndex;\n }\n\n // First check for overflow\n if (xPixel + tileCacheIndex <= 160) {\n // Get the pixel location in memory of the tile\n let previousXPixel = xPixel - (8 - tileCacheIndex);\n let previousTilePixelLocation = FRAME_LOCATION + getRgbPixelStart(xPixel + tileCacheIndex, yPixel);\n\n // Cycle through the RGB\n for (let tileCacheRgb = 0; tileCacheRgb < 3; tileCacheRgb++) {\n setPixelOnFrame(xPixel + tileCacheIndex, yPixel, tileCacheRgb, load(previousTilePixelLocation + tileCacheRgb));\n }\n\n // Copy the priority for the pixel\n let pixelPriority: i32 = getPriorityforPixel(previousXPixel, yPixel);\n addPriorityforPixel(xPixel + tileCacheIndex, yPixel, resetBitOnByte(2, pixelPriority), checkBitOnByte(2, pixelPriority));\n\n pixelsDrawn++;\n }\n }\n } else {\n // Save our current tile Id, and the next x value we should check the x index\n TileCache.tileId = tileIdFromTileMap;\n }\n\n // Calculate when we should do the tileCache calculation again\n if (xPixel >= TileCache.nextXIndexToPerformCacheCheck) {\n TileCache.nextXIndexToPerformCacheCheck = xPixel + 8;\n let xOffsetTileWidthRemainder: i32 = pixelXPositionInMap % 8;\n if (xPixel < xOffsetTileWidthRemainder) {\n TileCache.nextXIndexToPerformCacheCheck += xOffsetTileWidthRemainder;\n }\n }\n\n return pixelsDrawn;\n}\n\n// Function to draw a line of a tile in Color\n// This is for tile rendering shortcuts\nfunction drawLineOfTileFromTileId(\n xPixel: i32,\n yPixel: i32,\n pixelXPositionInMap: i32,\n pixelYPositionInMap: i32,\n tileMapAddress: i32,\n tileDataMemoryLocation: i32,\n tileIdFromTileMap: i32\n): i32 {\n // Get the which line of the tile we are rendering\n let tileLineY: i32 = pixelYPositionInMap % 8;\n\n // Now lets find our tileX start and end\n // This is for the case where i = 0, but scroll X was 3.\n // Or i is 157, and our camera is only 160 pixels wide\n let tileXStart: i32 = 0;\n if (xPixel == 0) {\n tileXStart = pixelXPositionInMap - (pixelXPositionInMap / 8) * 8;\n }\n let tileXEnd: i32 = 7;\n if (xPixel + 8 > 160) {\n tileXEnd = 160 - xPixel;\n }\n\n // initialize some variables for GBC\n let bgMapAttributes: i32 = -1;\n let vramBankId: i32 = 0;\n if (Cpu.GBCEnabled) {\n // Get Our GBC properties\n bgMapAttributes = loadFromVramBank(tileMapAddress, 1);\n if (checkBitOnByte(3, bgMapAttributes)) {\n vramBankId = 1;\n }\n\n if (checkBitOnByte(6, bgMapAttributes)) {\n // We are mirroring the tile, therefore, we need to opposite byte\n // So if our pixel was 0 our of 8, it wild become 7 :)\n tileLineY = 7 - tileLineY;\n }\n }\n\n // Return the number of pixels drawn\n return drawPixelsFromLineOfTile(\n tileIdFromTileMap,\n tileDataMemoryLocation,\n vramBankId,\n tileXStart,\n tileXEnd,\n tileLineY,\n xPixel,\n yPixel,\n 160,\n FRAME_LOCATION,\n false,\n 0,\n bgMapAttributes\n );\n}\n","// Functions for performance hacks, and debugging tiles\n\nimport { Cpu } from '../cpu/index';\nimport { Graphics, loadFromVramBank, setPixelOnFrame } from './graphics';\nimport { getMonochromeColorFromPalette, getRgbColorFromPalette, getColorComponentFromRgb } from './palette';\nimport { addPriorityforPixel } from './priority';\n// Assembly script really not feeling the reexport\n// using Skip Traps, because LCD has unrestricted access\n// http://gbdev.gg8.se/wiki/articles/Video_Display#LCD_OAM_DMA_Transfers\nimport { eightBitLoadFromGBMemory } from '../memory/load';\nimport { Memory } from '../memory/memory';\nimport { hexLog, checkBitOnByte, setBitOnByte, resetBitOnByte } from '../helpers/index';\n\nexport class TileCache {\n static tileId: i32 = -1;\n static horizontalFlip: boolean = false;\n static nextXIndexToPerformCacheCheck: i32 = -1;\n}\n\nexport function resetTileCache(): void {\n TileCache.tileId = -1;\n TileCache.nextXIndexToPerformCacheCheck = -1;\n}\n\nexport function drawPixelsFromLineOfTile(\n tileId: i32,\n tileDataMemoryLocation: i32,\n vramBankId: i32,\n tileLineXStart: i32,\n tileLineXEnd: i32,\n tileLineY: i32,\n outputLineX: i32,\n outputLineY: i32,\n outputWidth: i32,\n wasmMemoryStart: i32,\n shouldRepresentMonochromeColorByColorId: boolean = false,\n paletteLocation: i32 = 0,\n bgMapAttributes: i32 = -1\n): i32 {\n // Get our number of pixels drawn\n let pixelsDrawn: i32 = 0;\n\n // Get our tile data address\n let tileDataAddress: i32 = getTileDataAddress(tileDataMemoryLocation, tileId);\n\n // Get the bytes for our tile\n let byteOneForLineOfTilePixels: i32 = loadFromVramBank(tileDataAddress + tileLineY * 2, vramBankId);\n let byteTwoForLineOfTilePixels: i32 = loadFromVramBank(tileDataAddress + tileLineY * 2 + 1, vramBankId);\n\n // Loop through our X values to draw\n for (let x: i32 = tileLineXStart; x <= tileLineXEnd; x++) {\n // First find where we are going to do our final output x\n // And don't allow any width overflow\n let iteratedOutputX = outputLineX + (x - tileLineXStart);\n if (iteratedOutputX < outputWidth) {\n // However, We need to reverse our byte (if not horizontally flipped),\n // As pixel 0 is on byte 7, and pixel 1 is on byte 6, etc...\n // Therefore, is pixelX was 2, then really is need to be 5\n // So 2 - 7 = -5, * 1 = 5\n // Or to simplify, 7 - 2 = 5 haha!\n let pixelXInTile: i32 = x;\n if (bgMapAttributes < 0 || !checkBitOnByte(5, bgMapAttributes)) {\n pixelXInTile = 7 - pixelXInTile;\n }\n\n // Get our pallete colors for the tile\n let paletteColorId: i32 = 0;\n if (checkBitOnByte(pixelXInTile, byteTwoForLineOfTilePixels)) {\n // Byte one represents the second bit in our color id, so bit shift\n paletteColorId += 1;\n paletteColorId = paletteColorId << 1;\n }\n if (checkBitOnByte(pixelXInTile, byteOneForLineOfTilePixels)) {\n paletteColorId += 1;\n }\n\n // Get the pallete\n let red: i32 = 0;\n let green: i32 = 0;\n let blue: i32 = 0;\n\n // Check if we should draw color or not\n if (bgMapAttributes >= 0) {\n // Call the helper function to grab the correct color from the palette\n // Get the palette index byte\n let bgPalette: i32 = bgMapAttributes & 0x07;\n let rgbColorPalette: i32 = getRgbColorFromPalette(bgPalette, paletteColorId, false);\n\n // Split off into red green and blue\n red = getColorComponentFromRgb(0, rgbColorPalette);\n green = getColorComponentFromRgb(1, rgbColorPalette);\n blue = getColorComponentFromRgb(2, rgbColorPalette);\n } else {\n if (paletteLocation <= 0) {\n paletteLocation = Graphics.memoryLocationBackgroundPalette;\n }\n let monochromeColor: i32 = getMonochromeColorFromPalette(paletteColorId, paletteLocation, shouldRepresentMonochromeColorByColorId);\n red = monochromeColor;\n green = monochromeColor;\n blue = monochromeColor;\n }\n\n // Finally Lets place a pixel in memory\n // Find where our tile line would start\n let pixelStart: i32 = getTilePixelStart(iteratedOutputX, outputLineY, outputWidth);\n\n store(wasmMemoryStart + pixelStart, red);\n store(wasmMemoryStart + pixelStart + 1, green);\n store(wasmMemoryStart + pixelStart + 2, blue);\n\n let gbcBgPriority: boolean = false;\n if (bgMapAttributes >= 0) {\n gbcBgPriority = checkBitOnByte(7, bgMapAttributes);\n }\n\n // Lastly, add the pixel to our background priority map\n // https://github.com/torch2424/wasmBoy/issues/51\n // Bits 0 & 1 will represent the color Id drawn by the BG/Window\n // Bit 2 will represent if the Bg/Window has GBC priority.\n addPriorityforPixel(iteratedOutputX, outputLineY, paletteColorId, gbcBgPriority);\n\n pixelsDrawn++;\n }\n }\n\n return pixelsDrawn;\n}\n\nexport function getTilePixelStart(outputLineX: i32, outputLineY: i32, outputWidth: i32): i32 {\n // Finally Lets place a pixel in memory\n let pixelStart: i32 = outputLineY * outputWidth + outputLineX;\n\n // Each pixel takes 3 slots, therefore, multiply by 3!\n return pixelStart * 3;\n}\n\nexport function getTileDataAddress(tileDataMemoryLocation: i32, tileIdFromTileMap: i32): i32 {\n // Watch this part of The ultimate gameboy talk: https://youtu.be/HyzD8pNlpwI?t=30m50s\n // A line of 8 pixels on a single tile, is represented by 2 bytes.\n // since a single tile is 8x8 pixels, 8 * 2 = 16 bytes\n\n // Get the tile ID's tile addess from tile data.\n // For instance, let's say our first line of tile data represents tiles for letters:\n // a b c d e f g\n // And we have tileId 0x02. That means we want the tile for the 'c' character\n // Since each tile is 16 bytes, it would be the starting tileDataAddress + (tileId * tileSize), to skip over tiles we dont want\n // The whole signed thing is weird, and has something to do how the second set of tile data is stored :p\n if (tileDataMemoryLocation === Graphics.memoryLocationTileDataSelectZeroStart) {\n // Treat the tile Id as a signed int, subtract an offset of 128\n // if the tileId was 0 then the tile would be in memory region 0x9000-0x900F\n // NOTE: Assemblyscript, Can't cast to i16, need to make negative manually\n let signedTileId: i32 = tileIdFromTileMap + 128;\n if (checkBitOnByte(7, tileIdFromTileMap)) {\n signedTileId = tileIdFromTileMap - 128;\n }\n return tileDataMemoryLocation + signedTileId * 16;\n }\n\n // if the background layout gave us the tileId 0, then the tile data would be between 0x8000-0x800F.\n return tileDataMemoryLocation + tileIdFromTileMap * 16;\n}\n","// https://github.com/torch2424/wasmBoy/issues/51\n// Bits 0 & 1 will represent the color Id drawn by the BG/Window\n// Bit 2 will represent if the Bg/Window has GBC priority.\n\nimport { BG_PRIORITY_MAP_LOCATION } from '../constants';\nimport { setBitOnByte } from '../helpers/index';\n\nexport function addPriorityforPixel(x: i32, y: i32, colorId: i32 = 0, hasGbcBgPriority: boolean = false): void {\n let bgPriorityByte: i32 = colorId & 0x03;\n if (hasGbcBgPriority) {\n bgPriorityByte = setBitOnByte(2, bgPriorityByte);\n }\n\n store(BG_PRIORITY_MAP_LOCATION + getPixelStart(x, y), bgPriorityByte);\n}\n\nexport function getPriorityforPixel(x: i32, y: i32): u8 {\n return load(BG_PRIORITY_MAP_LOCATION + getPixelStart(x, y));\n}\n\nexport function clearPriorityMap(): void {\n for (let y: i32 = 0; y < 144; y++) {\n for (let x: i32 = 0; x < 160; x++) {\n store(BG_PRIORITY_MAP_LOCATION + getPixelStart(x, y), 0);\n }\n }\n}\n\nfunction getPixelStart(x: i32, y: i32): i32 {\n // Get the pixel number\n return y * 160 + x;\n}\n","// Functions for rendering the sprites\nimport { Graphics, loadFromVramBank, setPixelOnFrame } from './graphics';\nimport { Lcd } from './lcd';\nimport { Cpu } from '../cpu/index';\nimport { getTileDataAddress } from './tiles';\nimport { getMonochromeColorFromPalette, getRgbColorFromPalette, getColorComponentFromRgb } from './palette';\nimport { getPriorityforPixel } from './priority';\n// Assembly script really not feeling the reexport\n// using Skip Traps, because LCD has unrestricted access\n// http://gbdev.gg8.se/wiki/articles/Video_Display#LCD_OAM_DMA_Transfers\nimport { eightBitLoadFromGBMemory } from '../memory/load';\nimport { checkBitOnByte, setBitOnByte, resetBitOnByte, hexLog } from '../helpers/index';\n\nexport function renderSprites(scanlineRegister: i32, useLargerSprites: boolean): void {\n // Need to loop through all 40 sprites to check their status\n // Going backwards since lower sprites draw over higher ones\n // Will fix dragon warrior 3 intro\n for (let i: i32 = 39; i >= 0; i--) {\n // Sprites occupy 4 bytes in the sprite attribute table\n let spriteTableIndex: i32 = i * 4;\n // Y positon is offset by 16, X position is offset by 8\n\n let spriteYPosition: i32 = eightBitLoadFromGBMemory(Graphics.memoryLocationSpriteAttributesTable + spriteTableIndex);\n let spriteXPosition: i32 = eightBitLoadFromGBMemory(Graphics.memoryLocationSpriteAttributesTable + spriteTableIndex + 1);\n let spriteTileId: i32 = eightBitLoadFromGBMemory(Graphics.memoryLocationSpriteAttributesTable + spriteTableIndex + 2);\n\n // Pan docs of sprite attirbute table\n // Bit7 OBJ-to-BG Priority (0=OBJ Above BG, 1=OBJ Behind BG color 1-3)\n // (Used for both BG and Window. BG color 0 is always behind OBJ)\n // Bit6 Y flip (0=Normal, 1=Vertically mirrored)\n // Bit5 X flip (0=Normal, 1=Horizontally mirrored)\n // Bit4 Palette number **Non CGB Mode Only** (0=OBP0, 1=OBP1)\n // Bit3 Tile VRAM-Bank **CGB Mode Only** (0=Bank 0, 1=Bank 1)\n // Bit2-0 Palette number **CGB Mode Only** (OBP0-7)\n\n // Apply sprite X and Y offset\n // TODO: Sprites are overflowing on x if less than 8\n spriteYPosition -= 16;\n spriteXPosition -= 8;\n\n // Find our sprite height\n let spriteHeight: i32 = 8;\n if (useLargerSprites) {\n spriteHeight = 16;\n // @binji says in 8x16 mode, even tileId always drawn first\n // This will fix shantae sprites which always uses odd numbered indexes\n\n // TODO: Do the actual Pandocs thing:\n // \"In 8x16 mode, the lower bit of the tile number is ignored. Ie. the upper 8x8 tile is \"NN AND FEh\", and the lower 8x8 tile is \"NN OR 01h\".\"\n // So just knock off the last bit? :)\n if (spriteTileId % 2 === 1) {\n spriteTileId -= 1;\n }\n }\n\n // Find if our sprite is on the current scanline\n if (scanlineRegister >= spriteYPosition && scanlineRegister < spriteYPosition + spriteHeight) {\n // Then we need to draw the current sprite\n\n // Get our sprite attributes since we know we shall be drawing the tile\n let spriteAttributes: i32 = eightBitLoadFromGBMemory(Graphics.memoryLocationSpriteAttributesTable + spriteTableIndex + 3);\n\n // Check sprite Priority\n let isSpritePriorityBehindWindowAndBackground: boolean = checkBitOnByte(7, spriteAttributes);\n\n // Check if we should flip the sprite on the x or y axis\n let flipSpriteY: boolean = checkBitOnByte(6, spriteAttributes);\n let flipSpriteX: boolean = checkBitOnByte(5, spriteAttributes);\n\n // Find which line on the sprite we are on\n let currentSpriteLine: i32 = scanlineRegister - spriteYPosition;\n\n // If we fliiped the Y axis on our sprite, need to read from memory backwards to acheive the same effect\n if (flipSpriteY) {\n currentSpriteLine -= spriteHeight;\n currentSpriteLine = currentSpriteLine * -1;\n\n // Bug fix for the flipped flies in link's awakening\n currentSpriteLine -= 1;\n }\n\n // Each line of a tile takes two bytes of memory\n currentSpriteLine = currentSpriteLine * 2;\n\n // Get our sprite tile address, need to also add the current sprite line to get the correct bytes\n let spriteTileAddressStart: i32 = getTileDataAddress(Graphics.memoryLocationTileDataSelectOneStart, spriteTileId);\n spriteTileAddressStart = spriteTileAddressStart + currentSpriteLine;\n let spriteTileAddress: i32 = spriteTileAddressStart;\n\n // Find which VRAM Bank to load from\n let vramBankId: i32 = 0;\n if (Cpu.GBCEnabled && checkBitOnByte(3, spriteAttributes)) {\n vramBankId = 1;\n }\n let spriteDataByteOneForLineOfTilePixels: i32 = loadFromVramBank(spriteTileAddress, vramBankId);\n let spriteDataByteTwoForLineOfTilePixels: i32 = loadFromVramBank(spriteTileAddress + 1, vramBankId);\n\n // Iterate over the width of our sprite to find our individual pixels\n for (let tilePixel: i32 = 7; tilePixel >= 0; tilePixel--) {\n // Get our spritePixel, and check for flipping\n let spritePixelXInTile: i32 = tilePixel;\n if (flipSpriteX) {\n spritePixelXInTile -= 7;\n spritePixelXInTile = spritePixelXInTile * -1;\n }\n\n // Get the color Id of our sprite, similar to renderBackground()\n // With the first byte, and second byte lined up method thing\n // Yes, the second byte comes before the first, see ./background.ts\n let spriteColorId: i32 = 0;\n if (checkBitOnByte(spritePixelXInTile, spriteDataByteTwoForLineOfTilePixels)) {\n // Byte one represents the second bit in our color id, so bit shift\n spriteColorId += 1;\n spriteColorId = spriteColorId << 1;\n }\n if (checkBitOnByte(spritePixelXInTile, spriteDataByteOneForLineOfTilePixels)) {\n spriteColorId += 1;\n }\n\n // ColorId zero (last two bits of pallette) are transparent\n // http://gbdev.gg8.se/wiki/articles/Video_Display\n if (spriteColorId !== 0) {\n // Find our actual X pixel location on the gameboy \"camera\" view\n // This cannot be less than zero, i32 will overflow\n let spriteXPixelLocationInCameraView: i32 = spriteXPosition + (7 - tilePixel);\n if (spriteXPixelLocationInCameraView >= 0 && spriteXPixelLocationInCameraView <= 160) {\n // There are two cases where wouldnt draw the pixel on top of the Bg/window\n // 1. if isSpritePriorityBehindWindowAndBackground, sprite can only draw over color 0\n // 2. if bit 2 of our priority is set, then BG-to-OAM Priority from pandoc\n // is active, meaning BG tile will have priority above all OBJs\n // (regardless of the priority bits in OAM memory)\n // But if GBC and Bit 0 of LCDC is set, we always draw the object\n let shouldShowFromLcdcPriority: boolean = false;\n let shouldHideFromOamPriority: boolean = false;\n let shouldHideFromBgPriority: boolean = false;\n // LCDC Priority\n if (Cpu.GBCEnabled && !Lcd.bgDisplayEnabled) {\n shouldShowFromLcdcPriority = true;\n }\n\n if (!shouldShowFromLcdcPriority) {\n // Now that we have our coordinates, check for sprite priority\n // Lets get the priority byte we put in memory\n let bgPriorityByte: i32 = getPriorityforPixel(spriteXPixelLocationInCameraView, scanlineRegister);\n\n let bgColorFromPriorityByte: i32 = bgPriorityByte & 0x03;\n\n // Doing an else if, since either will automatically stop drawing the pixel\n if (isSpritePriorityBehindWindowAndBackground && bgColorFromPriorityByte > 0) {\n // OAM Priority\n shouldHideFromOamPriority = true;\n } else if (Cpu.GBCEnabled && checkBitOnByte(2, bgPriorityByte) && bgColorFromPriorityByte > 0) {\n // Bg priority\n shouldHideFromBgPriority = true;\n }\n }\n\n if (shouldShowFromLcdcPriority || (!shouldHideFromOamPriority && !shouldHideFromBgPriority)) {\n if (!Cpu.GBCEnabled) {\n // Get our monochrome color RGB from the current sprite pallete\n // Get our sprite pallete\n let spritePaletteLocation: i32 = Graphics.memoryLocationSpritePaletteOne;\n if (checkBitOnByte(4, spriteAttributes)) {\n spritePaletteLocation = Graphics.memoryLocationSpritePaletteTwo;\n }\n let spritePixelColorFromPalette: i32 = getMonochromeColorFromPalette(spriteColorId, spritePaletteLocation);\n\n // Finally set the pixel!\n setPixelOnFrame(spriteXPixelLocationInCameraView, scanlineRegister, 0, spritePixelColorFromPalette);\n setPixelOnFrame(spriteXPixelLocationInCameraView, scanlineRegister, 1, spritePixelColorFromPalette);\n setPixelOnFrame(spriteXPixelLocationInCameraView, scanlineRegister, 2, spritePixelColorFromPalette);\n } else {\n // Get our RGB Color\n\n // Finally lets add some, C O L O R\n // Want the botom 3 bits\n let bgPalette: i32 = spriteAttributes & 0x07;\n\n // Call the helper function to grab the correct color from the palette\n let rgbColorPalette: i32 = getRgbColorFromPalette(bgPalette, spriteColorId, true);\n\n // Split off into red green and blue\n let red: i32 = getColorComponentFromRgb(0, rgbColorPalette);\n let green: i32 = getColorComponentFromRgb(1, rgbColorPalette);\n let blue: i32 = getColorComponentFromRgb(2, rgbColorPalette);\n\n // Finally Place our colors on the things\n setPixelOnFrame(spriteXPixelLocationInCameraView, scanlineRegister, 0, red);\n setPixelOnFrame(spriteXPixelLocationInCameraView, scanlineRegister, 1, green);\n setPixelOnFrame(spriteXPixelLocationInCameraView, scanlineRegister, 2, blue);\n }\n }\n }\n }\n }\n }\n }\n}\n","// Functions to get information about the emulator for debugging purposes\nimport { Cpu } from '../cpu/index';\nimport { eightBitLoadFromGBMemory } from '../memory/index';\n\nexport function getRegisterA(): u8 {\n return Cpu.registerA;\n}\n\nexport function getRegisterB(): u8 {\n return Cpu.registerB;\n}\n\nexport function getRegisterC(): u8 {\n return Cpu.registerC;\n}\n\nexport function getRegisterD(): u8 {\n return Cpu.registerD;\n}\n\nexport function getRegisterE(): u8 {\n return Cpu.registerE;\n}\n\nexport function getRegisterH(): u8 {\n return Cpu.registerH;\n}\n\nexport function getRegisterL(): u8 {\n return Cpu.registerL;\n}\n\nexport function getRegisterF(): u8 {\n return Cpu.registerF;\n}\n\nexport function getProgramCounter(): u16 {\n return Cpu.programCounter;\n}\n\nexport function getStackPointer(): u16 {\n return Cpu.stackPointer;\n}\n\nexport function getOpcodeAtProgramCounter(): u8 {\n return eightBitLoadFromGBMemory(Cpu.programCounter);\n}\n","// Functions to debug graphical output\nimport { BACKGROUND_MAP_LOCATION, TILE_DATA_LOCATION } from '../constants';\nimport {\n Graphics,\n Lcd,\n getTileDataAddress,\n drawPixelsFromLineOfTile,\n getMonochromeColorFromPalette,\n getRgbColorFromPalette,\n getColorComponentFromRgb,\n loadFromVramBank\n} from '../graphics/index';\nimport { Cpu } from '../cpu/index';\nimport { eightBitLoadFromGBMemory, Memory } from '../memory/index';\nimport { checkBitOnByte, hexLog } from '../helpers/index';\n\nexport function drawBackgroundMapToWasmMemory(showColor: i32 = 0): void {\n // http://www.codeslinger.co.uk/pages/projects/gameboy/graphics.html\n // Bit 7 - LCD Display Enable (0=Off, 1=On)\n // Bit 6 - Window Tile Map Display Select (0=9800-9BFF, 1=9C00-9FFF)\n // Bit 5 - Window Display Enable (0=Off, 1=On)\n // Bit 4 - BG & Window Tile Data Select (0=8800-97FF, 1=8000-8FFF)\n // Bit 3 - BG Tile Map Display Select (0=9800-9BFF, 1=9C00-9FFF)\n // Bit 2 - OBJ (Sprite) Size (0=8x8, 1=8x16)\n // Bit 1 - OBJ (Sprite) Display Enable (0=Off, 1=On)\n // Bit 0 - BG Display (for CGB see below) (0=Off, 1=On)\n\n // Get our seleted tile data memory location\n let tileDataMemoryLocation = Graphics.memoryLocationTileDataSelectZeroStart;\n if (Lcd.bgWindowTileDataSelect) {\n tileDataMemoryLocation = Graphics.memoryLocationTileDataSelectOneStart;\n }\n\n let tileMapMemoryLocation = Graphics.memoryLocationTileMapSelectZeroStart;\n if (Lcd.bgTileMapDisplaySelect) {\n tileMapMemoryLocation = Graphics.memoryLocationTileMapSelectOneStart;\n }\n\n for (let y: i32 = 0; y < 256; y++) {\n for (let x: i32 = 0; x < 256; x++) {\n // Get our current Y\n let pixelYPositionInMap: i32 = y;\n\n // Get our Current X position of our pixel on the on the 160x144 camera\n // this is done by getting the current scroll X position,\n // and adding it do what X Value the scanline is drawing on the camera.\n let pixelXPositionInMap: i32 = x;\n\n // Divide our pixel position by 8 to get our tile.\n // Since, there are 256x256 pixels, and 32x32 tiles.\n // 256 / 8 = 32.\n // Also, bitshifting by 3, do do a division by 8\n // Need to use u16s, as they will be used to compute an address, which will cause weird errors and overflows\n let tileXPositionInMap: i32 = pixelXPositionInMap >> 3;\n let tileYPositionInMap: i32 = pixelYPositionInMap >> 3;\n\n // Get our tile address on the tileMap\n // NOTE: (tileMap represents where each tile is displayed on the screen)\n // NOTE: (tile map represents the entire map, now just what is within the \"camera\")\n // For instance, if we have y pixel 144. 144 / 8 = 18. 18 * 32 = line address in map memory.\n // And we have x pixel 160. 160 / 8 = 20.\n // * 32, because remember, this is NOT only for the camera, the actual map is 32x32. Therefore, the next tile line of the map, is 32 byte offset.\n // Think like indexing a 2d array, as a 1d array and it make sense :)\n let tileMapAddress: i32 = tileMapMemoryLocation + tileYPositionInMap * 32 + tileXPositionInMap;\n\n // Get the tile Id on the Tile Map\n let tileIdFromTileMap: i32 = loadFromVramBank(tileMapAddress, 0);\n\n // Now get our tileDataAddress for the corresponding tileID we found in the map\n // Read the comments in _getTileDataAddress() to see what's going on.\n // tl;dr if we had the tile map of \"a b c d\", and wanted tileId 2.\n // This funcitons returns the start of memory locaiton for the tile 'c'.\n let tileDataAddress: i32 = getTileDataAddress(tileDataMemoryLocation, tileIdFromTileMap);\n\n // Now we can process the the individual bytes that represent the pixel on a tile\n\n // Get the y pixel of the 8 by 8 tile.\n // Simply modulo the scanline.\n // For instance, let's say we are printing the first line of pixels on our camera,\n // And the first line of pixels on our tile.\n // yPixel = 1. 1 % 8 = 1.\n // And for the last line\n // yPixel = 144. 144 % 8 = 0.\n // 0 Represents last line of pixels in a tile, 1 represents first. 1 2 3 4 5 6 7 0.\n // Because remember, we are counting lines on the display NOT including zero\n let pixelYInTile: i32 = pixelYPositionInMap % 8;\n\n // Same logic as pixelYInTile.\n // However, We need to reverse our byte,\n // As pixel 0 is on byte 7, and pixel 1 is on byte 6, etc...\n // Therefore, is pixelX was 2, then really is need to be 5\n // So 2 - 7 = -5, * 1 = 5\n // Or to simplify, 7 - 2 = 5 haha!\n let pixelXInTile: i32 = pixelXPositionInMap % 8;\n pixelXInTile = 7 - pixelXInTile;\n\n // Get the GB Map Attributes\n // Bit 0-2 Background Palette number (BGP0-7)\n // Bit 3 Tile VRAM Bank number (0=Bank 0, 1=Bank 1)\n // Bit 4 Not used\n // Bit 5 Horizontal Flip (0=Normal, 1=Mirror horizontally)\n // Bit 6 Vertical Flip (0=Normal, 1=Mirror vertically)\n // Bit 7 BG-to-OAM Priority (0=Use OAM priority bit, 1=BG Priority)\n let bgMapAttributes: i32 = 0;\n if (Cpu.GBCEnabled && showColor > 0) {\n bgMapAttributes = loadFromVramBank(tileMapAddress, 1);\n }\n\n if (checkBitOnByte(6, bgMapAttributes)) {\n // We are mirroring the tile, therefore, we need to opposite byte\n // So if our pizel was 0 our of 8, it wild become 7 :)\n // TODO: This may be wrong :p\n pixelYInTile = 7 - pixelYInTile;\n }\n\n // Remember to represent a single line of 8 pixels on a tile, we need two bytes.\n // Therefore, we need to times our modulo by 2, to get the correct line of pixels on the tile.\n // But we need to load the time from a specific Vram bank\n let vramBankId: i32 = 0;\n if (checkBitOnByte(3, bgMapAttributes)) {\n vramBankId = 1;\n }\n\n // Remember to represent a single line of 8 pixels on a tile, we need two bytes.\n // Therefore, we need to times our modulo by 2, to get the correct line of pixels on the tile.\n // Again, think like you had to map a 2d array as a 1d.\n let byteOneForLineOfTilePixels: i32 = loadFromVramBank(tileDataAddress + pixelYInTile * 2, vramBankId);\n let byteTwoForLineOfTilePixels: i32 = loadFromVramBank(tileDataAddress + pixelYInTile * 2 + 1, vramBankId);\n\n // Now we can get the color for that pixel\n // Colors are represented by getting X position of ByteTwo, and X positon of Byte One\n // To Get the color Id.\n // For example, the result of the color id is 0000 00[xPixelByteTwo][xPixelByteOne]\n // See: How to draw a tile/sprite from memory: http://www.codeslinger.co.uk/pages/projects/gameboy/graphics.html\n let paletteColorId: i32 = 0;\n if (checkBitOnByte(pixelXInTile, byteTwoForLineOfTilePixels)) {\n // Byte one represents the second bit in our color id, so bit shift\n paletteColorId += 1;\n paletteColorId = paletteColorId << 1;\n }\n if (checkBitOnByte(pixelXInTile, byteOneForLineOfTilePixels)) {\n paletteColorId += 1;\n }\n\n // FINALLY, RENDER THAT PIXEL!\n let pixelStart: i32 = (y * 256 + x) * 3;\n\n if (Cpu.GBCEnabled && showColor > 0) {\n // Finally lets add some, C O L O R\n // Want the botom 3 bits\n let bgPalette: i32 = bgMapAttributes & 0x07;\n\n // Call the helper function to grab the correct color from the palette\n let rgbColorPalette: i32 = getRgbColorFromPalette(bgPalette, paletteColorId, false);\n\n // Split off into red green and blue\n let red: i32 = getColorComponentFromRgb(0, rgbColorPalette);\n let green: i32 = getColorComponentFromRgb(1, rgbColorPalette);\n let blue: i32 = getColorComponentFromRgb(2, rgbColorPalette);\n\n let offset: i32 = BACKGROUND_MAP_LOCATION + pixelStart;\n store(offset, red);\n store(offset + 1, green);\n store(offset + 2, blue);\n } else {\n // Only rendering camera for now, so coordinates are for the camera.\n // Get the rgb value for the color Id, will be repeated into R, G, B\n let monochromeColor: i32 = getMonochromeColorFromPalette(paletteColorId, Graphics.memoryLocationBackgroundPalette);\n\n for (let i: i32 = 0; i < 3; i++) {\n let offset: i32 = BACKGROUND_MAP_LOCATION + pixelStart + i;\n store(offset, monochromeColor);\n }\n }\n }\n }\n}\n\nexport function drawTileDataToWasmMemory(): void {\n for (let tileDataMapGridY: i32 = 0; tileDataMapGridY < 0x17; tileDataMapGridY++) {\n for (let tileDataMapGridX: i32 = 0; tileDataMapGridX < 0x1f; tileDataMapGridX++) {\n // Get Our VramBankID\n let vramBankId: i32 = 0;\n if (tileDataMapGridX > 0x0f) {\n vramBankId = 1;\n }\n\n // Get our tile ID\n let tileId: i32 = tileDataMapGridY;\n if (tileDataMapGridY > 0x0f) {\n tileId -= 0x0f;\n }\n tileId = tileId << 4;\n if (tileDataMapGridX > 0x0f) {\n tileId = tileId + (tileDataMapGridX - 0x0f);\n } else {\n tileId = tileId + tileDataMapGridX;\n }\n\n // Finally get our tile Data location\n let tileDataMemoryLocation: i32 = Graphics.memoryLocationTileDataSelectOneStart;\n if (tileDataMapGridY > 0x0f) {\n tileDataMemoryLocation = Graphics.memoryLocationTileDataSelectZeroStart;\n }\n\n // Draw each Y line of the tile\n for (let tileLineY: i32 = 0; tileLineY < 8; tileLineY++) {\n drawPixelsFromLineOfTile(\n tileId,\n tileDataMemoryLocation,\n vramBankId,\n 0,\n 7,\n tileLineY,\n tileDataMapGridX * 8,\n tileDataMapGridY * 8 + tileLineY,\n 0x1f * 8,\n TILE_DATA_LOCATION,\n true\n );\n }\n }\n }\n}\n","// These are legacy aliases for the original WasmBoy api\n\n/******************\n\n Functions\n\n*******************/\n\nexport { executeFrame as update, executeStep as emulationStep } from './core';\n\nexport { getNumberOfSamplesInAudioBuffer as getAudioQueueIndex, clearAudioBuffer as resetAudioQueue } from './sound/sound';\n\n/******************\n\n Memory Constants\n\n*******************/\nimport {\n WASMBOY_MEMORY_LOCATION,\n WASMBOY_MEMORY_SIZE,\n WASMBOY_WASM_PAGES,\n ASSEMBLYSCRIPT_MEMORY_LOCATION,\n ASSEMBLYSCRIPT_MEMORY_SIZE,\n WASMBOY_STATE_LOCATION,\n WASMBOY_STATE_SIZE,\n GAMEBOY_INTERNAL_MEMORY_LOCATION,\n GAMEBOY_INTERNAL_MEMORY_SIZE,\n VIDEO_RAM_LOCATION,\n VIDEO_RAM_SIZE,\n WORK_RAM_LOCATION,\n WORK_RAM_SIZE,\n OTHER_GAMEBOY_INTERNAL_MEMORY_LOCATION,\n OTHER_GAMEBOY_INTERNAL_MEMORY_SIZE,\n GRAPHICS_OUTPUT_LOCATION,\n GRAPHICS_OUTPUT_SIZE,\n GBC_PALETTE_LOCATION,\n GBC_PALETTE_SIZE,\n BG_PRIORITY_MAP_LOCATION,\n BG_PRIORITY_MAP_SIZE,\n FRAME_LOCATION,\n FRAME_SIZE,\n BACKGROUND_MAP_LOCATION,\n BACKGROUND_MAP_SIZE,\n TILE_DATA_LOCATION,\n TILE_DATA_SIZE,\n OAM_TILES_LOCATION,\n OAM_TILES_SIZE,\n AUDIO_BUFFER_LOCATION,\n AUDIO_BUFFER_SIZE,\n CARTRIDGE_RAM_LOCATION,\n CARTRIDGE_RAM_SIZE,\n CARTRIDGE_ROM_LOCATION,\n CARTRIDGE_ROM_SIZE\n} from './constants';\n\n// WasmBoy\nexport const wasmMemorySize: i32 = WASMBOY_MEMORY_SIZE;\nexport const wasmPages: i32 = WASMBOY_WASM_PAGES;\nexport const assemblyScriptMemoryBaseLocation: i32 = ASSEMBLYSCRIPT_MEMORY_LOCATION;\nexport const wasmBoyInternalStateLocation: i32 = WASMBOY_STATE_LOCATION;\nexport const wasmBoyInternalStateSize: i32 = WASMBOY_STATE_SIZE;\n\n// Gameboy\nexport const gameBoyInternalMemoryLocation: i32 = GAMEBOY_INTERNAL_MEMORY_LOCATION;\nexport const gameBoyInternalMemorySize: i32 = GAMEBOY_INTERNAL_MEMORY_SIZE;\nexport const gameBoyVramLocation: i32 = VIDEO_RAM_LOCATION;\nexport const gameBoyWramLocation: i32 = WORK_RAM_LOCATION;\nexport const gameBoyMemoryRegistersLocation: i32 = OTHER_GAMEBOY_INTERNAL_MEMORY_LOCATION;\n\n// Video output\nexport const videoOutputLocation: i32 = GRAPHICS_OUTPUT_LOCATION;\nexport const gameboyColorPaletteLocation: i32 = GBC_PALETTE_LOCATION;\nexport const gameboyColorPaletteSize: i32 = GBC_PALETTE_SIZE;\nexport const bgPriorityMapLocation: i32 = BG_PRIORITY_MAP_LOCATION;\nexport const frameInProgressVideoOutputLocation: i32 = FRAME_LOCATION;\nexport const backgroundMapLocation: i32 = BACKGROUND_MAP_LOCATION;\nexport const tileDataMap: i32 = TILE_DATA_LOCATION;\nexport const oamTiles: i32 = OAM_TILES_LOCATION;\n\n// Sound output\nexport const soundOutputLocation: i32 = AUDIO_BUFFER_LOCATION;\n\n// Game Cartridge\nexport const gameRamBanksLocation: i32 = CARTRIDGE_RAM_LOCATION;\n// Passed in Game backup or ROM from the user\nexport const gameBytesLocation: i32 = CARTRIDGE_ROM_LOCATION;\n"]}