Repository: timoinutilis/lowres-nx Branch: master Commit: 35adc1a215e9 Files: 192 Total size: 1.7 MB Directory structure: gitextract_m0nciqnm/ ├── .gitignore ├── .gitlab-ci.yml ├── LICENSE ├── README.md ├── assets/ │ ├── NX App Design.pxm │ ├── NX Logo.sketch │ ├── characters.pxm │ ├── fonts.pxm │ ├── old/ │ │ ├── App Icon.sketch │ │ ├── background_designer_sketch.pxm │ │ ├── character_designer_sketch.pxm │ │ ├── console_pixel.pxm │ │ ├── logo.pxm │ │ └── mobile_app_icon.pxm │ └── overlay.nx ├── core/ │ ├── accessories/ │ │ ├── disk_drive.c │ │ └── disk_drive.h │ ├── boot_intro.c │ ├── boot_intro.h │ ├── core.c │ ├── core.h │ ├── core_delegate.c │ ├── core_delegate.h │ ├── core_stats.c │ ├── core_stats.h │ ├── datamanager/ │ │ ├── data_manager.c │ │ └── data_manager.h │ ├── interpreter/ │ │ ├── charsets.c │ │ ├── charsets.h │ │ ├── cmd_audio.c │ │ ├── cmd_audio.h │ │ ├── cmd_background.c │ │ ├── cmd_background.h │ │ ├── cmd_control.c │ │ ├── cmd_control.h │ │ ├── cmd_data.c │ │ ├── cmd_data.h │ │ ├── cmd_files.c │ │ ├── cmd_files.h │ │ ├── cmd_io.c │ │ ├── cmd_io.h │ │ ├── cmd_maths.c │ │ ├── cmd_maths.h │ │ ├── cmd_memory.c │ │ ├── cmd_memory.h │ │ ├── cmd_screen.c │ │ ├── cmd_screen.h │ │ ├── cmd_sprites.c │ │ ├── cmd_sprites.h │ │ ├── cmd_strings.c │ │ ├── cmd_strings.h │ │ ├── cmd_subs.c │ │ ├── cmd_subs.h │ │ ├── cmd_text.c │ │ ├── cmd_text.h │ │ ├── cmd_variables.c │ │ ├── cmd_variables.h │ │ ├── data.c │ │ ├── data.h │ │ ├── error.c │ │ ├── error.h │ │ ├── interpreter.c │ │ ├── interpreter.h │ │ ├── interpreter_config.h │ │ ├── interpreter_utils.c │ │ ├── interpreter_utils.h │ │ ├── labels.c │ │ ├── labels.h │ │ ├── rcstring.c │ │ ├── rcstring.h │ │ ├── string_utils.c │ │ ├── string_utils.h │ │ ├── token.c │ │ ├── token.h │ │ ├── tokenizer.c │ │ ├── tokenizer.h │ │ ├── value.c │ │ ├── value.h │ │ ├── variables.c │ │ └── variables.h │ ├── libraries/ │ │ ├── audio_lib.c │ │ ├── audio_lib.h │ │ ├── default_characters.c │ │ ├── default_characters.h │ │ ├── sprites_lib.c │ │ ├── sprites_lib.h │ │ ├── startup_sequence.c │ │ ├── startup_sequence.h │ │ ├── text_lib.c │ │ └── text_lib.h │ ├── machine/ │ │ ├── audio_chip.c │ │ ├── audio_chip.h │ │ ├── io_chip.h │ │ ├── machine.c │ │ ├── machine.h │ │ ├── video_chip.c │ │ └── video_chip.h │ └── overlay/ │ ├── overlay.c │ ├── overlay.h │ ├── overlay_data.c │ └── overlay_data.h ├── docs/ │ ├── license.txt │ ├── manual.html │ └── readme.txt ├── extras/ │ └── LowRes NX.sublime-syntax ├── libretro/ │ ├── libretro.h │ ├── libretro_main.c │ └── libretro_main.h ├── platform/ │ ├── GameShell/ │ │ ├── README.md │ │ └── home/ │ │ └── cpi/ │ │ ├── apps/ │ │ │ └── Menu/ │ │ │ └── 55_LowRes NX/ │ │ │ └── action.config │ │ └── games/ │ │ └── LowResNX/ │ │ ├── LowRes Galaxy 2 (1.5).nx │ │ └── Star Scroller 1.2.nx │ ├── LibRetro/ │ │ ├── Makefile │ │ ├── Makefile.common │ │ ├── jni/ │ │ │ ├── Android.mk │ │ │ └── Application.mk │ │ └── link.T │ ├── Linux/ │ │ ├── README.md │ │ └── makefile │ ├── Windows/ │ │ ├── LowRes NX Win/ │ │ │ ├── LowRes NX Win.rc │ │ │ ├── LowRes NX Win.vcxproj │ │ │ ├── LowRes NX Win.vcxproj.filters │ │ │ ├── resource.h │ │ │ └── resource1.h │ │ └── LowRes NX Win.sln │ ├── iOS/ │ │ ├── LowRes NX iOS/ │ │ │ ├── AppDelegate.swift │ │ │ ├── Assets.xcassets/ │ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ ├── Base.lproj/ │ │ │ │ ├── LaunchScreen.storyboard │ │ │ │ └── Main.storyboard │ │ │ ├── CoreWrapper.swift │ │ │ ├── Info.plist │ │ │ ├── LowResNX-Bridging-Header.h │ │ │ ├── LowResNXView.swift │ │ │ └── ViewController.swift │ │ ├── LowRes NX iOS.xcodeproj/ │ │ │ ├── project.pbxproj │ │ │ └── project.xcworkspace/ │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata/ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── program.nx │ ├── macOS/ │ │ ├── LowRes NX macOS/ │ │ │ ├── Assets.xcassets/ │ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ ├── Info.plist │ │ │ └── LowRes_NX_macOS_SDL.entitlements │ │ └── LowRes NX macOS.xcodeproj/ │ │ ├── project.pbxproj │ │ └── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── IDEWorkspaceChecks.plist │ └── web/ │ ├── README.md │ ├── embed/ │ │ ├── package/ │ │ │ ├── LowResNX120.js │ │ │ ├── LowResNX120.wasm │ │ │ ├── index.html │ │ │ └── program.nx │ │ └── readme.txt │ └── makefile ├── programs/ │ ├── Gfx Designer 2.0.nx │ ├── LowRes Adventure 1.1.nx │ ├── LowRes Galaxy 2 (1.5).nx │ ├── Sound Composer 1.5.nx │ └── Star Scroller 1.2.nx ├── programs test/ │ ├── BG Scroll.nx │ ├── Crazy Text.nx │ ├── Demo 0.5.nx │ ├── Files.nx │ ├── Scrolling Map 0.3.nx │ ├── Slide Show 0.4.nx │ ├── Sprites with Background 0.3.nx │ ├── Subs 1.nx │ ├── Subs 2.nx │ ├── drawing.nx │ ├── gamepad.nx │ ├── hello world.nx │ ├── sprite collision.nx │ └── touch.nx ├── scripts/ │ ├── export_characters.py │ └── export_characters_hex.py └── sdl/ ├── config.h ├── dev_menu.c ├── dev_menu.h ├── dev_menu_data.h ├── main.c ├── main.h ├── runner.c ├── runner.h ├── screenshot.c ├── screenshot.h ├── sdl_include.h ├── settings.c ├── settings.h ├── stb_image_write.h ├── system_paths.c ├── system_paths.h ├── utils.c └── utils.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore ## Build generated build/ DerivedData/ ## Various settings *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata/ ## Other *.moved-aside *.xcuserstate ## Obj-C/Swift specific *.hmap *.ipa *.dSYM.zip *.dSYM # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # # Pods/ # Carthage # # Add this line if you want to avoid checking in source code from Carthage dependencies. # Carthage/Checkouts Carthage/Build # fastlane # # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the # screenshots whenever they are needed. # For more information about the recommended setup visit: # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md fastlane/report.xml fastlane/screenshots #Code Injection # # After new code Injection tools there's a generated folder /iOSInjectionProject # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ platform/Windows/LowRes NX Win/Debug/ platform/Windows/LowRes NX Win/Release/ platform/Linux/output/ platform/Windows/Debug/ platform/Windows/Release/ platform/Windows/LowRes NX Win/SDL2.dll platform/Windows/.vs/ *.user *.aps *.bc platform/web/output Disk.nx *.o ================================================ FILE: .gitlab-ci.yml ================================================ # DESCRIPTION: GitLab CI/CD for libRetro ############################################################################## ################################# BOILERPLATE ################################ ############################################################################## # Core definitions .core-defs: variables: CORENAME: lowresnx MAKEFILE_PATH: platform/LibRetro JNI_PATH: platform/LibRetro # Inclusion templates, required for the build to work include: ################################## DESKTOPS ################################ # Windows 64-bit - project: 'libretro-infrastructure/ci-templates' file: '/windows-x64-mingw.yml' # Windows 32-bit - project: 'libretro-infrastructure/ci-templates' file: '/windows-i686-mingw.yml' # Linux 64-bit - project: 'libretro-infrastructure/ci-templates' file: '/linux-x64.yml' # Linux 32-bit - project: 'libretro-infrastructure/ci-templates' file: '/linux-i686.yml' # Linux 64-bit (ARM) - project: 'libretro-infrastructure/ci-templates' file: '/linux-aarch64.yml' # MacOS PowerPC 32-bit - project: 'libretro-infrastructure/ci-templates' file: '/osx-ppc.yml' # MacOS 64-bit - project: 'libretro-infrastructure/ci-templates' file: '/osx-x64.yml' # MacOS ARM 64-bit - project: 'libretro-infrastructure/ci-templates' file: '/osx-arm64.yml' # DJGPP - project: 'libretro-infrastructure/ci-templates' file: '/djgpp-static.yml' ################################## CELLULAR ################################ # Android - project: 'libretro-infrastructure/ci-templates' file: '/android-jni.yml' # iOS - project: 'libretro-infrastructure/ci-templates' file: '/ios-arm64.yml' # iOS (armv7) - project: 'libretro-infrastructure/ci-templates' file: '/ios9.yml' ################################## CONSOLES ################################ # PlayStation Portable - project: 'libretro-infrastructure/ci-templates' file: '/psp-static.yml' # PlayStation Vita - project: 'libretro-infrastructure/ci-templates' file: '/vita-static.yml' # PlayStation2 - project: 'libretro-infrastructure/ci-templates' file: '/ps2-static.yml' # Nintendo 3DS - project: 'libretro-infrastructure/ci-templates' file: '/ctr-static.yml' # Nintendo GameCube - project: 'libretro-infrastructure/ci-templates' file: '/ngc-static.yml' # Nintendo Wii - project: 'libretro-infrastructure/ci-templates' file: '/wii-static.yml' # Nintendo WiiU - project: 'libretro-infrastructure/ci-templates' file: '/wiiu-static.yml' # Nintendo Switch - project: 'libretro-infrastructure/ci-templates' file: '/libnx-static.yml' # tvOS (AppleTV) - project: 'libretro-infrastructure/ci-templates' file: '/tvos-arm64.yml' # OpenDingux - project: 'libretro-infrastructure/ci-templates' file: '/dingux-mips32.yml' # OpenDingux (ARM) - project: 'libretro-infrastructure/ci-templates' file: '/dingux-arm32.yml' #################################### MISC ################################## # Emscripten - project: 'libretro-infrastructure/ci-templates' file: '/emscripten-static.yml' # Stages for building stages: - build-prepare - build-shared - build-static ############################################################################## #################################### STAGES ################################## ############################################################################## # ################################### DESKTOPS ################################# # Windows 64-bit libretro-build-windows-x64: extends: - .libretro-windows-x64-mingw-make-default - .core-defs # Windows 32-bit libretro-build-windows-i686: extends: - .libretro-windows-i686-mingw-make-default - .core-defs # Linux 64-bit libretro-build-linux-x64: extends: - .libretro-linux-x64-make-default - .core-defs # Linux 32-bit libretro-build-linux-i686: extends: - .libretro-linux-i686-make-default - .core-defs # Linux 64-bit (ARM) libretro-build-linux-aarch64: extends: - .libretro-linux-aarch64-make-default - .core-defs # DJGPP libretro-build-djgpp-i586: extends: - .libretro-djgpp-static-retroarch-master - .core-defs # MacOS 64-bit libretro-build-osx-x64: extends: - .libretro-osx-x64-make-default - .core-defs # MacOS ARM 64-bit libretro-build-osx-arm64: extends: - .libretro-osx-arm64-make-default - .core-defs # MacOS PowerPC 32-bit libretro-build-osx-ppc: extends: - .libretro-osx-ppc-make-default - .core-defs ################################### CELLULAR ################################# # Android ARMv7a android-armeabi-v7a: extends: - .libretro-android-jni-armeabi-v7a - .core-defs # Android ARMv8a android-arm64-v8a: extends: - .libretro-android-jni-arm64-v8a - .core-defs # Android 64-bit x86 android-x86_64: extends: - .libretro-android-jni-x86_64 - .core-defs # Android 32-bit x86 android-x86: extends: - .libretro-android-jni-x86 - .core-defs # iOS libretro-build-ios-arm64: extends: - .libretro-ios-arm64-make-default - .core-defs # iOS (armv7) [iOS 9 and up] libretro-build-ios9: extends: - .libretro-ios9-make-default - .core-defs # tvOS libretro-build-tvos-arm64: extends: - .libretro-tvos-arm64-make-default - .core-defs ################################### CONSOLES ################################# # PlayStation Portable libretro-build-psp: extends: - .libretro-psp-static-retroarch-master - .core-defs # PlayStation Vita libretro-build-vita: extends: - .libretro-vita-static-retroarch-master - .core-defs # PlayStation2 libretro-build-ps2: extends: - .libretro-ps2-static-retroarch-master - .core-defs # Nintendo 3DS libretro-build-ctr: extends: - .libretro-ctr-static-retroarch-master - .core-defs # Nintendo GameCube libretro-build-ngc: extends: - .libretro-ngc-static-retroarch-master - .core-defs # Nintendo Wii libretro-build-wii: extends: - .libretro-wii-static-retroarch-master - .core-defs # Nintendo WiiU libretro-build-wiiu: extends: - .libretro-wiiu-static-retroarch-master - .core-defs # Nintendo Switch libretro-build-libnx-aarch64: extends: - .libretro-libnx-static-retroarch-master - .core-defs # OpenDingux libretro-build-dingux-mips32: extends: - .libretro-dingux-mips32-make-default - .core-defs # OpenDingux Beta libretro-build-dingux-odbeta-mips32: extends: - .libretro-dingux-odbeta-mips32-make-default - .core-defs # OpenDingux Beta libretro-build-rs90-odbeta-mips32: extends: - .libretro-rs90-odbeta-mips32-make-default - .core-defs # RetroFW libretro-build-retrofw-mips32: extends: - .libretro-retrofw-mips32-make-default - .core-defs # Miyoo libretro-build-miyoo-arm32: extends: - .libretro-miyoo-arm32-make-default - .core-defs #################################### MISC ################################## # Emscripten libretro-build-emscripten: extends: - .libretro-emscripten-static-retroarch-master - .core-defs ================================================ FILE: LICENSE ================================================ Copyright 2016-2021 Timo Kloss This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ================================================ FILE: README.md ================================================ # LowRes NX ## Program retro games in BASIC Make your own retro games on a virtual game console. Program in the classic BASIC language and create sprites, tile maps, sound and music with the included tools. As a beginner you will quickly understand how to create simple text games or show your first sprite on a tile map. As an experienced programmer you can discover the full potential of retro hardware tricks! ## Virtual Game Console Imagine LowRes NX as a handheld game console with a d-pad, two action buttons and a little rubber keyboard below a slidable touchscreen. LowRes NX was inspired by real 8- and 16-bit systems and simulates chips for graphics, sound and I/O, which actually work like classic hardware. It supports hardware sprites as well as hardware parallax scrolling, and even offers vertical blank and raster interrupts to create authentic retro effects. ## Old-School Programming The programming language of LowRes NX is based on second-generation, structured BASIC. It offers all the classic commands, but with labels, loops and subprograms instead of line numbers. Graphics and sound are supported by additional commands and you can even access the virtual hardware directly using PEEK and POKE. You have complete control over the program flow, there is no standard update function to implement. ## Creative Tools LowRes NX includes all the tools you need: The Gfx Designer for editing sprites, tiles, fonts and maps, as well as the Sound Composer for music and sound effects. All of these are just normal BASIC programs. You can change and improve them or even create your own custom editors. ## Share and Play Send your games directly to other users or share them via the website. All programs are open source, so you can play them, learn from them and edit them. Do you prefer making just art or music? Share your creations as assets and let other programmers use them in their projects. ================================================ FILE: assets/overlay.nx ================================================ #2:CHARS 00000000000000000000000000000000 3C3C3C3C3C3C3C3C3C242424243C243C FEFEFEFE7E000000FE9292DA7E000000 7EFFFFFFFFFFFF7E7EDB81DBDB81DB7E 1C7F7F7F7F7F7F1C1C7741477141771C F7FFFFFE7FFFFFEFF79D9BF66FD9B9EF 3E7E7EFFFFFFFF7F3E624AC7919BC57F 3C3C7C7C780000003C24644C78000000 1E3E7E7C7C7E3E1E1E32664C4C66321E 787C7E3E3E7E7C78784C663232664C78 007E7EFFFFFF7E7E007E5AE781E75A7E 003C3CFFFFFF3C3C003C24E781E7243C 0000003C3C7C7C780000003C24644C78 000000FFFFFF0000000000FF81FF0000 000000003C3C3C3C000000003C24243C 0F1F3F7EFCF8F0E00F193366CC98B0E0 7EFFFFFFFFFFFF7E7EC399918999C37E 3C7C7C7C3CFFFFFF3C64446424E781FF 7EFFFFFF7EFFFFFF7EC399F366CF81FF 7EFFFFFFFFFFFF7E7EC399F3F999C37E FFFFFFFFFF0F0F0FFF999981F909090F FFFFFFFFFFFFFFFEFF819F83F9F983FE 3E7EFEFFFFFFFF7E3E62CE839999C37E FFFFFF3F7E7C7878FF81F933664C4878 7EFFFFFFFFFFFF7E7EC399C39999C37E 7EFFFFFFFFFFFF7E7EC399C1F999C37E 00003C3C3C3C3C0000003C243C243C00 00003C3C3C7C7C7800003C243C644C78 001E3E7E7C7E3E1E001E32664C66321E 0000FFFFFFFFFF000000FF81FF81FF00 00787C7E3E7E7C7800784C6632664C78 7EFFFFFF3E3C3C3C7EC399F3263C243C 7EFFFFFFFFFFFE7E7EC39991919FC27E 3C7EFFFFFFFFFFFF3C66C399819999FF FEFFFFFFFFFFFFFEFE839983999983FE 7EFFFFFFFFFFFF7E7EC3999F9F99C37E FCFEFFFFFFFFFEFCFC869399999386FC FFFFFFFCFCFFFFFFFF819F849C9F81FF FFFFFFFCFCF0F0F0FF819F849C9090F0 7EFEFFFFFFFFFF7E7EC29F919999C37E FFFFFFFFFFFFFFFFFF999981999999FF 7E7E7E3C3C7E7E7E7E4266242466427E 3F3F3F0FFFFFFF7E3F213909F999C37E FFFFFFFEFEFFFFFFFF999386869399FF F0F0F0F0F0FFFFFFF0909090909F81FF E7FFFFFFFFFFFFFFE7BD9981819999FF FFFFFFFFFFFFFFFFFF998981919999FF 7EFFFFFFFFFFFF7E7EC399999999C37E FEFFFFFFFEF0F0F0FE8399839E9090F0 7EFFFFFFFFFFFF7F7EC399999593C17F FEFFFFFFFEFFFFFFFE839983869399FF 7FFFFFFF7FFFFFFE7FC19FC379F983FE FFFFFF3C3C3C3C3CFF81E7242424243C FFFFFFFFFFFFFF7EFF9999999999C37E FFFFFFFFFFFF7E3CFF99999999C3663C FFFFFFFFFFFFFFE7FF9999818199BDE7 FFFFFF7EFFFFFFFFFF99C366C39999FF FFFFFFFF7E3C3C3CFF9999C36624243C FFFFFF7EFCFFFFFFFF81F366CC9F81FF 7EFFFFFFFFFFFF7E7EC399A5BDA5C37E 7EFFFFFFFFFFFF7E7EC3A1B9A5B9C37E 3C7EFFFFFFFF7E3C3C66E78181E7663C 3C7EFFFFFF0000003C66C399FF000000 0000000000FFFFFF0000000000FF81FF ================================================ FILE: core/accessories/disk_drive.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "disk_drive.h" #include "core.h" #include #include #include #include void disk_init(struct Core *core) { // init lazily in disk_prepare() } void disk_deinit(struct Core *core) { struct DataManager *dataManager = &core->diskDrive->dataManager; if (dataManager->data) { free(dataManager->data); dataManager->data = NULL; } data_deinit(dataManager); } void disk_reset(struct Core *core) { struct DataManager *dataManager = &core->diskDrive->dataManager; if (dataManager->data) { data_reset(dataManager); } } bool disk_prepare(struct Core *core) { struct DataManager *dataManager = &core->diskDrive->dataManager; if (dataManager->data == NULL) { dataManager->data = calloc(DATA_SIZE, 1); if (!dataManager->data) exit(EXIT_FAILURE); data_init(dataManager); } return delegate_diskDriveWillAccess(core); } bool disk_saveFile(struct Core *core, int index, char *comment, int address, int length) { if (!disk_prepare(core)) { return false; } assert(address >= 0 && address + length <= sizeof(struct Machine)); struct DataManager *dataManager = &core->diskDrive->dataManager; if (!data_canSetEntry(dataManager, index, length)) { delegate_diskDriveIsFull(core); } else { uint8_t *source = &((uint8_t *)core->machine)[address]; data_setEntry(dataManager, index, comment, source, length); delegate_diskDriveDidSave(core); } return true; } bool disk_loadFile(struct Core *core, int index, int address, int maxLength, int offset, bool *pokeFailed) { if (!disk_prepare(core)) { return false; } struct DataEntry *entry = &core->diskDrive->dataManager.entries[index]; uint8_t *data = core->diskDrive->dataManager.data; // read file int start = entry->start + offset; int length = entry->length; if (maxLength > 0 && length > maxLength) { length = maxLength; } if (offset + length > entry->length) { length = entry->length - offset; } for (int i = 0; i < length; i++) { bool poke = machine_poke(core, address + i, data[i + start]); if (!poke) { *pokeFailed = true; return true; } } return true; } ================================================ FILE: core/accessories/disk_drive.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef disk_drive_h #define disk_drive_h #include #include #include "data_manager.h" struct Core; struct DiskDrive { struct DataManager dataManager; }; void disk_init(struct Core *core); void disk_deinit(struct Core *core); void disk_reset(struct Core *core); bool disk_prepare(struct Core *core); bool disk_saveFile(struct Core *core, int index, char *comment, int address, int length); bool disk_loadFile(struct Core *core, int index, int address, int maxLength, int offset, bool *pokeFailed); #endif /* disk_drive_h */ ================================================ FILE: core/boot_intro.c ================================================ // // Copyright 2017-2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "boot_intro.h" #include "core.h" const int bootIntroStateAddress = 0xA000; const char *bootIntroSourceCode = "VER$=\"" CORE_VERSION "\"\n\nGLOBAL JIN\n\nFONT 64\n\nENVELOPE 0,0,10,0,6\nENVELOPE 1,0,10,0,6\nENVELOPE 2,0,7,0,7\nENVELOPE 3,0,7,0,7\nLFO 0,5,4,0,0\nLFO 1,5,4,0,0\nVOLUME 0,15,%10\nVOLUME 1,15,%01\nVOLUME 2,15,%10\nVOLUME 3,15,%01\n\nSPRITE.A 0,(3,0,0,0,1)\nSPRITE.A 1,(0,0,0,0,1)\nSPRITE.A 2,(0,0,0,0,3)\nSPRITE.A 3,(1,0,0,0,0)\nSPRITE.A 4,(2,0,0,0,1)\n\nSPRITE 1,72,56,5\nSPRITE 2,64,56,1\n\nPAL 2\nCELL 0,15,39\nCELL 1,15,40\nCELL 2,15,41\nCELL 3,15,42\nCELL 4,15,43\nCELL 5,15,44\n\nTEXT 20-LEN(VER$),15,VER$\n\n\nDO\n SPRITE 3,76,40,7\n WAIT 30\n FOR Y=40 TO 56\n SPRITE 3,76,Y,7\n WAIT 2\n NEXT Y\n FOR I=1 TO 30\n WAIT VBL\n IF PEEK($A000)=1 THEN GOTO LOADING\n NEXT I\nLOOP\n\nLOADING:\nON VBL CALL JINGLE\nFOR Y=56 TO 49 STEP -1\n SPRITE 0,72,Y,37\n SPRITE 1,,Y,\n WAIT 4\nNEXT Y\nWAIT 60\n\nPOKE $A000,2\nDO\n N=N+1\n SPRITE 4,76,80,8+(N MOD 4)*2\n WAIT 10\nLOOP\n\nSUB JINGLE\n IF JIN=0 THEN\n PLAY 0,52,0\n PLAY 1,48,0\n ELSE IF JIN=15 THEN\n STOP\n PLAY 2,50,1\n PLAY 3,46,1\n ELSE IF JIN=30 THEN\n ON VBL OFF\n END IF\n JIN=JIN+1\nEND SUB\n\n#1:MAIN PALETTES\n0A2A150030381500003F2F0F003F0A34\n003F2A15003F2A15003F2A15003F2A15\n\n#2:MAIN CHARACTERS\n00000000000000000000000000000000\n3F7FC0809CBEBEBE3F40BFFFFFFFFFFF\nFF80808080808080FFFFFFFFFFFFFFFF\nFF01010101010101FFFFFFFFFFFFFFFF\nFCFE0321210D6D61FC02FDDFFFFFFFFF\n7FFF809F9F9F9F9F7F80FFFFFFFFFFFF\nFEFF01F9F9F9F9F9FE01FFFFFFFFFFFF\n7EBDBDBDBD8181FF7EC3DBC3C3FFFFFF\n08090200C100204800412200C1002241\n00000000800000000000000080000000\n00412200C100224108482000C1000209\n00000000800000000000000080000000\n08482000C10002090849220000002249\n00000000800000000000000000000000\n084922000000224908090200C1002048\n00000000000000000000000080000000\n00000000000000000000000000000000\n9C8088948880403FFFFFFFFFFFFF7F3F\n80B6929B89B692FFFFC9FFE4FFC9FFFF\n01D9496D25D949FFFF27FF93FF27FFFF\n01011129110102FCFFFFFFFFFFFFFEFC\n9F9F9F80FFFF80FFFFFFFFFFFF80FFFF\nF9F9F901FFFF01FFFFFFFFFFFF01FFFF\n00000000000000000000000000000000\n08000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000800000000000000\n00000000000000000000000000000000\n08000000000000000800000000000000\n00000000000000000000000000000000\n08000000000000000800000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n0000000000000301000000000F0F0C0F\n000000000000C08000000000F0F030F0\n00004344444473000000434444447300\n000022A2AAB62200000022A2AAB62200\n0000E794E79497000000E794E7949700\n00009C201804B80000009C201804B800\n00004564544C450000004564544C4500\n000010A040A01000000010A040A01000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000F0F000000000000\n0000000000000000F0F0000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00181818180018000000000000000000\n006C6C24000000000000000000000000\n00247E24247E24000000000000000000\n00083E380E3E08000000000000000000\n00626408102646000000000000000000\n001C34386E643A000000000000000000\n00181830000000000000000000000000\n00000408080804000000040808080400\n00001008080810000000100808081000\n000024187E1824000000000000000000\n000018187E1818000000000000000000\n00000000181830000000000000000000\n000000007E0000000000000000000000\n00000000000010000000000000001000\n00060C18306040000000000000000000\n00001C2222221C0000001C2222221C00\n0000380808083E000000380808083E00\n00003C021C203E0000003C021C203E00\n00003C021C023C0000003C021C023C00\n00002020283E080000002020283E0800\n00003E203C023C0000003E203C023C00\n00001C203C221C0000001C203C221C00\n00003E040810200000003E0408102000\n00001C221C221C0000001C221C221C00\n00001C221E023C0000001C221E023C00\n00000018001800000000000000000000\n00000018001830000000000000000000\n00000C1830180C000000000000000000\n0000007E007E00000000000000000000\n000030180C1830000000000000000000\n003C660C180018000000000000000000\n003C666E6E603C000000000000000000\n00183C667E6666000000000000000000\n007C667C66667C000000000000000000\n003C666060663C000000000000000000\n00786C66666C78000000000000000000\n007E607860607E000000000000000000\n007E6078606060000000000000000000\n003C606E66663C000000000000000000\n0066667E666666000000000000000000\n003C181818183C000000000000000000\n001E060606663C000000000000000000\n00666C78786C66000000000000000000\n0060606060607E000000000000000000\n0042667E7E6666000000000000000000\n0066767E6E6666000000000000000000\n003C666666663C000000000000000000\n007C667C606060000000000000000000\n003C66666A6C3E000000000000000000\n007C667C786C66000000000000000000\n003E603C06067C000000000000000000\n007E1818181818000000000000000000\n0066666666663C000000000000000000\n00666666663C18000000000000000000\n0066667E7E6642000000000000000000\n00663C183C6666000000000000000000\n0066663C181818000000000000000000\n007E0C1830607E000000000000000000\n003C303030303C000000000000000000\n006030180C0602000000000000000000\n003C0C0C0C0C3C000000000000000000\n00183C66000000000000000000000000\n0000000000007E000000000000000000\n"; ================================================ FILE: core/boot_intro.h ================================================ // // Copyright 2017-2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef boot_intro_h #define boot_intro_h extern const int bootIntroStateAddress; extern const char *bootIntroSourceCode; enum BootIntroState { BootIntroStateDefault, BootIntroStateProgramAvailable, BootIntroStateReadyToRun, BootIntroStateDone }; #endif /* boot_intro_h */ ================================================ FILE: core/core.c ================================================ // // Copyright 2016-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "core.h" #include #include #include #include "string_utils.h" #include "startup_sequence.h" const char CoreInputKeyReturn = '\n'; const char CoreInputKeyBackspace = '\b'; const char CoreInputKeyRight = 17; const char CoreInputKeyLeft = 18; const char CoreInputKeyDown = 19; const char CoreInputKeyUp = 20; void core_handleInput(struct Core *core, struct CoreInput *input); void core_init(struct Core *core) { memset(core, 0, sizeof(struct Core)); core->machine = calloc(1, sizeof(struct Machine)); if (!core->machine) exit(EXIT_FAILURE); core->machineInternals = calloc(1, sizeof(struct MachineInternals)); if (!core->machineInternals) exit(EXIT_FAILURE); core->interpreter = calloc(1, sizeof(struct Interpreter)); if (!core->interpreter) exit(EXIT_FAILURE); core->diskDrive = calloc(1, sizeof(struct DiskDrive)); if (!core->diskDrive) exit(EXIT_FAILURE); core->overlay = calloc(1, sizeof(struct Overlay)); if (!core->overlay) exit(EXIT_FAILURE); machine_init(core); itp_init(core); overlay_init(core); disk_init(core); } void core_deinit(struct Core *core) { itp_deinit(core); disk_deinit(core); free(core->machine); core->machine = NULL; free(core->machineInternals); core->machineInternals = NULL; free(core->interpreter); core->interpreter = NULL; free(core->diskDrive); core->diskDrive = NULL; free(core->overlay); core->overlay = NULL; } void core_setDelegate(struct Core *core, struct CoreDelegate *delegate) { core->delegate = delegate; } struct CoreError core_compileProgram(struct Core *core, const char *sourceCode, bool resetPersistent) { machine_reset(core, resetPersistent); overlay_reset(core); disk_reset(core); return itp_compileProgram(core, sourceCode); } void core_traceError(struct Core *core, struct CoreError error) { core->interpreter->debug = false; struct TextLib *lib = &core->overlay->textLib; txtlib_printText(lib, err_getString(error.code)); txtlib_printText(lib, "\n"); if (error.sourcePosition >= 0 && core->interpreter->sourceCode) { int number = lineNumber(core->interpreter->sourceCode, error.sourcePosition); char lineNumberText[30]; sprintf(lineNumberText, "IN LINE %d:\n", number); txtlib_printText(lib, lineNumberText); const char *line = lineString(core->interpreter->sourceCode, error.sourcePosition); if (line) { txtlib_printText(lib, line); txtlib_printText(lib, "\n"); free((void *)line); } } } void core_willRunProgram(struct Core *core, long secondsSincePowerOn) { runStartupSequence(core); core->interpreter->timer = (float)(secondsSincePowerOn * 60 % TIMER_WRAP_VALUE); machine_suspendEnergySaving(core, 30); delegate_controlsDidChange(core); } void core_update(struct Core *core, struct CoreInput *input) { core_handleInput(core, input); itp_runInterrupt(core, InterruptTypeVBL); itp_runProgram(core); itp_didFinishVBL(core); overlay_draw(core, true); audio_bufferRegisters(core); } void core_handleInput(struct Core *core, struct CoreInput *input) { struct IORegisters *ioRegisters = &core->machine->ioRegisters; union IOAttributes ioAttr = ioRegisters->attr; bool processedOtherInput = false; if (input->key != 0) { if (ioAttr.keyboardEnabled) { char key = input->key; if ( (key >= 32 && key < 127) || key == CoreInputKeyBackspace || key == CoreInputKeyReturn || key == CoreInputKeyDown || key == CoreInputKeyUp || key == CoreInputKeyRight || key == CoreInputKeyLeft ) { ioRegisters->key = key; } } input->key = 0; machine_suspendEnergySaving(core, 2); } if (input->touch) { if (ioAttr.touchEnabled) { ioRegisters->status.touch = 1; int x = input->touchX; int y = input->touchY; if (x < 0) x = 0; else if (x >= SCREEN_WIDTH) x = SCREEN_WIDTH - 1; if (y < 0) y = 0; else if (y >= SCREEN_HEIGHT) y = SCREEN_HEIGHT - 1; ioRegisters->touchX = x; ioRegisters->touchY = y; } else { ioRegisters->status.touch = 0; } machine_suspendEnergySaving(core, 2); } else { ioRegisters->status.touch = 0; } for (int i = 0; i < NUM_GAMEPADS; i++) { union Gamepad *gamepad = &ioRegisters->gamepads[i]; if (ioAttr.gamepadsEnabled > i && !ioAttr.keyboardEnabled) { struct CoreInputGamepad *inputGamepad = &input->gamepads[i]; gamepad->up = inputGamepad->up && !inputGamepad->down; gamepad->down = inputGamepad->down && !inputGamepad->up; gamepad->left = inputGamepad->left && !inputGamepad->right; gamepad->right = inputGamepad->right && !inputGamepad->left; gamepad->buttonA = inputGamepad->buttonA; gamepad->buttonB = inputGamepad->buttonB; if (inputGamepad->up || inputGamepad->down || inputGamepad->left || inputGamepad->right) { // some d-pad combinations are not registered as I/O, but mark them anyway. processedOtherInput = true; } if (gamepad->value) { machine_suspendEnergySaving(core, 2); } } else { gamepad->value = 0; } } if (input->pause) { if (core->interpreter->state == StatePaused) { core->interpreter->state = StateEvaluate; overlay_updateState(core); processedOtherInput = true; } else if (ioAttr.gamepadsEnabled > 0 && !ioAttr.keyboardEnabled) { ioRegisters->status.pause = 1; } input->pause = false; } input->out_hasUsedInput = processedOtherInput || ioRegisters->key || ioRegisters->status.value || ioRegisters->gamepads[0].value || ioRegisters->gamepads[1].value; } void core_willSuspendProgram(struct Core *core) { if (core->machineInternals->hasChangedPersistent) { delegate_persistentRamDidChange(core, core->machine->persistentRam, PERSISTENT_RAM_SIZE); core->machineInternals->hasChangedPersistent = false; } } void core_setDebug(struct Core *core, bool enabled) { core->interpreter->debug = enabled; overlay_updateState(core); } bool core_getDebug(struct Core *core) { return core->interpreter->debug; } bool core_isKeyboardEnabled(struct Core *core) { return core->machine->ioRegisters.attr.keyboardEnabled; } bool core_shouldRender(struct Core *core) { enum State state = core->interpreter->state; bool shouldRender = (!core->machineInternals->isEnergySaving && state != StateEnd && state != StateNoProgram) || core->machineInternals->energySavingTimer > 0 || core->machineInternals->energySavingTimer % 20 == 0; core->machineInternals->energySavingTimer--; return shouldRender; } void core_setInputGamepad(struct CoreInput *input, int player, bool up, bool down, bool left, bool right, bool buttonA, bool buttonB) { struct CoreInputGamepad *gamepad = &input->gamepads[player]; gamepad->up = up; gamepad->down = down; gamepad->left = left; gamepad->right = right; gamepad->buttonA = buttonA; gamepad->buttonB = buttonB; } void core_diskLoaded(struct Core *core) { core->interpreter->state = StateEvaluate; } ================================================ FILE: core/core.h ================================================ // // Copyright 2016-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef core_h #define core_h #define CORE_VERSION "1.2" #include #include #include "machine.h" #include "overlay.h" #include "interpreter.h" #include "disk_drive.h" #include "core_delegate.h" struct Core { struct Machine *machine; struct MachineInternals *machineInternals; struct Interpreter *interpreter; struct DiskDrive *diskDrive; struct Overlay *overlay; struct CoreDelegate *delegate; }; struct CoreInputGamepad { bool up; bool down; bool left; bool right; bool buttonA; bool buttonB; }; struct CoreInput { struct CoreInputGamepad gamepads[NUM_GAMEPADS]; bool pause; int touchX; int touchY; bool touch; char key; bool out_hasUsedInput; }; extern const char CoreInputKeyReturn; extern const char CoreInputKeyBackspace; extern const char CoreInputKeyRight; extern const char CoreInputKeyLeft; extern const char CoreInputKeyDown; extern const char CoreInputKeyUp; void core_init(struct Core *core); void core_deinit(struct Core *core); void core_setDelegate(struct Core *core, struct CoreDelegate *delegate); struct CoreError core_compileProgram(struct Core *core, const char *sourceCode, bool resetPersistent); void core_traceError(struct Core *core, struct CoreError error); void core_willRunProgram(struct Core *core, long secondsSincePowerOn); void core_update(struct Core *core, struct CoreInput *input); void core_willSuspendProgram(struct Core *core); void core_setDebug(struct Core *core, bool enabled); bool core_getDebug(struct Core *core); bool core_isKeyboardEnabled(struct Core *core); bool core_shouldRender(struct Core *core); void core_setInputGamepad(struct CoreInput *input, int player, bool up, bool down, bool left, bool right, bool buttonA, bool buttonB); void core_diskLoaded(struct Core *core); // for dev mode only: void core_handleInput(struct Core *core, struct CoreInput *input); #endif /* core_h */ ================================================ FILE: core/core_delegate.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include #include "core.h" void delegate_interpreterDidFail(struct Core *core, struct CoreError coreError) { if (core->delegate->interpreterDidFail) { core->delegate->interpreterDidFail(core->delegate->context, coreError); } } bool delegate_diskDriveWillAccess(struct Core *core) { if (core->delegate->diskDriveWillAccess) { return core->delegate->diskDriveWillAccess(core->delegate->context, &core->diskDrive->dataManager); } return true; } void delegate_diskDriveDidSave(struct Core *core) { if (core->delegate->diskDriveDidSave) { core->delegate->diskDriveDidSave(core->delegate->context, &core->diskDrive->dataManager); } } void delegate_diskDriveIsFull(struct Core *core) { if (core->delegate->diskDriveIsFull) { core->delegate->diskDriveIsFull(core->delegate->context, &core->diskDrive->dataManager); } } void delegate_controlsDidChange(struct Core *core) { if (core->delegate->controlsDidChange) { struct ControlsInfo info; union IOAttributes ioAttr = core->machine->ioRegisters.attr; if (ioAttr.keyboardEnabled) { if (core->interpreter->isKeyboardOptional) { info.keyboardMode = KeyboardModeOptional; } else { info.keyboardMode = KeyboardModeOn; } } else { info.keyboardMode = KeyboardModeOff; } info.numGamepadsEnabled = ioAttr.keyboardEnabled ? 0 : ioAttr.gamepadsEnabled; info.isTouchEnabled = ioAttr.touchEnabled; info.isAudioEnabled = core->machineInternals->audioInternals.audioEnabled; core->delegate->controlsDidChange(core->delegate->context, info); } } void delegate_persistentRamWillAccess(struct Core *core, uint8_t *destination, int size) { if (core->delegate->persistentRamWillAccess) { core->delegate->persistentRamWillAccess(core->delegate->context, destination, size); } } void delegate_persistentRamDidChange(struct Core *core, uint8_t *data, int size) { if (core->delegate->persistentRamDidChange) { core->delegate->persistentRamDidChange(core->delegate->context, data, size); } } ================================================ FILE: core/core_delegate.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef core_delegate_h #define core_delegate_h #include "data_manager.h" #include "error.h" struct Core; enum KeyboardMode { KeyboardModeOff, KeyboardModeOn, KeyboardModeOptional }; struct ControlsInfo { enum KeyboardMode keyboardMode; int numGamepadsEnabled; bool isTouchEnabled; bool isAudioEnabled; }; struct CoreDelegate { void *context; /** Called on error */ void (*interpreterDidFail)(void *context, struct CoreError coreError); /** Returns true if the disk is ready, false if not. In case of not, core_diskLoaded must be called when ready. */ bool (*diskDriveWillAccess)(void *context, struct DataManager *diskDataManager); /** Called when a disk data entry was saved */ void (*diskDriveDidSave)(void *context, struct DataManager *diskDataManager); /** Called when a disk data entry was tried to be saved, but the disk is full */ void (*diskDriveIsFull)(void *context, struct DataManager *diskDataManager); /** Called when keyboard or gamepad settings changed */ void (*controlsDidChange)(void *context, struct ControlsInfo controlsInfo); /** Called when persistent RAM will be accessed the first time */ void (*persistentRamWillAccess)(void *context, uint8_t *destination, int size); /** Called when persistent RAM should be saved */ void (*persistentRamDidChange)(void *context, uint8_t *data, int size); }; void delegate_interpreterDidFail(struct Core *core, struct CoreError coreError); bool delegate_diskDriveWillAccess(struct Core *core); void delegate_diskDriveDidSave(struct Core *core); void delegate_diskDriveIsFull(struct Core *core); void delegate_controlsDidChange(struct Core *core); void delegate_persistentRamWillAccess(struct Core *core, uint8_t *destination, int size); void delegate_persistentRamDidChange(struct Core *core, uint8_t *data, int size); #endif /* core_delegate_h */ ================================================ FILE: core/core_stats.c ================================================ // // Copyright 2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "core_stats.h" #include #include #include "string_utils.h" void stats_init(struct Stats *stats) { memset(stats, 0, sizeof(struct Stats)); stats->tokenizer = calloc(1, sizeof(struct Tokenizer)); if (!stats->tokenizer) exit(EXIT_FAILURE); stats->romDataManager = calloc(1, sizeof(struct DataManager)); if (!stats->romDataManager) exit(EXIT_FAILURE); stats->romDataManager->data = calloc(1, DATA_SIZE); if (!stats->romDataManager->data) exit(EXIT_FAILURE); } void stats_deinit(struct Stats *stats) { free(stats->romDataManager->data); stats->romDataManager->data = NULL; free(stats->tokenizer); stats->tokenizer = NULL; free(stats->romDataManager); stats->romDataManager = NULL; } struct CoreError stats_update(struct Stats *stats, const char *sourceCode) { stats->numTokens = 0; stats->romSize = 0; struct CoreError error = err_noCoreError(); const char *upperCaseSourceCode = uppercaseString(sourceCode); if (!upperCaseSourceCode) { error = err_makeCoreError(ErrorOutOfMemory, -1); goto cleanup; } error = tok_tokenizeUppercaseProgram(stats->tokenizer, upperCaseSourceCode); if (error.code != ErrorNone) { goto cleanup; } stats->numTokens = stats->tokenizer->numTokens; struct DataManager *romDataManager = stats->romDataManager; error = data_uppercaseImport(romDataManager, upperCaseSourceCode, false); if (error.code != ErrorNone) { goto cleanup; } stats->romSize = data_currentSize(stats->romDataManager); // add default characters if ROM entry 0 is unused struct DataEntry *entry0 = &romDataManager->entries[0]; if (entry0->length == 0 && (DATA_SIZE - data_currentSize(romDataManager)) >= 1024) { stats->romSize += 1024; } cleanup: tok_freeTokens(stats->tokenizer); if (upperCaseSourceCode) { free((void *)upperCaseSourceCode); } return error; } ================================================ FILE: core/core_stats.h ================================================ // // Copyright 2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef core_stats_h #define core_stats_h #include #include "error.h" #include "tokenizer.h" #include "data_manager.h" struct Stats { struct Tokenizer *tokenizer; struct DataManager *romDataManager; int numTokens; int romSize; }; void stats_init(struct Stats *stats); void stats_deinit(struct Stats *stats); struct CoreError stats_update(struct Stats *stats, const char *sourceCode); #endif /* core_stats_h */ ================================================ FILE: core/datamanager/data_manager.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "data_manager.h" #include "charsets.h" #include #include #include #include "string_utils.h" int data_calcOutputSize(struct DataManager *manager); void data_init(struct DataManager *manager) { data_reset(manager); } void data_deinit(struct DataManager *manager) { assert(manager); if (manager->diskSourceCode) { free((void *)manager->diskSourceCode); manager->diskSourceCode = NULL; } } void data_reset(struct DataManager *manager) { memset(manager->entries, 0, sizeof(struct DataEntry) * MAX_ENTRIES); strcpy(manager->entries[1].comment, "MAIN PALETTES"); strcpy(manager->entries[2].comment, "MAIN CHARACTERS"); strcpy(manager->entries[3].comment, "MAIN BG"); strcpy(manager->entries[15].comment, "MAIN SOUND"); if (manager->diskSourceCode) { free((void *)manager->diskSourceCode); manager->diskSourceCode = NULL; } } struct CoreError data_import(struct DataManager *manager, const char *input, bool keepSourceCode) { assert(manager); assert(input); const char *uppercaseInput = uppercaseString(input); if (!uppercaseInput) return err_makeCoreError(ErrorOutOfMemory, -1); struct CoreError error = data_uppercaseImport(manager, uppercaseInput, keepSourceCode); free((void *)uppercaseInput); return error; } struct CoreError data_uppercaseImport(struct DataManager *manager, const char *input, bool keepSourceCode) { assert(manager); assert(input); data_reset(manager); const char *character = input; uint8_t *currentDataByte = manager->data; uint8_t *endDataByte = &manager->data[DATA_SIZE]; // skip stuff before const char *prevChar = NULL; while (*character && !(*character == '#' && (!prevChar || *prevChar == '\n'))) { prevChar = character; character++; } if (keepSourceCode) { size_t length = (size_t)(character - input); char *diskSourceCode = malloc(length + 1); if (!diskSourceCode) exit(EXIT_FAILURE); stringConvertCopy(diskSourceCode, input, length); manager->diskSourceCode = diskSourceCode; } while (*character) { if (*character == '#') { character++; // entry index int entryIndex = 0; while (*character) { if (strchr(CharSetDigits, *character)) { int digit = (int)*character - (int)'0'; entryIndex *= 10; entryIndex += digit; character++; } else { break; } } if (*character != ':') return err_makeCoreError(ErrorUnexpectedCharacter, (int)(character - input)); character++; if (entryIndex >= MAX_ENTRIES) return err_makeCoreError(ErrorIndexOutOfBounds, (int)(character - input)); struct DataEntry *entry = &manager->entries[entryIndex]; if (entry->length > 0) return err_makeCoreError(ErrorIndexAlreadyDefined, (int)(character - input)); // file comment const char *comment = character; do { character++; } while (*character && *character != '\n' && *character != '\r'); size_t commentLen = (character - comment); if (commentLen >= ENTRY_COMMENT_SIZE) commentLen = ENTRY_COMMENT_SIZE - 1; memset(entry->comment, 0, ENTRY_COMMENT_SIZE); strncpy(entry->comment, comment, commentLen); // binary data uint8_t *startByte = currentDataByte; bool shift = true; int value = 0; while (*character && *character != '#') { char *spos = strchr(CharSetHex, *character); if (spos) { int digit = (int)(spos - CharSetHex); if (shift) { value = digit << 4; } else { value |= digit; if (currentDataByte >= endDataByte) return err_makeCoreError(ErrorRomIsFull, (int)(character - input)); *currentDataByte = value; ++currentDataByte; } shift = !shift; } else if (*character != ' ' && *character != '\t' && *character != '\n' && *character != '\r') { return err_makeCoreError(ErrorUnexpectedCharacter, (int)(character - input)); } character++; } if (!shift) return err_makeCoreError(ErrorSyntax, (int)(character - input)); // incomplete hex value int start = (int)(startByte - manager->data); int length = (int)(currentDataByte - startByte); entry->start = start; entry->length = length; for (int i = entryIndex + 1; i < MAX_ENTRIES; i++) { manager->entries[i].start = entry->start + entry->length; } } else if (*character == ' ' || *character == '\t' || *character == '\n' || *character == '\r') { character++; } else { return err_makeCoreError(ErrorUnexpectedCharacter, (int)(character - input)); } } return err_noCoreError(); } char *data_export(struct DataManager *manager) { assert(manager); size_t outputSize = data_calcOutputSize(manager); if (outputSize > 0) { char *output = malloc(outputSize); if (output) { char *current = output; if (manager->diskSourceCode) { size_t len = strlen(manager->diskSourceCode); if (len > 0) { strcpy(current, manager->diskSourceCode); char endChar = current[len - 1]; current += len; if (endChar != '\n') { // add new line after end of program current[0] = '\n'; current++; } } } for (int i = 0; i < MAX_ENTRIES; i++) { struct DataEntry *entry = &manager->entries[i]; if (entry->length > 0) { sprintf(current, "#%d:%s\n", i, entry->comment); current += strlen(current); int valuesInLine = 0; int pos = 0; uint8_t *entryData = &manager->data[entry->start]; while (pos < entry->length) { sprintf(current, "%02X", entryData[pos]); current += strlen(current); pos++; valuesInLine++; if (pos == entry->length) { sprintf(current, "\n\n"); } else if (valuesInLine == 16) { sprintf(current, "\n"); valuesInLine = 0; } current += strlen(current); } } } } return output; } return NULL; } int data_calcOutputSize(struct DataManager *manager) { int size = 0; for (int i = 0; i < MAX_ENTRIES; i++) { struct DataEntry *entry = &manager->entries[i]; if (entry->length > 0) { size += (i >= 10 ? 4 : 3) + strlen(entry->comment) + 1; // #10:comment\n size += entry->length * 2; // 2x hex letters size += entry->length / 16 + 1; // new line every 16 values size += 1; // new line } } if (manager->diskSourceCode) { size += strlen(manager->diskSourceCode) + 1; // possible new line between program and data } size += 1; // 0-byte return size; } int data_currentSize(struct DataManager *manager) { int size = 0; for (int i = 0; i < MAX_ENTRIES; i++) { size += manager->entries[i].length; } return size; } bool data_canSetEntry(struct DataManager *manager, int index, int length) { int size = 0; for (int i = 0; i < MAX_ENTRIES; i++) { if (i != index) { size += manager->entries[i].length; } } return size + length <= DATA_SIZE; } void data_setEntry(struct DataManager *manager, int index, const char *comment, uint8_t *source, int length) { struct DataEntry *entry = &manager->entries[index]; uint8_t *data = manager->data; // move data of higher entries int nextStart = entry->start + length; assert(nextStart <= DATA_SIZE); if (length > entry->length) // new entry is bigger { int diff = length - entry->length; for (int i = DATA_SIZE - 1; i >= nextStart; i--) { data[i] = data[i - diff]; } } else if (length < entry->length) // new entry is smaller { int diff = entry->length - length; for (int i = nextStart; i < DATA_SIZE - diff; i++) { data[i] = data[i + diff]; } for (int i = DATA_SIZE - diff; i < DATA_SIZE; i++) { data[i] = 0; } } // write new entry strncpy(entry->comment, comment, ENTRY_COMMENT_SIZE); entry->comment[ENTRY_COMMENT_SIZE - 1] = 0; entry->length = length; int start = entry->start; for (int i = 0; i < length; i++) { data[i + start] = source[i]; } // move entry positions for (int i = index + 1; i < MAX_ENTRIES; i++) { struct DataEntry *thisEntry = &manager->entries[i]; struct DataEntry *prevEntry = &manager->entries[i - 1]; thisEntry->start = prevEntry->start + prevEntry->length; } } ================================================ FILE: core/datamanager/data_manager.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef data_manager_h #define data_manager_h #include #include #include #include "error.h" #define MAX_ENTRIES 16 #define DATA_SIZE 0x8000 #define ENTRY_COMMENT_SIZE 32 struct DataEntry { char comment[ENTRY_COMMENT_SIZE]; int start; int length; }; struct DataManager { struct DataEntry entries[MAX_ENTRIES]; uint8_t *data; const char *diskSourceCode; }; void data_init(struct DataManager *manager); void data_deinit(struct DataManager *manager); void data_reset(struct DataManager *manager); struct CoreError data_import(struct DataManager *manager, const char *input, bool keepSourceCode); struct CoreError data_uppercaseImport(struct DataManager *manager, const char *input, bool keepSourceCode); char *data_export(struct DataManager *manager); int data_currentSize(struct DataManager *manager); bool data_canSetEntry(struct DataManager *manager, int index, int length); void data_setEntry(struct DataManager *manager, int index, const char *comment, uint8_t *source, int length); #endif /* data_manager_h */ ================================================ FILE: core/interpreter/charsets.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "charsets.h" const char *CharSetDigits = "0123456789"; const char *CharSetLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_"; const char *CharSetAlphaNum = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"; const char *CharSetHex = "0123456789ABCDEF"; ================================================ FILE: core/interpreter/charsets.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef charsets_h #define charsets_h extern const char *CharSetDigits; extern const char *CharSetLetters; extern const char *CharSetAlphaNum; extern const char *CharSetHex; #endif /* charsets_h */ ================================================ FILE: core/interpreter/cmd_audio.c ================================================ // // Copyright 2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "cmd_audio.h" #include "core.h" #include "interpreter_utils.h" enum ErrorCode cmd_SOUND(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // SOUND ++interpreter->pc; // n value struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1); if (nValue.type == ValueTypeError) return nValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // wave value struct TypedValue waveValue = itp_evaluateOptionalNumericExpression(core, 0, 3); if (waveValue.type == ValueTypeError) return waveValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // pulse width value struct TypedValue pwValue = itp_evaluateOptionalNumericExpression(core, 0, 15); if (pwValue.type == ValueTypeError) return pwValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // length value struct TypedValue lenValue = itp_evaluateOptionalNumericExpression(core, 0, 255); if (lenValue.type == ValueTypeError) return lenValue.v.errorCode; if (interpreter->pass == PassRun) { int n = nValue.v.floatValue; struct Voice *voice = &core->machine->audioRegisters.voices[n]; if (waveValue.type != ValueTypeNull) { voice->attr.wave = waveValue.v.floatValue; } if (pwValue.type != ValueTypeNull) { voice->attr.pulseWidth = pwValue.v.floatValue; } if (lenValue.type != ValueTypeNull) { int len = lenValue.v.floatValue; voice->length = len; voice->attr.timeout = (len > 0) ? 1 : 0; } } return itp_endOfCommand(interpreter); } //enum ErrorCode cmd_SOUND_COPY(struct Core *core) //{ // struct Interpreter *interpreter = core->interpreter; // // // SOUND COPY // ++interpreter->pc; // ++interpreter->pc; // // // sound value // struct TypedValue sValue = itp_evaluateNumericExpression(core, 0, 15); // if (sValue.type == ValueTypeError) return sValue.v.errorCode; // // // TO // if (interpreter->pc->type != TokenTO) return ErrorSyntax; // ++interpreter->pc; // // // voice value // struct TypedValue vValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1); // if (vValue.type == ValueTypeError) return vValue.v.errorCode; // // if (interpreter->pass == PassRun) // { // audlib_copySound(&interpreter->audioLib, interpreter->audioLib.sourceAddress, sValue.v.floatValue, vValue.v.floatValue); // } // // return itp_endOfCommand(interpreter); //} enum ErrorCode cmd_VOLUME(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // VOLUME ++interpreter->pc; // n value struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1); if (nValue.type == ValueTypeError) return nValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // volume value struct TypedValue volValue = itp_evaluateOptionalNumericExpression(core, 0, 15); if (volValue.type == ValueTypeError) return volValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // mix value struct TypedValue mixValue = itp_evaluateOptionalNumericExpression(core, 0, 3); if (mixValue.type == ValueTypeError) return mixValue.v.errorCode; if (interpreter->pass == PassRun) { int n = nValue.v.floatValue; struct Voice *voice = &core->machine->audioRegisters.voices[n]; if (volValue.type != ValueTypeNull) { voice->status.volume = volValue.v.floatValue; } if (mixValue.type != ValueTypeNull) { int mix = mixValue.v.floatValue; voice->status.mix = mix; } } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_ENVELOPE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // ENVELOPE ++interpreter->pc; // n value struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1); if (nValue.type == ValueTypeError) return nValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // attack value struct TypedValue attValue = itp_evaluateOptionalNumericExpression(core, 0, 15); if (attValue.type == ValueTypeError) return attValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // decay value struct TypedValue decValue = itp_evaluateOptionalNumericExpression(core, 0, 15); if (decValue.type == ValueTypeError) return decValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // sustain value struct TypedValue susValue = itp_evaluateOptionalNumericExpression(core, 0, 15); if (susValue.type == ValueTypeError) return susValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // release value struct TypedValue relValue = itp_evaluateOptionalNumericExpression(core, 0, 15); if (relValue.type == ValueTypeError) return relValue.v.errorCode; if (interpreter->pass == PassRun) { int n = nValue.v.floatValue; struct Voice *voice = &core->machine->audioRegisters.voices[n]; if (attValue.type != ValueTypeNull) { voice->envA = attValue.v.floatValue; } if (decValue.type != ValueTypeNull) { voice->envD = decValue.v.floatValue; } if (susValue.type != ValueTypeNull) { voice->envS = susValue.v.floatValue; } if (relValue.type != ValueTypeNull) { voice->envR = relValue.v.floatValue; } } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_LFO(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // LFO ++interpreter->pc; // n value struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1); if (nValue.type == ValueTypeError) return nValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // rate value struct TypedValue rateValue = itp_evaluateOptionalNumericExpression(core, 0, 15); if (rateValue.type == ValueTypeError) return rateValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // osc amount value struct TypedValue oscValue = itp_evaluateOptionalNumericExpression(core, 0, 15); if (oscValue.type == ValueTypeError) return oscValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // vol amount value struct TypedValue volValue = itp_evaluateOptionalNumericExpression(core, 0, 15); if (volValue.type == ValueTypeError) return volValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // pw amount value struct TypedValue pwValue = itp_evaluateOptionalNumericExpression(core, 0, 15); if (pwValue.type == ValueTypeError) return pwValue.v.errorCode; if (interpreter->pass == PassRun) { int n = nValue.v.floatValue; struct Voice *voice = &core->machine->audioRegisters.voices[n]; if (rateValue.type != ValueTypeNull) { voice->lfoFrequency = rateValue.v.floatValue; } if (oscValue.type != ValueTypeNull) { voice->lfoOscAmount = oscValue.v.floatValue; } if (volValue.type != ValueTypeNull) { voice->lfoVolAmount = volValue.v.floatValue; } if (pwValue.type != ValueTypeNull) { voice->lfoPWAmount = pwValue.v.floatValue; } } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_LFO_A(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // LFO.A ++interpreter->pc; // obsolete syntax! // n value struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1); if (nValue.type == ValueTypeError) return nValue.v.errorCode; struct Voice *voice = NULL; if (interpreter->pass == PassRun) { int n = nValue.v.floatValue; voice = &core->machine->audioRegisters.voices[n]; } // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; union LFOAttributes attr; if (voice) attr = voice->lfoAttr; else attr.value = 0; // attr value struct TypedValue attrValue = itp_evaluateLFOAttributes(core, attr); if (attrValue.type == ValueTypeError) return attrValue.v.errorCode; if (interpreter->pass == PassRun) { voice->lfoAttr.value = attrValue.v.floatValue; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_LFO_WAVE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // LFO WAVE ++interpreter->pc; ++interpreter->pc; // n value struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1); if (nValue.type == ValueTypeError) return nValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // wave value struct TypedValue wavValue = itp_evaluateOptionalNumericExpression(core, 0, 3); if (wavValue.type == ValueTypeError) return wavValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // inv value struct TypedValue invValue = itp_evaluateOptionalNumericExpression(core, -1, 1); if (invValue.type == ValueTypeError) return invValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // env value struct TypedValue envValue = itp_evaluateOptionalNumericExpression(core, -1, 1); if (envValue.type == ValueTypeError) return envValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // tri value struct TypedValue triValue = itp_evaluateOptionalNumericExpression(core, -1, 1); if (triValue.type == ValueTypeError) return triValue.v.errorCode; if (interpreter->pass == PassRun) { int n = nValue.v.floatValue; struct Voice *voice = &core->machine->audioRegisters.voices[n]; if (wavValue.type != ValueTypeNull) voice->lfoAttr.wave = wavValue.v.floatValue; if (invValue.type != ValueTypeNull) voice->lfoAttr.invert = invValue.v.floatValue ? 1 : 0; if (envValue.type != ValueTypeNull) voice->lfoAttr.envMode = envValue.v.floatValue ? 1 : 0; if (triValue.type != ValueTypeNull) voice->lfoAttr.trigger = triValue.v.floatValue ? 1 : 0; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_PLAY(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // PLAY ++interpreter->pc; // n value struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1); if (nValue.type == ValueTypeError) return nValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // pitch value struct TypedValue pValue = itp_evaluateNumericExpression(core, 0, 96); if (pValue.type == ValueTypeError) return pValue.v.errorCode; int len = -1; if (interpreter->pc->type == TokenComma) { // comma ++interpreter->pc; // length value struct TypedValue lenValue = itp_evaluateNumericExpression(core, 0, 255); if (lenValue.type == ValueTypeError) return lenValue.v.errorCode; len = lenValue.v.floatValue; } int sound = -1; if (interpreter->pc->type == TokenSOUND) { // SOUND ++interpreter->pc; // length value struct TypedValue sValue = itp_evaluateNumericExpression(core, 0, NUM_SOUNDS - 1); if (sValue.type == ValueTypeError) return sValue.v.errorCode; sound = sValue.v.floatValue; } if (interpreter->pass == PassRun) { audlib_play(&core->interpreter->audioLib, nValue.v.floatValue, pValue.v.floatValue, len, sound); } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_STOP(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // STOP ++interpreter->pc; // n value struct TypedValue nValue = itp_evaluateOptionalNumericExpression(core, 0, NUM_VOICES - 1); if (nValue.type == ValueTypeError) return nValue.v.errorCode; if (interpreter->pass == PassRun) { if (nValue.type != ValueTypeNull) { int n = nValue.v.floatValue; audlib_stopVoice(&interpreter->audioLib, n); } else { audlib_stopAll(&interpreter->audioLib); } } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_MUSIC(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // MUSIC ++interpreter->pc; // pattern value struct TypedValue pValue = itp_evaluateOptionalNumericExpression(core, 0, NUM_PATTERNS - 1); if (pValue.type == ValueTypeError) return pValue.v.errorCode; if (interpreter->pass == PassRun) { int startPattern = (pValue.type != ValueTypeNull) ? pValue.v.floatValue : 0; audlib_playMusic(&interpreter->audioLib, startPattern); } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_TRACK(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // TRACK ++interpreter->pc; // track value struct TypedValue tValue = itp_evaluateNumericExpression(core, 0, NUM_TRACKS - 1); if (tValue.type == ValueTypeError) return tValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // voice value struct TypedValue vValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1); if (vValue.type == ValueTypeError) return vValue.v.errorCode; if (interpreter->pass == PassRun) { audlib_playTrack(&interpreter->audioLib, tValue.v.floatValue, vValue.v.floatValue); } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_SOUND_SOURCE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // SOUND ++interpreter->pc; // SOURCE ++interpreter->pc; // address value struct TypedValue aValue = itp_evaluateNumericExpression(core, 0, 0xFFFF); if (aValue.type == ValueTypeError) return aValue.v.errorCode; if (interpreter->pass == PassRun) { interpreter->audioLib.sourceAddress = aValue.v.floatValue; } return itp_endOfCommand(interpreter); } struct TypedValue fnc_MUSIC(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // MUSIC ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // x value struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric); if (xValue.type == ValueTypeError) return xValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { int x = xValue.v.floatValue; struct ComposerPlayer *player = &interpreter->audioLib.musicPlayer; switch (x) { case 0: value.v.floatValue = player->index; break; case 1: value.v.floatValue = player->row; break; case 2: value.v.floatValue = player->tick; break; case 3: value.v.floatValue = player->speed; break; default: return val_makeError(ErrorInvalidParameter); } } return value; } ================================================ FILE: core/interpreter/cmd_audio.h ================================================ // // Copyright 2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef cmd_audio_h #define cmd_audio_h #include #include "error.h" #include "value.h" struct Core; enum ErrorCode cmd_SOUND(struct Core *core); //enum ErrorCode cmd_SOUND_COPY(struct Core *core); enum ErrorCode cmd_VOLUME(struct Core *core); enum ErrorCode cmd_ENVELOPE(struct Core *core); enum ErrorCode cmd_LFO(struct Core *core); enum ErrorCode cmd_LFO_A(struct Core *core); enum ErrorCode cmd_LFO_WAVE(struct Core *core); enum ErrorCode cmd_PLAY(struct Core *core); enum ErrorCode cmd_STOP(struct Core *core); enum ErrorCode cmd_MUSIC(struct Core *core); enum ErrorCode cmd_TRACK(struct Core *core); enum ErrorCode cmd_SOUND_SOURCE(struct Core *core); struct TypedValue fnc_MUSIC(struct Core *core); #endif /* cmd_audio_h */ ================================================ FILE: core/interpreter/cmd_background.c ================================================ // // Copyright 2017-2019 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "cmd_background.h" #include "core.h" #include "text_lib.h" #include "cmd_text.h" #include "interpreter_utils.h" #include #include enum ErrorCode cmd_BG(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // BG ++interpreter->pc; // bg value struct TypedValue bgValue = itp_evaluateNumericExpression(core, 0, 1); if (bgValue.type == ValueTypeError) return bgValue.v.errorCode; if (interpreter->pass == PassRun) { interpreter->textLib.bg = bgValue.v.floatValue; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_BG_SOURCE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // BG SOURCE ++interpreter->pc; ++interpreter->pc; // address value struct TypedValue aValue = itp_evaluateNumericExpression(core, 0, 0xFFFF); if (aValue.type == ValueTypeError) return aValue.v.errorCode; int w = 0; if (interpreter->pc->type == TokenComma) { // comma ++interpreter->pc; // width value struct TypedValue wValue = itp_evaluateNumericExpression(core, 1, 0xFFFF); if (wValue.type == ValueTypeError) return wValue.v.errorCode; w = wValue.v.floatValue; } int h = 0; if (interpreter->pc->type == TokenComma) { // comma ++interpreter->pc; // height value struct TypedValue hValue = itp_evaluateNumericExpression(core, 1, 0xFFFF); if (hValue.type == ValueTypeError) return hValue.v.errorCode; h = hValue.v.floatValue; } if (interpreter->pass == PassRun) { int address = aValue.v.floatValue; if (w > 0) { core->interpreter->textLib.sourceAddress = address; core->interpreter->textLib.sourceWidth = w; core->interpreter->textLib.sourceHeight = h; } else { // data with preceding size (W x H) core->interpreter->textLib.sourceAddress = address + 4; core->interpreter->textLib.sourceWidth = machine_peek(core, address + 2); core->interpreter->textLib.sourceHeight = machine_peek(core, address + 3); } } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_BG_COPY(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // BG COPY ++interpreter->pc; ++interpreter->pc; // src X value struct TypedValue srcXValue = itp_evaluateNumericExpression(core, 0, 0xFFFF); if (srcXValue.type == ValueTypeError) return srcXValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // src Y value struct TypedValue srcYValue = itp_evaluateNumericExpression(core, 0, 0xFFFF); if (srcYValue.type == ValueTypeError) return srcYValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // width value struct TypedValue wValue = itp_evaluateNumericExpression(core, 0, PLANE_COLUMNS); if (wValue.type == ValueTypeError) return wValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // height value struct TypedValue hValue = itp_evaluateNumericExpression(core, 0, PLANE_ROWS); if (hValue.type == ValueTypeError) return hValue.v.errorCode; // TO if (interpreter->pc->type != TokenTO) return ErrorSyntax; ++interpreter->pc; // dst X value struct TypedValue dstXValue = itp_evaluateExpression(core, TypeClassNumeric); if (dstXValue.type == ValueTypeError) return dstXValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // dst Y value struct TypedValue dstYValue = itp_evaluateExpression(core, TypeClassNumeric); if (dstYValue.type == ValueTypeError) return dstYValue.v.errorCode; if (interpreter->pass == PassRun) { txtlib_copyBackground(&interpreter->textLib, srcXValue.v.floatValue, srcYValue.v.floatValue, wValue.v.floatValue, hValue.v.floatValue, dstXValue.v.floatValue, dstYValue.v.floatValue); } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_BG_SCROLL(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // BG SCROLL ++interpreter->pc; ++interpreter->pc; // x1 value struct TypedValue x1Value = itp_evaluateNumericExpression(core, 0, PLANE_COLUMNS - 1); if (x1Value.type == ValueTypeError) return x1Value.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // y1 value struct TypedValue y1Value = itp_evaluateNumericExpression(core, 0, PLANE_ROWS - 1); if (y1Value.type == ValueTypeError) return y1Value.v.errorCode; // TO if (interpreter->pc->type != TokenTO) return ErrorSyntax; ++interpreter->pc; // x2 value struct TypedValue x2Value = itp_evaluateNumericExpression(core, 0, PLANE_COLUMNS - 1); if (x2Value.type == ValueTypeError) return x2Value.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // y2 value struct TypedValue y2Value = itp_evaluateNumericExpression(core, 0, PLANE_ROWS - 1); if (y2Value.type == ValueTypeError) return y2Value.v.errorCode; // STEP if (interpreter->pc->type != TokenSTEP) return ErrorSyntax; ++interpreter->pc; // dx value struct TypedValue dxValue = itp_evaluateExpression(core, TypeClassNumeric); if (dxValue.type == ValueTypeError) return dxValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // dy value struct TypedValue dyValue = itp_evaluateExpression(core, TypeClassNumeric); if (dyValue.type == ValueTypeError) return dyValue.v.errorCode; if (interpreter->pass == PassRun) { txtlib_scrollBackground(&interpreter->textLib, x1Value.v.floatValue, y1Value.v.floatValue, x2Value.v.floatValue, y2Value.v.floatValue, dxValue.v.floatValue, dyValue.v.floatValue); } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_ATTR(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // ATTR ++interpreter->pc; // attributes value struct TypedValue aValue = itp_evaluateCharAttributes(core, interpreter->textLib.charAttr); if (aValue.type == ValueTypeError) return aValue.v.errorCode; if (interpreter->pass == PassRun) { interpreter->textLib.charAttr.value = aValue.v.floatValue; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_PAL(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // PAL ++interpreter->pc; // value struct TypedValue value = itp_evaluateNumericExpression(core, 0, NUM_PALETTES - 1); if (value.type == ValueTypeError) return value.v.errorCode; if (interpreter->pass == PassRun) { interpreter->textLib.charAttr.palette = value.v.floatValue; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_FLIP(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // FLIP ++interpreter->pc; // x value struct TypedValue fxValue = itp_evaluateNumericExpression(core, -1, 1); if (fxValue.type == ValueTypeError) return fxValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // y value struct TypedValue fyValue = itp_evaluateNumericExpression(core, -1, 1); if (fyValue.type == ValueTypeError) return fyValue.v.errorCode; if (interpreter->pass == PassRun) { interpreter->textLib.charAttr.flipX = fxValue.v.floatValue ? 1 : 0; interpreter->textLib.charAttr.flipY = fyValue.v.floatValue ? 1 : 0; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_PRIO(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // PRIO ++interpreter->pc; // value struct TypedValue value = itp_evaluateNumericExpression(core, -1, 1); if (value.type == ValueTypeError) return value.v.errorCode; if (interpreter->pass == PassRun) { interpreter->textLib.charAttr.priority = value.v.floatValue ? 1 : 0; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_BG_FILL(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // BG FILL ++interpreter->pc; ++interpreter->pc; // x1 value struct TypedValue x1Value = itp_evaluateExpression(core, TypeClassNumeric); if (x1Value.type == ValueTypeError) return x1Value.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // y1 value struct TypedValue y1Value = itp_evaluateExpression(core, TypeClassNumeric); if (y1Value.type == ValueTypeError) return y1Value.v.errorCode; // TO if (interpreter->pc->type != TokenTO) return ErrorSyntax; ++interpreter->pc; // x2 value struct TypedValue x2Value = itp_evaluateExpression(core, TypeClassNumeric); if (x2Value.type == ValueTypeError) return x2Value.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // y2 value struct TypedValue y2Value = itp_evaluateExpression(core, TypeClassNumeric); if (y2Value.type == ValueTypeError) return y2Value.v.errorCode; // CHAR if (interpreter->pc->type == TokenCHAR) { ++interpreter->pc; // write character with current attributes // character value struct TypedValue cValue = itp_evaluateNumericExpression(core, 0, NUM_CHARACTERS - 1); if (cValue.type == ValueTypeError) return cValue.v.errorCode; if (interpreter->pass == PassRun) { txtlib_setCells(&interpreter->textLib, floorf(x1Value.v.floatValue), floorf(y1Value.v.floatValue), floorf(x2Value.v.floatValue), floorf(y2Value.v.floatValue), cValue.v.floatValue); } } else { // write current attributes (obsolete syntax!) if (interpreter->pass == PassRun) { txtlib_setCells(&interpreter->textLib, floorf(x1Value.v.floatValue), floorf(y1Value.v.floatValue), floorf(x2Value.v.floatValue), floorf(y2Value.v.floatValue), -1); } } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_BG_TINT(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // BG TINT ++interpreter->pc; ++interpreter->pc; // x1 value struct TypedValue x1Value = itp_evaluateExpression(core, TypeClassNumeric); if (x1Value.type == ValueTypeError) return x1Value.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // y1 value struct TypedValue y1Value = itp_evaluateExpression(core, TypeClassNumeric); if (y1Value.type == ValueTypeError) return y1Value.v.errorCode; // TO if (interpreter->pc->type != TokenTO) return ErrorSyntax; ++interpreter->pc; // x2 value struct TypedValue x2Value = itp_evaluateExpression(core, TypeClassNumeric); if (x2Value.type == ValueTypeError) return x2Value.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // y2 value struct TypedValue y2Value = itp_evaluateExpression(core, TypeClassNumeric); if (y2Value.type == ValueTypeError) return y2Value.v.errorCode; struct SimpleAttributes attrs; enum ErrorCode attrsError = itp_evaluateSimpleAttributes(core, &attrs); if (attrsError != ErrorNone) return attrsError; if (interpreter->pass == PassRun) { txtlib_setCellsAttr(&interpreter->textLib, floorf(x1Value.v.floatValue), floorf(y1Value.v.floatValue), floorf(x2Value.v.floatValue), floorf(y2Value.v.floatValue), attrs.pal, attrs.flipX, attrs.flipY, attrs.prio); } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_CELL(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // CELL ++interpreter->pc; // x value struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric); if (xValue.type == ValueTypeError) return xValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // y value struct TypedValue yValue = itp_evaluateExpression(core, TypeClassNumeric); if (yValue.type == ValueTypeError) return yValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // character value struct TypedValue cValue = itp_evaluateOptionalNumericExpression(core, 0, NUM_CHARACTERS - 1); if (cValue.type == ValueTypeError) return cValue.v.errorCode; if (interpreter->pass == PassRun) { int c = (cValue.type == ValueTypeFloat) ? cValue.v.floatValue : -1; txtlib_setCell(&interpreter->textLib, floorf(xValue.v.floatValue), floorf(yValue.v.floatValue), c); } return itp_endOfCommand(interpreter); } struct TypedValue fnc_CELL(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // CELL.? enum TokenType type = interpreter->pc->type; ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // x value struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric); if (xValue.type == ValueTypeError) return xValue; // comma if (interpreter->pc->type != TokenComma) return val_makeError(ErrorSyntax); ++interpreter->pc; // y value struct TypedValue yValue = itp_evaluateExpression(core, TypeClassNumeric); if (yValue.type == ValueTypeError) return yValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { struct Cell *cell = txtlib_getCell(&interpreter->textLib, floorf(xValue.v.floatValue), floorf(yValue.v.floatValue)); if (type == TokenCELLA) { value.v.floatValue = cell->attr.value; } else if (type == TokenCELLC) { value.v.floatValue = cell->character; } else { assert(0); } } return value; } enum ErrorCode cmd_MCELL(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // MCELL ++interpreter->pc; // x value struct TypedValue xValue = itp_evaluateNumericExpression(core, 0, interpreter->textLib.sourceWidth - 1); if (xValue.type == ValueTypeError) return xValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // y value struct TypedValue yValue = itp_evaluateNumericExpression(core, 0, interpreter->textLib.sourceHeight - 1); if (yValue.type == ValueTypeError) return yValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // character value struct TypedValue cValue = itp_evaluateOptionalNumericExpression(core, 0, NUM_CHARACTERS - 1); if (cValue.type == ValueTypeError) return cValue.v.errorCode; if (interpreter->pass == PassRun) { int c = (cValue.type == ValueTypeFloat) ? cValue.v.floatValue : -1; bool success = txtlib_setSourceCell(&interpreter->textLib, xValue.v.floatValue, yValue.v.floatValue, c); if (!success) return ErrorIllegalMemoryAccess; } return itp_endOfCommand(interpreter); } struct TypedValue fnc_MCELL(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // MCELL.? enum TokenType type = interpreter->pc->type; ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // x value struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric); if (xValue.type == ValueTypeError) return xValue; // comma if (interpreter->pc->type != TokenComma) return val_makeError(ErrorSyntax); ++interpreter->pc; // y value struct TypedValue yValue = itp_evaluateExpression(core, TypeClassNumeric); if (yValue.type == ValueTypeError) return yValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { int x = floorf(xValue.v.floatValue); int y = floorf(yValue.v.floatValue); value.v.floatValue = txtlib_getSourceCell(&interpreter->textLib, x, y, (type == TokenMCELLA)); } return value; } enum ErrorCode cmd_TINT(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // TINT ++interpreter->pc; // x value struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric); if (xValue.type == ValueTypeError) return xValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // y value struct TypedValue yValue = itp_evaluateExpression(core, TypeClassNumeric); if (yValue.type == ValueTypeError) return yValue.v.errorCode; struct SimpleAttributes attrs; enum ErrorCode attrsError = itp_evaluateSimpleAttributes(core, &attrs); if (attrsError != ErrorNone) return attrsError; if (interpreter->pass == PassRun) { struct Cell *cell = txtlib_getCell(&interpreter->textLib, floorf(xValue.v.floatValue), floorf(yValue.v.floatValue)); if (attrs.pal >= 0) cell->attr.palette = attrs.pal; if (attrs.flipX >= 0) cell->attr.flipX = attrs.flipX; if (attrs.flipY >= 0) cell->attr.flipY = attrs.flipY; if (attrs.prio >= 0) cell->attr.priority = attrs.prio; } return itp_endOfCommand(interpreter); } ================================================ FILE: core/interpreter/cmd_background.h ================================================ // // Copyright 2017-2019 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef cmd_background_h #define cmd_background_h #include #include "error.h" #include "value.h" struct Core; enum ErrorCode cmd_BG(struct Core *core); enum ErrorCode cmd_BG_SOURCE(struct Core *core); enum ErrorCode cmd_BG_COPY(struct Core *core); enum ErrorCode cmd_BG_SCROLL(struct Core *core); enum ErrorCode cmd_ATTR(struct Core *core); enum ErrorCode cmd_PAL(struct Core *core); enum ErrorCode cmd_FLIP(struct Core *core); enum ErrorCode cmd_PRIO(struct Core *core); enum ErrorCode cmd_BG_FILL(struct Core *core); enum ErrorCode cmd_BG_TINT(struct Core *core); enum ErrorCode cmd_CELL(struct Core *core); struct TypedValue fnc_CELL(struct Core *core); enum ErrorCode cmd_MCELL(struct Core *core); struct TypedValue fnc_MCELL(struct Core *core); enum ErrorCode cmd_TINT(struct Core *core); #endif /* cmd_background_h */ ================================================ FILE: core/interpreter/cmd_control.c ================================================ // // Copyright 2017-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "cmd_control.h" #include "core.h" #include enum ErrorCode cmd_END(struct Core *core) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt; // END ++interpreter->pc; if (interpreter->pass == PassRun) { itp_endProgram(core); return ErrorNone; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_IF(struct Core *core, bool isAfterBlockElse) { struct Interpreter *interpreter = core->interpreter; // IF struct Token *tokenIF = interpreter->pc; ++interpreter->pc; // Expression struct TypedValue value = itp_evaluateExpression(core, TypeClassNumeric); if (value.type == ValueTypeError) return value.v.errorCode; // THEN if (interpreter->pc->type != TokenTHEN) return ErrorSyntax; ++interpreter->pc; if (interpreter->pass == PassPrepare) { if (interpreter->pc->type == TokenEol) { // IF block if (interpreter->isSingleLineIf) return ErrorExpectedCommand; enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, isAfterBlockElse ? LabelTypeELSEIF : LabelTypeIF, tokenIF); if (errorCode != ErrorNone) return errorCode; // Eol ++interpreter->pc; } else { // single line IF interpreter->isSingleLineIf = true; struct Token *token = interpreter->pc; while (token->type != TokenEol && token->type != TokenELSE) { token++; } tokenIF->jumpToken = token + 1; // after ELSE or Eol } } else if (interpreter->pass == PassRun) { if (value.v.floatValue == 0) { interpreter->pc = tokenIF->jumpToken; // after ELSE or END IF, or Eol for single line } } return ErrorNone; } enum ErrorCode cmd_ELSE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // ELSE struct Token *tokenELSE = interpreter->pc; ++interpreter->pc; if (interpreter->pass == PassPrepare) { if (interpreter->isSingleLineIf) { if (interpreter->pc->type == TokenEol) return ErrorExpectedCommand; struct Token *token = interpreter->pc; while (token->type != TokenEol) { token++; } tokenELSE->jumpToken = token + 1; // after Eol } else { struct LabelStackItem *item = lab_popLabelStackItem(interpreter); if (!item) return ErrorElseWithoutIf; if (item->type == LabelTypeIF) { item->token->jumpToken = interpreter->pc; } else if (item->type == LabelTypeELSEIF) { item->token->jumpToken = interpreter->pc; item = lab_popLabelStackItem(interpreter); assert(item->type == LabelTypeELSE); item->token->jumpToken = tokenELSE; } else { return ErrorElseWithoutIf; } enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeELSE, tokenELSE); if (errorCode != ErrorNone) return errorCode; if (interpreter->pc->type == TokenIF) { return cmd_IF(core, true); } else { // Eol if (interpreter->pc->type != TokenEol) return ErrorSyntax; ++interpreter->pc; } } } else if (interpreter->pass == PassRun) { interpreter->pc = tokenELSE->jumpToken; // after END IF, or Eol for single line } return ErrorNone; } enum ErrorCode cmd_END_IF(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // END IF ++interpreter->pc; ++interpreter->pc; if (interpreter->pass == PassPrepare) { struct LabelStackItem *item = lab_popLabelStackItem(interpreter); if (!item) { return ErrorEndIfWithoutIf; } else if (item->type == LabelTypeIF || item->type == LabelTypeELSE) { item->token->jumpToken = interpreter->pc; } else if (item->type == LabelTypeELSEIF) { item->token->jumpToken = interpreter->pc; item = lab_popLabelStackItem(interpreter); assert(item->type == LabelTypeELSE); item->token->jumpToken = interpreter->pc; } else { return ErrorEndIfWithoutIf; } } // Eol if (interpreter->pc->type != TokenEol) return ErrorSyntax; ++interpreter->pc; return ErrorNone; } enum ErrorCode cmd_FOR(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // FOR struct Token *tokenFOR = interpreter->pc; ++interpreter->pc; // Variable struct Token *tokenFORVar = interpreter->pc; enum ErrorCode errorCode = ErrorNone; enum ValueType valueType = ValueTypeNull; union Value *varValue = itp_readVariable(core, &valueType, &errorCode, true); if (!varValue) return errorCode; if (valueType != ValueTypeFloat) return ErrorTypeMismatch; // Eq if (interpreter->pc->type != TokenEq) return ErrorSyntax; ++interpreter->pc; // start value struct TypedValue startValue = itp_evaluateExpression(core, TypeClassNumeric); if (startValue.type == ValueTypeError) return startValue.v.errorCode; // TO if (interpreter->pc->type != TokenTO) return ErrorSyntax; ++interpreter->pc; // limit value struct Token *tokenFORLimit = interpreter->pc; struct TypedValue limitValue = itp_evaluateExpression(core, TypeClassNumeric); if (limitValue.type == ValueTypeError) return limitValue.v.errorCode; // STEP struct TypedValue stepValue; if (interpreter->pc->type == TokenSTEP) { ++interpreter->pc; // step value stepValue = itp_evaluateExpression(core, TypeClassNumeric); if (stepValue.type == ValueTypeError) return stepValue.v.errorCode; } else { stepValue.type = ValueTypeFloat; stepValue.v.floatValue = 1.0f; } if (interpreter->pass == PassPrepare) { lab_pushLabelStackItem(interpreter, LabelTypeFORLimit, tokenFORLimit); lab_pushLabelStackItem(interpreter, LabelTypeFORVar, tokenFORVar); enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeFOR, tokenFOR); if (errorCode != ErrorNone) return errorCode; // Eol if (interpreter->pc->type != TokenEol) return ErrorSyntax; ++interpreter->pc; } else if (interpreter->pass == PassRun) { varValue->floatValue = startValue.v.floatValue; // limit check if ((stepValue.v.floatValue > 0 && varValue->floatValue > limitValue.v.floatValue) || (stepValue.v.floatValue < 0 && varValue->floatValue < limitValue.v.floatValue)) { interpreter->pc = tokenFOR->jumpToken; // after NEXT's Eol } else { // Eol if (interpreter->pc->type != TokenEol) return ErrorSyntax; ++interpreter->pc; } } return ErrorNone; } enum ErrorCode cmd_NEXT(struct Core *core) { struct Interpreter *interpreter = core->interpreter; struct LabelStackItem *itemFORLimit = NULL; struct LabelStackItem *itemFORVar = NULL; struct LabelStackItem *itemFOR = NULL; if (interpreter->pass == PassPrepare) { itemFOR = lab_popLabelStackItem(interpreter); if (!itemFOR || itemFOR->type != LabelTypeFOR) return ErrorNextWithoutFor; itemFORVar = lab_popLabelStackItem(interpreter); assert(itemFORVar && itemFORVar->type == LabelTypeFORVar); itemFORLimit = lab_popLabelStackItem(interpreter); assert(itemFORLimit && itemFORLimit->type == LabelTypeFORLimit); } // NEXT struct Token *tokenNEXT = interpreter->pc; ++interpreter->pc; // Variable enum ErrorCode errorCode = ErrorNone; struct Token *tokenVar = interpreter->pc; enum ValueType valueType = ValueTypeNull; union Value *varValue = itp_readVariable(core, &valueType, &errorCode, true); if (!varValue) return errorCode; if (valueType != ValueTypeFloat) return ErrorTypeMismatch; if (interpreter->pass == PassPrepare) { if (tokenVar->symbolIndex != itemFORVar->token->symbolIndex) return ErrorNextWithoutFor; } // Eol if (interpreter->pc->type != TokenEol) return ErrorSyntax; ++interpreter->pc; if (interpreter->pass == PassPrepare) { itemFOR->token->jumpToken = interpreter->pc; tokenNEXT->jumpToken = itemFORLimit->token; } else if (interpreter->pass == PassRun) { struct Token *storedPc = interpreter->pc; interpreter->pc = tokenNEXT->jumpToken; // limit value struct TypedValue limitValue = itp_evaluateExpression(core, TypeClassNumeric); if (limitValue.type == ValueTypeError) return limitValue.v.errorCode; // STEP struct TypedValue stepValue; if (interpreter->pc->type == TokenSTEP) { ++interpreter->pc; // step value stepValue = itp_evaluateExpression(core, TypeClassNumeric); if (stepValue.type == ValueTypeError) return stepValue.v.errorCode; } else { stepValue.type = ValueTypeFloat; stepValue.v.floatValue = 1.0f; } // Eol if (interpreter->pc->type != TokenEol) return ErrorSyntax; ++interpreter->pc; varValue->floatValue += stepValue.v.floatValue; // limit check if ((stepValue.v.floatValue > 0 && varValue->floatValue > limitValue.v.floatValue) || (stepValue.v.floatValue < 0 && varValue->floatValue < limitValue.v.floatValue)) { interpreter->pc = storedPc; // after NEXT's Eol } } return ErrorNone; } enum ErrorCode cmd_GOTO(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // GOTO struct Token *tokenGOTO = interpreter->pc; ++interpreter->pc; // Identifier if (interpreter->pc->type != TokenIdentifier) return ErrorExpectedLabel; struct Token *tokenIdentifier = interpreter->pc; ++interpreter->pc; if (interpreter->pass == PassPrepare) { struct JumpLabelItem *item = tok_getJumpLabel(&interpreter->tokenizer, tokenIdentifier->symbolIndex); if (!item) return ErrorUndefinedLabel; tokenGOTO->jumpToken = item->token; return itp_endOfCommand(interpreter); } else if (interpreter->pass == PassRun) { interpreter->pc = tokenGOTO->jumpToken; // after label } return ErrorNone; } enum ErrorCode cmd_GOSUB(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // GOSUB struct Token *tokenGOSUB = interpreter->pc; ++interpreter->pc; // Identifier if (interpreter->pc->type != TokenIdentifier) return ErrorExpectedLabel; struct Token *tokenIdentifier = interpreter->pc; ++interpreter->pc; if (interpreter->pass == PassPrepare) { struct JumpLabelItem *item = tok_getJumpLabel(&interpreter->tokenizer, tokenIdentifier->symbolIndex); if (!item) return ErrorUndefinedLabel; tokenGOSUB->jumpToken = item->token; return itp_endOfCommand(interpreter); } else if (interpreter->pass == PassRun) { enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeGOSUB, interpreter->pc); if (errorCode != ErrorNone) return errorCode; interpreter->pc = tokenGOSUB->jumpToken; // after label } return ErrorNone; } enum ErrorCode cmd_RETURN(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // RETURN struct Token *tokenRETURN = interpreter->pc; ++interpreter->pc; // Identifier struct Token *tokenIdentifier = NULL; if (interpreter->pc->type == TokenIdentifier) { tokenIdentifier = interpreter->pc; ++interpreter->pc; } if (interpreter->pass == PassPrepare) { if (tokenIdentifier) { struct JumpLabelItem *item = tok_getJumpLabel(&interpreter->tokenizer, tokenIdentifier->symbolIndex); if (!item) return ErrorUndefinedLabel; tokenRETURN->jumpToken = item->token; } } else if (interpreter->pass == PassRun) { struct LabelStackItem *itemGOSUB = lab_popLabelStackItem(interpreter); if (!itemGOSUB) return ErrorReturnWithoutGosub; if (itemGOSUB->type == LabelTypeGOSUB) { if (tokenRETURN->jumpToken) { // jump to label interpreter->pc = tokenRETURN->jumpToken; // after label // clear stack interpreter->numLabelStackItems = 0; } else { // jump back interpreter->pc = itemGOSUB->token; // after GOSUB } } else { return ErrorReturnWithoutGosub; } } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_WAIT(struct Core *core) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt; // WAIT ++interpreter->pc; int wait = 0; if (interpreter->pc->type == TokenVBL) { // VBL ++interpreter->pc; } else { // value struct TypedValue value = itp_evaluateNumericExpression(core, 1, 0xFFFF); if (value.type == ValueTypeError) return value.v.errorCode; wait = value.v.floatValue - 1; } if (interpreter->pass == PassRun) { interpreter->exitEvaluation = true; interpreter->waitCount = wait; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_ON(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // ON ++interpreter->pc; // RASTER/VBL enum TokenType type = interpreter->pc->type; if (type != TokenRASTER && type != TokenVBL) return ErrorSyntax; ++interpreter->pc; if (interpreter->pc->type == TokenOFF) { // OFF ++interpreter->pc; if (interpreter->pass == PassRun) { if (type == TokenRASTER) { interpreter->currentOnRasterToken = NULL; } else if (type == TokenVBL) { interpreter->currentOnVBLToken = NULL; } } } else { // CALL if (interpreter->pc->type != TokenCALL) return ErrorSyntax; struct Token *tokenCALL = interpreter->pc; ++interpreter->pc; // Identifier if (interpreter->pc->type != TokenIdentifier) return ErrorExpectedSubprogramName; struct Token *tokenIdentifier = interpreter->pc; ++interpreter->pc; if (interpreter->pass == PassPrepare) { struct SubItem *item = tok_getSub(&interpreter->tokenizer, tokenIdentifier->symbolIndex); if (!item) return ErrorUndefinedSubprogram; tokenCALL->jumpToken = item->token; } else if (interpreter->pass == PassRun) { if (type == TokenRASTER) { interpreter->currentOnRasterToken = tokenCALL->jumpToken; } else if (type == TokenVBL) { interpreter->currentOnVBLToken = tokenCALL->jumpToken; } } } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_DO(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // DO struct Token *tokenDO = interpreter->pc; ++interpreter->pc; if (interpreter->pass == PassPrepare) { enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeDO, tokenDO); if (errorCode != ErrorNone) return errorCode; } // Eol if (interpreter->pc->type != TokenEol) return ErrorSyntax; ++interpreter->pc; return ErrorNone; } enum ErrorCode cmd_LOOP(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // LOOP struct Token *tokenLOOP = interpreter->pc; ++interpreter->pc; if (interpreter->pass == PassPrepare) { struct LabelStackItem *item = lab_popLabelStackItem(interpreter); if (!item || item->type != LabelTypeDO) return ErrorLoopWithoutDo; tokenLOOP->jumpToken = item->token + 1; item->token->jumpToken = tokenLOOP + 1; // Eol if (interpreter->pc->type != TokenEol) return ErrorSyntax; ++interpreter->pc; } else if (interpreter->pass == PassRun) { interpreter->pc = tokenLOOP->jumpToken; // after DO } return ErrorNone; } enum ErrorCode cmd_REPEAT(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // REPEAT struct Token *tokenREPEAT = interpreter->pc; ++interpreter->pc; if (interpreter->pass == PassPrepare) { enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeREPEAT, tokenREPEAT); if (errorCode != ErrorNone) return errorCode; } // Eol if (interpreter->pc->type != TokenEol) return ErrorSyntax; ++interpreter->pc; return ErrorNone; } enum ErrorCode cmd_UNTIL(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // UNTIL struct Token *tokenUNTIL = interpreter->pc; ++interpreter->pc; // Expression struct TypedValue value = itp_evaluateExpression(core, TypeClassNumeric); if (value.type == ValueTypeError) return value.v.errorCode; if (interpreter->pass == PassPrepare) { struct LabelStackItem *item = lab_popLabelStackItem(interpreter); if (!item || item->type != LabelTypeREPEAT) return ErrorUntilWithoutRepeat; tokenUNTIL->jumpToken = item->token + 1; item->token->jumpToken = interpreter->pc; // Eol if (interpreter->pc->type != TokenEol) return ErrorSyntax; ++interpreter->pc; } else if (interpreter->pass == PassRun) { if (value.v.floatValue == 0) { interpreter->pc = tokenUNTIL->jumpToken; // after REPEAT } else { // Eol if (interpreter->pc->type != TokenEol) return ErrorSyntax; ++interpreter->pc; } } return ErrorNone; } enum ErrorCode cmd_WHILE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // WHILE struct Token *tokenWHILE = interpreter->pc; ++interpreter->pc; if (interpreter->pass == PassPrepare) { enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeWHILE, tokenWHILE); if (errorCode != ErrorNone) return errorCode; } // Expression struct TypedValue value = itp_evaluateExpression(core, TypeClassNumeric); if (value.type == ValueTypeError) return value.v.errorCode; // Eol if (interpreter->pc->type != TokenEol) return ErrorSyntax; ++interpreter->pc; if (interpreter->pass == PassRun) { if (value.v.floatValue == 0) { interpreter->pc = tokenWHILE->jumpToken; // after WEND } } return ErrorNone; } enum ErrorCode cmd_WEND(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // WEND struct Token *tokenWEND = interpreter->pc; ++interpreter->pc; if (interpreter->pass == PassPrepare) { struct LabelStackItem *item = lab_popLabelStackItem(interpreter); if (!item || item->type != LabelTypeWHILE) return ErrorWendWithoutWhile; tokenWEND->jumpToken = item->token; item->token->jumpToken = tokenWEND + 1; // Eol if (interpreter->pc->type != TokenEol) return ErrorSyntax; ++interpreter->pc; } else if (interpreter->pass == PassRun) { interpreter->pc = tokenWEND->jumpToken; // on WHILE } return ErrorNone; } enum ErrorCode cmd_EXIT(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // EXIT struct Token *tokenEXIT = interpreter->pc; ++interpreter->pc; if (interpreter->pass == PassPrepare) { enum LabelType types[] = {LabelTypeFOR, LabelTypeDO, LabelTypeWHILE, LabelTypeREPEAT}; struct LabelStackItem *item = lab_searchLabelStackItem(interpreter, types, 4); if (!item) return ErrorExitNotInsideLoop; tokenEXIT->jumpToken = item->token; return itp_endOfCommand(interpreter); } else if (interpreter->pass == PassRun) { interpreter->pc = tokenEXIT->jumpToken->jumpToken; } return ErrorNone; } enum ErrorCode cmd_SYSTEM(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // SYSTEM ++interpreter->pc; // type value struct TypedValue tValue = itp_evaluateNumericExpression(core, 0, 0); if (tValue.type == ValueTypeError) return tValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // setting value struct TypedValue sValue = itp_evaluateExpression(core, TypeClassNumeric); if (sValue.type == ValueTypeError) return sValue.v.errorCode; if (interpreter->pass == PassRun) { switch ((int)tValue.v.floatValue) { case 0: core->machineInternals->isEnergySaving = (sValue.v.floatValue != 0.0f); break; } } return itp_endOfCommand(interpreter); } ================================================ FILE: core/interpreter/cmd_control.h ================================================ // // Copyright 2017-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef cmd_control_h #define cmd_control_h #include #include #include "error.h" struct Core; enum ErrorCode cmd_END(struct Core *core); enum ErrorCode cmd_IF(struct Core *core, bool isAfterBlockElse); enum ErrorCode cmd_ELSE(struct Core *core); enum ErrorCode cmd_END_IF(struct Core *core); enum ErrorCode cmd_FOR(struct Core *core); enum ErrorCode cmd_NEXT(struct Core *core); enum ErrorCode cmd_GOTO(struct Core *core); enum ErrorCode cmd_GOSUB(struct Core *core); enum ErrorCode cmd_RETURN(struct Core *core); enum ErrorCode cmd_WAIT(struct Core *core); enum ErrorCode cmd_ON(struct Core *core); enum ErrorCode cmd_DO(struct Core *core); enum ErrorCode cmd_LOOP(struct Core *core); enum ErrorCode cmd_REPEAT(struct Core *core); enum ErrorCode cmd_UNTIL(struct Core *core); enum ErrorCode cmd_WHILE(struct Core *core); enum ErrorCode cmd_WEND(struct Core *core); enum ErrorCode cmd_EXIT(struct Core *core); enum ErrorCode cmd_SYSTEM(struct Core *core); #endif /* cmd_control_h */ ================================================ FILE: core/interpreter/cmd_data.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "cmd_data.h" #include "core.h" enum ErrorCode cmd_DATA(struct Core *core) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pass == PassPrepare) { if (!interpreter->firstData) { interpreter->firstData = interpreter->pc; } if (interpreter->lastData) { interpreter->lastData->jumpToken = interpreter->pc; } interpreter->lastData = interpreter->pc; } do { ++interpreter->pc; // DATA at first, then comma if (interpreter->pc->type == TokenString) { ++interpreter->pc; } else if (interpreter->pc->type == TokenFloat) { ++interpreter->pc; } else if (interpreter->pc->type == TokenMinus) { ++interpreter->pc; if (interpreter->pc->type != TokenFloat) return ErrorSyntax; ++interpreter->pc; } else { return ErrorSyntax; } } while (interpreter->pc->type == TokenComma); // Eol if (interpreter->pc->type != TokenEol) return ErrorSyntax; ++interpreter->pc; return ErrorNone; } enum ErrorCode cmd_READ(struct Core *core) { struct Interpreter *interpreter = core->interpreter; do { // READ at first, then comma ++interpreter->pc; // variable enum ValueType varType = ValueTypeNull; enum ErrorCode errorCode = ErrorNone; union Value *varValue = itp_readVariable(core, &varType, &errorCode, true); if (!varValue) return errorCode; if (interpreter->pass == PassRun) { if (!interpreter->currentDataValueToken) return ErrorOutOfData; struct Token *dataValueToken = interpreter->currentDataValueToken; if (dataValueToken->type == TokenFloat) { if (varType != ValueTypeFloat) return ErrorTypeMismatch; varValue->floatValue = dataValueToken->floatValue; } else if (dataValueToken->type == TokenMinus) { if (varType != ValueTypeFloat) return ErrorTypeMismatch; interpreter->currentDataValueToken++; varValue->floatValue = -interpreter->currentDataValueToken->floatValue; } else if (dataValueToken->type == TokenString) { if (varType != ValueTypeString) return ErrorTypeMismatch; if (varValue->stringValue) { rcstring_release(varValue->stringValue); } varValue->stringValue = dataValueToken->stringValue; rcstring_retain(varValue->stringValue); } dat_nextData(interpreter); } } while (interpreter->pc->type == TokenComma); return itp_endOfCommand(interpreter); } enum ErrorCode cmd_RESTORE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // RESTORE struct Token *tokenRESTORE = interpreter->pc; ++interpreter->pc; // optional jump label if (interpreter->pc->type == TokenIdentifier) { if (interpreter->pass == PassPrepare) { struct JumpLabelItem *item = tok_getJumpLabel(&interpreter->tokenizer, interpreter->pc->symbolIndex); if (!item) return ErrorUndefinedLabel; tokenRESTORE->jumpToken = item->token; } else if (interpreter->pass == PassRun) { // find DATA after label dat_restoreData(interpreter, tokenRESTORE->jumpToken); } ++interpreter->pc; } else if (interpreter->pass == PassRun) { // restore to first DATA dat_restoreData(interpreter, NULL); } return itp_endOfCommand(interpreter); } ================================================ FILE: core/interpreter/cmd_data.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef cmd_data_h #define cmd_data_h #include #include "error.h" struct Core; enum ErrorCode cmd_DATA(struct Core *core); enum ErrorCode cmd_READ(struct Core *core); enum ErrorCode cmd_RESTORE(struct Core *core); #endif /* cmd_data_h */ ================================================ FILE: core/interpreter/cmd_files.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "cmd_files.h" #include "core.h" #include #include enum ErrorCode cmd_LOAD(struct Core *core) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt; // LOAD struct Token *startPc = interpreter->pc; ++interpreter->pc; // file value struct TypedValue fileValue = itp_evaluateNumericExpression(core, 0, MAX_ENTRIES - 1); if (fileValue.type == ValueTypeError) return fileValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // address value struct TypedValue addressValue = itp_evaluateExpression(core, TypeClassNumeric); if (addressValue.type == ValueTypeError) return addressValue.v.errorCode; int maxLength = 0; int offset = 0; if (interpreter->pc->type == TokenComma) { ++interpreter->pc; // max length value struct TypedValue maxLengthValue = itp_evaluateNumericExpression(core, 0, DATA_SIZE); if (maxLengthValue.type == ValueTypeError) return maxLengthValue.v.errorCode; maxLength = maxLengthValue.v.floatValue; if (interpreter->pc->type == TokenComma) { ++interpreter->pc; // offset value struct TypedValue offsetValue = itp_evaluateNumericExpression(core, 0, DATA_SIZE); if (offsetValue.type == ValueTypeError) return offsetValue.v.errorCode; offset = offsetValue.v.floatValue; } } if (interpreter->pass == PassRun) { bool pokeFailed = false; bool ready = disk_loadFile(core, fileValue.v.floatValue, addressValue.v.floatValue, maxLength, offset, &pokeFailed); if (pokeFailed) return ErrorIllegalMemoryAccess; interpreter->exitEvaluation = true; if (!ready) { // disk not ready interpreter->pc = startPc; interpreter->state = StateWaitForDisk; return ErrorNone; } } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_SAVE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt; // SAVE struct Token *startPc = interpreter->pc; ++interpreter->pc; // file value struct TypedValue fileValue = itp_evaluateNumericExpression(core, 0, MAX_ENTRIES - 1); if (fileValue.type == ValueTypeError) return fileValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // comment value struct TypedValue commentValue = itp_evaluateExpression(core, TypeClassString); if (commentValue.type == ValueTypeError) return commentValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // address value struct TypedValue addressValue = itp_evaluateExpression(core, TypeClassNumeric); if (addressValue.type == ValueTypeError) return addressValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // length value struct TypedValue lengthValue = itp_evaluateNumericExpression(core, 1, DATA_SIZE); if (lengthValue.type == ValueTypeError) return lengthValue.v.errorCode; if (interpreter->pass == PassRun) { int address = addressValue.v.floatValue; int length = lengthValue.v.floatValue; if (address + length > 0x10000) { return ErrorIllegalMemoryAccess; } bool ready = disk_saveFile(core, fileValue.v.floatValue, commentValue.v.stringValue->chars, address, length); rcstring_release(commentValue.v.stringValue); interpreter->exitEvaluation = true; if (!ready) { // disk not ready interpreter->pc = startPc; interpreter->state = StateWaitForDisk; return ErrorNone; } } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_FILES(struct Core *core) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt; // FILES struct Token *startPc = interpreter->pc; ++interpreter->pc; if (interpreter->pass == PassRun) { bool ready = disk_prepare(core); interpreter->exitEvaluation = true; if (!ready) { // disk not ready interpreter->pc = startPc; interpreter->state = StateWaitForDisk; return ErrorNone; } } return itp_endOfCommand(interpreter); } struct TypedValue fnc_FILE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // FILE$ ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // file value struct TypedValue fileValue = itp_evaluateNumericExpression(core, 0, MAX_ENTRIES - 1); if (fileValue.type == ValueTypeError) return fileValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue resultValue; resultValue.type = ValueTypeString; if (interpreter->pass == PassRun) { if (core->diskDrive->dataManager.data == NULL) return val_makeError(ErrorDirectoryNotLoaded); int index = fileValue.v.floatValue; struct DataEntry *entry = &core->diskDrive->dataManager.entries[index]; size_t len = strlen(entry->comment); resultValue.v.stringValue = rcstring_new(entry->comment, len); rcstring_retain(resultValue.v.stringValue); interpreter->cycles += len; } return resultValue; } struct TypedValue fnc_FSIZE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // FSIZE ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // file value struct TypedValue fileValue = itp_evaluateNumericExpression(core, 0, MAX_ENTRIES - 1); if (fileValue.type == ValueTypeError) return fileValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue resultValue; resultValue.type = ValueTypeFloat; if (interpreter->pass == PassRun) { if (core->diskDrive->dataManager.data == NULL) return val_makeError(ErrorDirectoryNotLoaded); int index = fileValue.v.floatValue; struct DataEntry *entry = &core->diskDrive->dataManager.entries[index]; resultValue.v.floatValue = entry->length; } return resultValue; } ================================================ FILE: core/interpreter/cmd_files.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef cmd_files_h #define cmd_files_h #include #include "error.h" #include "value.h" struct Core; enum ErrorCode cmd_LOAD(struct Core *core); enum ErrorCode cmd_SAVE(struct Core *core); enum ErrorCode cmd_FILES(struct Core *core); struct TypedValue fnc_FILE(struct Core *core); struct TypedValue fnc_FSIZE(struct Core *core); #endif /* cmd_files_h */ ================================================ FILE: core/interpreter/cmd_io.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "cmd_io.h" #include "core.h" #include enum ErrorCode cmd_KEYBOARD(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // KEYBOARD ++interpreter->pc; // ON/OFF/OPTIONAL enum TokenType type = interpreter->pc->type; if (type != TokenON && type != TokenOFF && type != TokenOPTIONAL) return ErrorSyntax; ++interpreter->pc; if (interpreter->pass == PassRun) { core->machine->ioRegisters.attr.keyboardEnabled = (type == TokenON || type == TokenOPTIONAL); interpreter->isKeyboardOptional = (type == TokenOPTIONAL); delegate_controlsDidChange(core); } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_TOUCHSCREEN(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // TOUCHSCREEN ++interpreter->pc; if (interpreter->pass == PassRun) { if (core->machine->ioRegisters.attr.gamepadsEnabled > 0) return ErrorInputChangeNotAllowed; core->machine->ioRegisters.attr.touchEnabled = 1; delegate_controlsDidChange(core); } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_GAMEPAD(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // GAMEPAD ++interpreter->pc; // number struct TypedValue value = itp_evaluateNumericExpression(core, 1, 2); if (value.type == ValueTypeError) return value.v.errorCode; if (interpreter->pass == PassRun) { if (core->machine->ioRegisters.attr.touchEnabled) return ErrorInputChangeNotAllowed; core->machine->ioRegisters.attr.gamepadsEnabled = value.v.floatValue; core->machine->ioRegisters.status.touch = 0; for (int i = 0; i < NUM_GAMEPADS; i++) { core->machine->ioRegisters.gamepads[i].value = 0; } delegate_controlsDidChange(core); } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_PAUSE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // PAUSE ++interpreter->pc; // ON/OFF? enum TokenType type = interpreter->pc->type; if (type == TokenON || type == TokenOFF) { ++interpreter->pc; } if (interpreter->pass == PassRun) { if (type == TokenON) { core->machine->ioRegisters.status.pause = 0; interpreter->handlesPause = true; } else if (type == TokenOFF) { interpreter->handlesPause = false; } else { interpreter->state = StatePaused; overlay_updateState(core); } } return itp_endOfCommand(interpreter); } struct TypedValue fnc_UP_DOWN_LEFT_RIGHT(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // UP/DOWN/LEFT/RIGHT enum TokenType type = interpreter->pc->type; ++interpreter->pc; // TAP bool tap = false; if (interpreter->pc->type == TokenTAP) { ++interpreter->pc; tap = true; } // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // p expression struct TypedValue pValue = itp_evaluateNumericExpression(core, 0, 1); if (pValue.type == ValueTypeError) return pValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { if (core->machine->ioRegisters.attr.gamepadsEnabled == 0) return val_makeError(ErrorGamepadNotEnabled); int p = pValue.v.floatValue; int active = 0; int lastFrameActive = 0; union Gamepad *gamepad = &core->machine->ioRegisters.gamepads[p]; union Gamepad *lastFrameGamepad = &core->interpreter->lastFrameGamepads[p]; switch (type) { case TokenUP: active = gamepad->up; lastFrameActive = lastFrameGamepad->up; break; case TokenDOWN: active = gamepad->down; lastFrameActive = lastFrameGamepad->down; break; case TokenLEFT: active = gamepad->left; lastFrameActive = lastFrameGamepad->left; break; case TokenRIGHT: active = gamepad->right; lastFrameActive = lastFrameGamepad->right; break; default: assert(0); break; } value.v.floatValue = active && !(tap && lastFrameActive) ? BAS_TRUE : BAS_FALSE; } return value; } struct TypedValue fnc_BUTTON(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // BUTTON ++interpreter->pc; // TAP bool tap = false; if (interpreter->pc->type == TokenTAP) { ++interpreter->pc; tap = true; } // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // p expression struct TypedValue pValue = itp_evaluateNumericExpression(core, 0, 1); if (pValue.type == ValueTypeError) return pValue; int n = -1; if (interpreter->pc->type == TokenComma) { // comma ++interpreter->pc; // n expression struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, 1); if (nValue.type == ValueTypeError) return nValue; n = nValue.v.floatValue; } // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { if (core->machine->ioRegisters.attr.gamepadsEnabled == 0) return val_makeError(ErrorGamepadNotEnabled); int p = pValue.v.floatValue; union Gamepad *gamepad = &core->machine->ioRegisters.gamepads[p]; int active = (n == -1) ? (gamepad->buttonA || gamepad->buttonB) : (n == 0) ? gamepad->buttonA : gamepad->buttonB; if (active && tap) { // invalidate button if it was already pressed last frame union Gamepad *lastFrameGamepad = &core->interpreter->lastFrameGamepads[p]; if ((n == -1) ? (lastFrameGamepad->buttonA || lastFrameGamepad->buttonB) : (n == 0) ? lastFrameGamepad->buttonA : lastFrameGamepad->buttonB) { active = 0; } } value.v.floatValue = active ? BAS_TRUE : BAS_FALSE; } return value; } struct TypedValue fnc_TOUCH(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // TOUCH ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { if (core->machine->ioRegisters.attr.touchEnabled == 0) return val_makeError(ErrorTouchNotEnabled); value.v.floatValue = core->machine->ioRegisters.status.touch ? BAS_TRUE : BAS_FALSE; } return value; } struct TypedValue fnc_TAP(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // TAP ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { if (core->machine->ioRegisters.attr.touchEnabled == 0) return val_makeError(ErrorTouchNotEnabled); value.v.floatValue = (core->machine->ioRegisters.status.touch && !core->interpreter->lastFrameIOStatus.touch) ? BAS_TRUE : BAS_FALSE; } return value; } struct TypedValue fnc_TOUCH_X_Y(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // TOUCH.? enum TokenType type = interpreter->pc->type; ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { if (core->machine->ioRegisters.attr.touchEnabled == 0) return val_makeError(ErrorTouchNotEnabled); if (type == TokenTOUCHX) { value.v.floatValue = core->machine->ioRegisters.touchX; } else if (type == TokenTOUCHY) { value.v.floatValue = core->machine->ioRegisters.touchY; } else { assert(0); } } return value; } struct TypedValue fnc_PAUSE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // PAUSE ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { if (interpreter->handlesPause) return val_makeError(ErrorAutomaticPauseNotDisabled); value.v.floatValue = core->machine->ioRegisters.status.pause ? BAS_TRUE : BAS_FALSE; core->machine->ioRegisters.status.pause = 0; } return value; } ================================================ FILE: core/interpreter/cmd_io.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef cmd_io_h #define cmd_io_h #include #include "error.h" #include "value.h" struct Core; enum ErrorCode cmd_KEYBOARD(struct Core *core); enum ErrorCode cmd_TOUCHSCREEN(struct Core *core); enum ErrorCode cmd_GAMEPAD(struct Core *core); enum ErrorCode cmd_PAUSE(struct Core *core); struct TypedValue fnc_UP_DOWN_LEFT_RIGHT(struct Core *core); struct TypedValue fnc_BUTTON(struct Core *core); struct TypedValue fnc_TOUCH(struct Core *core); struct TypedValue fnc_TAP(struct Core *core); struct TypedValue fnc_TOUCH_X_Y(struct Core *core); struct TypedValue fnc_PAUSE(struct Core *core); #endif /* cmd_io_h */ ================================================ FILE: core/interpreter/cmd_maths.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #define _USE_MATH_DEFINES #include "cmd_maths.h" #include "core.h" #include #include #include struct TypedValue fnc_math0(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // function enum TokenType type = interpreter->pc->type; ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { switch (type) { case TokenPI: value.v.floatValue = M_PI; break; default: assert(0); break; } } return value; } struct TypedValue fnc_math1(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // function enum TokenType type = interpreter->pc->type; ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // expression struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric); if (xValue.type == ValueTypeError) return xValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { switch (type) { case TokenABS: value.v.floatValue = fabsf(xValue.v.floatValue); break; case TokenACOS: if (xValue.v.floatValue < -1.0 || xValue.v.floatValue > 1.0) return val_makeError(ErrorInvalidParameter); value.v.floatValue = acosf(xValue.v.floatValue); break; case TokenASIN: if (xValue.v.floatValue < -1.0 || xValue.v.floatValue > 1.0) return val_makeError(ErrorInvalidParameter); value.v.floatValue = asinf(xValue.v.floatValue); break; case TokenATAN: value.v.floatValue = atanf(xValue.v.floatValue); break; case TokenCOS: value.v.floatValue = cosf(xValue.v.floatValue); break; case TokenEXP: value.v.floatValue = expf(xValue.v.floatValue); break; case TokenHCOS: value.v.floatValue = coshf(xValue.v.floatValue); break; case TokenHSIN: value.v.floatValue = sinhf(xValue.v.floatValue); break; case TokenHTAN: value.v.floatValue = tanhf(xValue.v.floatValue); break; case TokenINT: value.v.floatValue = floorf(xValue.v.floatValue); break; case TokenLOG: if (xValue.v.floatValue <= 0) return val_makeError(ErrorInvalidParameter); value.v.floatValue = logf(xValue.v.floatValue); break; case TokenSGN: value.v.floatValue = (xValue.v.floatValue > 0) ? 1 : (xValue.v.floatValue < 0) ? BAS_TRUE : BAS_FALSE; break; case TokenSIN: value.v.floatValue = sinf(xValue.v.floatValue); break; case TokenSQR: if (xValue.v.floatValue < 0) return val_makeError(ErrorInvalidParameter); value.v.floatValue = sqrtf(xValue.v.floatValue); break; case TokenTAN: value.v.floatValue = tanf(xValue.v.floatValue); break; default: assert(0); break; } } return value; } struct TypedValue fnc_math2(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // function enum TokenType type = interpreter->pc->type; ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // x expression struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric); if (xValue.type == ValueTypeError) return xValue; // comma if (interpreter->pc->type != TokenComma) return val_makeError(ErrorSyntax); ++interpreter->pc; // y expression struct TypedValue yValue = itp_evaluateExpression(core, TypeClassNumeric); if (yValue.type == ValueTypeError) return yValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { float x = xValue.v.floatValue; float y = yValue.v.floatValue; switch (type) { case TokenMAX: value.v.floatValue = (x > y) ? x : y; break; case TokenMIN: value.v.floatValue = (x < y) ? x : y; break; default: assert(0); break; } } return value; } enum ErrorCode cmd_RANDOMIZE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // RANDOMIZE ++interpreter->pc; // value struct TypedValue value = itp_evaluateExpression(core, TypeClassNumeric); if (value.type == ValueTypeError) return value.v.errorCode; if (interpreter->pass == PassRun) { interpreter->seed = value.v.floatValue; } return itp_endOfCommand(interpreter); } struct TypedValue fnc_RND(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // RND ++interpreter->pc; int x = -1; if (interpreter->pc->type == TokenBracketOpen) { // bracket open ++interpreter->pc; // expression struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric); if (xValue.type == ValueTypeError) return xValue; x = xValue.v.floatValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; } struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { int seed = (1140671485 * interpreter->seed + 12820163) & 0xFFFFFF; interpreter->seed = seed; float rnd = seed / (float)0x1000000; if (x >= 0) { // integer 0...x value.v.floatValue = floorf(rnd * (x + 1)); } else { // float 0..<1 value.v.floatValue = rnd; } } return value; } enum ErrorCode cmd_ADD(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // ADD ++interpreter->pc; enum ErrorCode errorCode = ErrorNone; // Variable enum ValueType valueType = ValueTypeNull; union Value *varValue = itp_readVariable(core, &valueType, &errorCode, false); if (!varValue) return errorCode; if (valueType != ValueTypeFloat) return ErrorTypeMismatch; if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // n vale struct TypedValue nValue = itp_evaluateExpression(core, TypeClassNumeric); if (nValue.type == ValueTypeError) return nValue.v.errorCode; bool hasRange = false; float base = 0; float top = 0; if (interpreter->pc->type == TokenComma) { // comma ++interpreter->pc; // base value struct TypedValue baseValue = itp_evaluateExpression(core, TypeClassNumeric); if (baseValue.type == ValueTypeError) return baseValue.v.errorCode; base = baseValue.v.floatValue; // TO if (interpreter->pc->type != TokenTO) return ErrorSyntax; ++interpreter->pc; // top value struct TypedValue topValue = itp_evaluateExpression(core, TypeClassNumeric); if (topValue.type == ValueTypeError) return topValue.v.errorCode; top = topValue.v.floatValue; hasRange = true; } if (interpreter->pass == PassRun) { varValue->floatValue += nValue.v.floatValue; if (hasRange) { if (varValue->floatValue < base) varValue->floatValue = top; if (varValue->floatValue > top) varValue->floatValue = base; } } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_INC_DEC(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // INC/DEC enum TokenType type = interpreter->pc->type; ++interpreter->pc; enum ErrorCode errorCode = ErrorNone; // Variable enum ValueType valueType = ValueTypeNull; union Value *varValue = itp_readVariable(core, &valueType, &errorCode, false); if (!varValue) return errorCode; if (valueType != ValueTypeFloat) return ErrorTypeMismatch; if (interpreter->pass == PassRun) { switch (type) { case TokenINC: ++varValue->floatValue; break; case TokenDEC: --varValue->floatValue; break; default: assert(0); break; } } return itp_endOfCommand(interpreter); } ================================================ FILE: core/interpreter/cmd_maths.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef cmd_maths_h #define cmd_maths_h #include #include "value.h" struct Core; struct TypedValue fnc_math0(struct Core *core); struct TypedValue fnc_math1(struct Core *core); struct TypedValue fnc_math2(struct Core *core); enum ErrorCode cmd_RANDOMIZE(struct Core *core); struct TypedValue fnc_RND(struct Core *core); enum ErrorCode cmd_ADD(struct Core *core); enum ErrorCode cmd_INC_DEC(struct Core *core); #endif /* cmd_maths_h */ ================================================ FILE: core/interpreter/cmd_memory.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "cmd_memory.h" #include "core.h" #include "data_manager.h" #include struct TypedValue fnc_PEEK(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // PEEK/W/L enum TokenType type = interpreter->pc->type; ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // expression struct TypedValue addressValue = itp_evaluateExpression(core, TypeClassNumeric); if (addressValue.type == ValueTypeError) return addressValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue resultValue; resultValue.type = ValueTypeFloat; if (interpreter->pass == PassRun) { switch (type) { case TokenPEEK: { int peek = machine_peek(core, addressValue.v.floatValue); if (peek == -1) return val_makeError(ErrorIllegalMemoryAccess); resultValue.v.floatValue = peek; break; } case TokenPEEKW: { int peek1 = machine_peek(core, addressValue.v.floatValue); int peek2 = machine_peek(core, addressValue.v.floatValue + 1); if (peek1 == -1 || peek2 == -1) return val_makeError(ErrorIllegalMemoryAccess); int16_t value = peek1 | (peek2 << 8); resultValue.v.floatValue = value; break; } case TokenPEEKL: { int peek1 = machine_peek(core, addressValue.v.floatValue); int peek2 = machine_peek(core, addressValue.v.floatValue + 1); int peek3 = machine_peek(core, addressValue.v.floatValue + 2); int peek4 = machine_peek(core, addressValue.v.floatValue + 3); if (peek1 == -1 || peek2 == -1 || peek3 == -1 || peek4 == -1) return val_makeError(ErrorIllegalMemoryAccess); int32_t value = peek1 | (peek2 << 8) | (peek3 << 16) | (peek4 << 24); resultValue.v.floatValue = value; break; } default: assert(0); } } return resultValue; } enum ErrorCode cmd_POKE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // POKE/W/L enum TokenType type = interpreter->pc->type; ++interpreter->pc; // address value struct TypedValue addressValue = itp_evaluateExpression(core, TypeClassNumeric); if (addressValue.type == ValueTypeError) return addressValue.v.errorCode; if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // poke vale struct TypedValue pokeValue = itp_evaluateExpression(core, TypeClassNumeric); if (pokeValue.type == ValueTypeError) return pokeValue.v.errorCode; if (interpreter->pass == PassRun) { switch (type) { case TokenPOKE: { bool poke = machine_poke(core, addressValue.v.floatValue, pokeValue.v.floatValue); if (!poke) return ErrorIllegalMemoryAccess; break; } case TokenPOKEW: { int16_t value = pokeValue.v.floatValue; bool poke1 = machine_poke(core, addressValue.v.floatValue , value); bool poke2 = machine_poke(core, addressValue.v.floatValue + 1, value >> 8); if (!poke1 || !poke2) return ErrorIllegalMemoryAccess; break; } case TokenPOKEL: { int32_t value = pokeValue.v.floatValue; bool poke1 = machine_poke(core, addressValue.v.floatValue , value); bool poke2 = machine_poke(core, addressValue.v.floatValue + 1, value >> 8); bool poke3 = machine_poke(core, addressValue.v.floatValue + 2, value >> 16); bool poke4 = machine_poke(core, addressValue.v.floatValue + 3, value >> 24); if (!poke1 || !poke2 || !poke3 || !poke4) return ErrorIllegalMemoryAccess; break; } default: assert(0); } } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_FILL(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // FILL ++interpreter->pc; // start value struct TypedValue startValue = itp_evaluateExpression(core, TypeClassNumeric); if (startValue.type == ValueTypeError) return startValue.v.errorCode; if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // length value struct TypedValue lengthValue = itp_evaluateExpression(core, TypeClassNumeric); if (lengthValue.type == ValueTypeError) return lengthValue.v.errorCode; int fill = 0; if (interpreter->pc->type == TokenComma) { ++interpreter->pc; // fill value struct TypedValue fillValue = itp_evaluateExpression(core, TypeClassNumeric); if (fillValue.type == ValueTypeError) return fillValue.v.errorCode; fill = fillValue.v.floatValue; } if (interpreter->pass == PassRun) { int start = startValue.v.floatValue; int length = lengthValue.v.floatValue; for (int i = 0; i < length; i++) { bool poke = machine_poke(core, start + i, fill); if (!poke) return ErrorIllegalMemoryAccess; } interpreter->cycles += length; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_COPY(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // COPY ++interpreter->pc; // source value struct TypedValue sourceValue = itp_evaluateExpression(core, TypeClassNumeric); if (sourceValue.type == ValueTypeError) return sourceValue.v.errorCode; if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // length value struct TypedValue lengthValue = itp_evaluateExpression(core, TypeClassNumeric); if (lengthValue.type == ValueTypeError) return lengthValue.v.errorCode; if (interpreter->pc->type != TokenTO) return ErrorSyntax; ++interpreter->pc; // destination value struct TypedValue destinationValue = itp_evaluateExpression(core, TypeClassNumeric); if (destinationValue.type == ValueTypeError) return destinationValue.v.errorCode; if (interpreter->pass == PassRun) { int source = sourceValue.v.floatValue; int length = lengthValue.v.floatValue; int destination = destinationValue.v.floatValue; if (source < destination) { for (int i = length - 1; i >= 0; i--) { int peek = machine_peek(core, source + i); if (peek == -1) return ErrorIllegalMemoryAccess; bool poke = machine_poke(core, destination + i, peek); if (!poke) return ErrorIllegalMemoryAccess; } } else if (source > destination) { for (int i = 0; i < length; i++) { int peek = machine_peek(core, source + i); if (peek == -1) return ErrorIllegalMemoryAccess; bool poke = machine_poke(core, destination + i, peek); if (!poke) return ErrorIllegalMemoryAccess; } } interpreter->cycles += length; } return itp_endOfCommand(interpreter); } struct TypedValue fnc_ROM_SIZE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // ROM/SIZE enum TokenType type = interpreter->pc->type; ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // index expression struct TypedValue indexValue = itp_evaluateNumericExpression(core, 0, MAX_ENTRIES - 1); if (indexValue.type == ValueTypeError) return indexValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { int index = indexValue.v.floatValue; if (type == TokenSIZE) { value.v.floatValue = interpreter->romDataManager.entries[index].length; } else { value.v.floatValue = interpreter->romDataManager.entries[index].start; } } return value; } enum ErrorCode cmd_ROL_ROR(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // ROL/ROR enum TokenType type = interpreter->pc->type; ++interpreter->pc; // address value struct TypedValue addressValue = itp_evaluateExpression(core, TypeClassNumeric); if (addressValue.type == ValueTypeError) return addressValue.v.errorCode; if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // n vale struct TypedValue nValue = itp_evaluateExpression(core, TypeClassNumeric); if (nValue.type == ValueTypeError) return nValue.v.errorCode; if (interpreter->pass == PassRun) { int value = machine_peek(core, addressValue.v.floatValue); if (value == -1) return ErrorIllegalMemoryAccess; int n = (int)nValue.v.floatValue; if (type == TokenROR) { n = -n; } n &= 0x07; value = value << n; value = value | (value >> 8); bool poke = machine_poke(core, addressValue.v.floatValue, value); if (!poke) return ErrorIllegalMemoryAccess; } return itp_endOfCommand(interpreter); } ================================================ FILE: core/interpreter/cmd_memory.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef cmd_memory_h #define cmd_memory_h #include #include "error.h" struct Core; struct TypedValue fnc_PEEK(struct Core *core); enum ErrorCode cmd_POKE(struct Core *core); enum ErrorCode cmd_FILL(struct Core *core); enum ErrorCode cmd_COPY(struct Core *core); struct TypedValue fnc_ROM_SIZE(struct Core *core); enum ErrorCode cmd_ROL_ROR(struct Core *core); #endif /* cmd_memory_h */ ================================================ FILE: core/interpreter/cmd_screen.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "cmd_screen.h" #include "core.h" #include #include #include "interpreter_utils.h" enum ErrorCode cmd_PALETTE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // PALETTE ++interpreter->pc; // n value struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_PALETTES - 1); if (nValue.type == ValueTypeError) return nValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // c0 value struct TypedValue c0Value = itp_evaluateOptionalNumericExpression(core, 0, 63); if (c0Value.type == ValueTypeError) return c0Value.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // c1 value struct TypedValue c1Value = itp_evaluateOptionalNumericExpression(core, 0, 63); if (c1Value.type == ValueTypeError) return c1Value.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // c2 value struct TypedValue c2Value = itp_evaluateOptionalNumericExpression(core, 0, 63); if (c2Value.type == ValueTypeError) return c2Value.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // c3 value struct TypedValue c3Value = itp_evaluateOptionalNumericExpression(core, 0, 63); if (c3Value.type == ValueTypeError) return c3Value.v.errorCode; if (interpreter->pass == PassRun) { int n = nValue.v.floatValue; uint8_t *palColors = &core->machine->colorRegisters.colors[n * 4]; if (c0Value.type != ValueTypeNull) palColors[0] = c0Value.v.floatValue; if (c1Value.type != ValueTypeNull) palColors[1] = c1Value.v.floatValue; if (c2Value.type != ValueTypeNull) palColors[2] = c2Value.v.floatValue; if (c3Value.type != ValueTypeNull) palColors[3] = c3Value.v.floatValue; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_SCROLL(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // SCROLL ++interpreter->pc; // bg value struct TypedValue bgValue = itp_evaluateNumericExpression(core, 0, 1); if (bgValue.type == ValueTypeError) return bgValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // x value struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric); if (xValue.type == ValueTypeError) return xValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // y value struct TypedValue yValue = itp_evaluateExpression(core, TypeClassNumeric); if (yValue.type == ValueTypeError) return yValue.v.errorCode; if (interpreter->pass == PassRun) { struct VideoRegisters *reg = &core->machine->videoRegisters; int bg = bgValue.v.floatValue; int x = (int)xValue.v.floatValue; int y = (int)yValue.v.floatValue; if (bg == 0) { reg->scrollAX = x & 0xFF; reg->scrollAY = y & 0xFF; reg->scrollMSB.aX = (x >> 8) & 1; reg->scrollMSB.aY = (y >> 8) & 1; } else { reg->scrollBX = x & 0xFF; reg->scrollBY = y & 0xFF; reg->scrollMSB.bX = (x >> 8) & 1; reg->scrollMSB.bY = (y >> 8) & 1; } } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_DISPLAY(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // DISPLAY ++interpreter->pc; // obsolete syntax! // atrb value struct TypedValue aValue = itp_evaluateDisplayAttributes(core, core->machine->videoRegisters.attr); if (aValue.type == ValueTypeError) return aValue.v.errorCode; if (interpreter->pass == PassRun) { core->machine->videoRegisters.attr.value = aValue.v.floatValue; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_SPRITE_VIEW(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // SPRITE VIEW ++interpreter->pc; ++interpreter->pc; // ON/OFF enum TokenType type = interpreter->pc->type; if (type != TokenON && type != TokenOFF) return ErrorSyntax; ++interpreter->pc; if (interpreter->pass == PassRun) { core->machine->videoRegisters.attr.spritesEnabled = type == TokenON ? 1 : 0; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_BG_VIEW(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // BG VIEW ++interpreter->pc; ++interpreter->pc; // ON/OFF enum TokenType type = interpreter->pc->type; if (type != TokenON && type != TokenOFF) return ErrorSyntax; ++interpreter->pc; // bg value struct TypedValue bgValue = itp_evaluateNumericExpression(core, 0, 1); if (bgValue.type == ValueTypeError) return bgValue.v.errorCode; if (interpreter->pass == PassRun) { int value = type == TokenON ? 1 : 0; if (bgValue.v.floatValue == 0) { core->machine->videoRegisters.attr.planeAEnabled = value; } else { core->machine->videoRegisters.attr.planeBEnabled = value; } } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_CELL_SIZE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // CELL SIZE ++interpreter->pc; ++interpreter->pc; // bg value struct TypedValue bgValue = itp_evaluateNumericExpression(core, 0, 1); if (bgValue.type == ValueTypeError) return bgValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // size value struct TypedValue sValue = itp_evaluateOptionalNumericExpression(core, 0, 1); if (sValue.type == ValueTypeError) return sValue.v.errorCode; if (interpreter->pass == PassRun) { if (bgValue.v.floatValue == 0) { core->machine->videoRegisters.attr.planeACellSize = sValue.v.floatValue; } else { core->machine->videoRegisters.attr.planeBCellSize = sValue.v.floatValue; } } return itp_endOfCommand(interpreter); } struct TypedValue fnc_COLOR(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // COLOR ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // pal expression struct TypedValue pValue = itp_evaluateNumericExpression(core, 0, NUM_PALETTES - 1); if (pValue.type == ValueTypeError) return pValue; // comma if (interpreter->pc->type != TokenComma) return val_makeError(ErrorSyntax); ++interpreter->pc; // pal expression struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, 3); if (nValue.type == ValueTypeError) return nValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { int p = pValue.v.floatValue; int n = nValue.v.floatValue; value.v.floatValue = core->machine->colorRegisters.colors[p * 4 + n]; } return value; } struct TypedValue fnc_screen0(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // function enum TokenType type = interpreter->pc->type; ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { switch (type) { case TokenTIMER: value.v.floatValue = core->interpreter->timer; break; case TokenRASTER: value.v.floatValue = core->machine->videoRegisters.rasterLine; break; case TokenDISPLAY: // obsolete syntax! value.v.floatValue = core->machine->videoRegisters.attr.value; break; default: assert(0); break; } } return value; } struct TypedValue fnc_SCROLL_X_Y(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // SCROLL.? enum TokenType type = interpreter->pc->type; ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // bg value struct TypedValue bgValue = itp_evaluateNumericExpression(core, 0, 1); if (bgValue.type == ValueTypeError) return bgValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { int bg = bgValue.v.floatValue; struct VideoRegisters *reg = &core->machine->videoRegisters; switch (type) { case TokenSCROLLX: if (bg == 0) { value.v.floatValue = reg->scrollAX | (reg->scrollMSB.aX << 8); } else { value.v.floatValue = reg->scrollBX | (reg->scrollMSB.bX << 8); } break; case TokenSCROLLY: if (bg == 0) { value.v.floatValue = reg->scrollAY | (reg->scrollMSB.aY << 8); } else { value.v.floatValue = reg->scrollBY | (reg->scrollMSB.bY << 8); } break; default: assert(0); break; } } return value; } ================================================ FILE: core/interpreter/cmd_screen.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef cmd_screen_h #define cmd_screen_h #include #include "error.h" #include "value.h" struct Core; enum ErrorCode cmd_PALETTE(struct Core *core); enum ErrorCode cmd_SCROLL(struct Core *core); enum ErrorCode cmd_DISPLAY(struct Core *core); enum ErrorCode cmd_SPRITE_VIEW(struct Core *core); enum ErrorCode cmd_BG_VIEW(struct Core *core); enum ErrorCode cmd_CELL_SIZE(struct Core *core); struct TypedValue fnc_COLOR(struct Core *core); struct TypedValue fnc_screen0(struct Core *core); struct TypedValue fnc_SCROLL_X_Y(struct Core *core); #endif /* cmd_screen_h */ ================================================ FILE: core/interpreter/cmd_sprites.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "cmd_sprites.h" #include "core.h" #include "value.h" #include "cmd_text.h" #include "interpreter_utils.h" #include "sprites_lib.h" #include enum ErrorCode cmd_SPRITE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // SPRITE ++interpreter->pc; // n value struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_SPRITES - 1); if (nValue.type == ValueTypeError) return nValue.v.errorCode; // comma if (interpreter->pc->type == TokenComma) { ++interpreter->pc; // x value struct TypedValue xValue = itp_evaluateOptionalExpression(core, TypeClassNumeric); if (xValue.type == ValueTypeError) return xValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // y value struct TypedValue yValue = itp_evaluateOptionalExpression(core, TypeClassNumeric); if (yValue.type == ValueTypeError) return yValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // c value struct TypedValue cValue = itp_evaluateOptionalNumericExpression(core, 0, NUM_CHARACTERS - 1); if (cValue.type == ValueTypeError) return cValue.v.errorCode; if (interpreter->pass == PassRun) { int n = nValue.v.floatValue; struct Sprite *sprite = &core->machine->spriteRegisters.sprites[n]; if (xValue.type != ValueTypeNull) sprite->x = ((int)xValue.v.floatValue + SPRITE_OFFSET_X) & 0xFF; if (yValue.type != ValueTypeNull) sprite->y = ((int)yValue.v.floatValue + SPRITE_OFFSET_Y) & 0xFF; if (cValue.type != ValueTypeNull) sprite->character = cValue.v.floatValue; } } else { struct SimpleAttributes attrs; enum ErrorCode attrsError = itp_evaluateSimpleAttributes(core, &attrs); if (attrsError != ErrorNone) return attrsError; if (interpreter->pass == PassRun) { int n = nValue.v.floatValue; struct Sprite *sprite = &core->machine->spriteRegisters.sprites[n]; if (attrs.pal >= 0) sprite->attr.palette = attrs.pal; if (attrs.flipX >= 0) sprite->attr.flipX = attrs.flipX; if (attrs.flipY >= 0) sprite->attr.flipY = attrs.flipY; if (attrs.prio >= 0) sprite->attr.priority = attrs.prio; if (attrs.size >= 0) sprite->attr.size = attrs.size; } } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_SPRITE_A(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // SPRITE.A ++interpreter->pc; // n value struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_SPRITES - 1); if (nValue.type == ValueTypeError) return nValue.v.errorCode; struct Sprite *sprite = NULL; if (interpreter->pass == PassRun) { int n = nValue.v.floatValue; sprite = &core->machine->spriteRegisters.sprites[n]; } // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; union CharacterAttributes attr; if (sprite) { attr = sprite->attr; } else { attr.value = 0; } // attr value struct TypedValue aValue = itp_evaluateCharAttributes(core, attr); if (aValue.type == ValueTypeError) return aValue.v.errorCode; if (interpreter->pass == PassRun) { sprite->attr.value = aValue.v.floatValue; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_SPRITE_OFF(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // SPRITE ++interpreter->pc; // OFF if (interpreter->pc->type != TokenOFF) return ErrorSyntax; ++interpreter->pc; int from = 0; int to = NUM_SPRITES - 1; if (!itp_isEndOfCommand(interpreter)) { // from value struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_SPRITES - 1); if (nValue.type == ValueTypeError) return nValue.v.errorCode; from = nValue.v.floatValue; to = from; // TO if (interpreter->pc->type == TokenTO) { ++interpreter->pc; // to value struct TypedValue mValue = itp_evaluateNumericExpression(core, 0, NUM_SPRITES - 1); if (mValue.type == ValueTypeError) return mValue.v.errorCode; to = mValue.v.floatValue; } } if (interpreter->pass == PassRun) { for (int i = from; i <= to; i++) { struct Sprite *sprite = &core->machine->spriteRegisters.sprites[i]; sprite->x = 0; sprite->y = 0; } } return itp_endOfCommand(interpreter); } struct TypedValue fnc_SPRITE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // SPRITE.? enum TokenType type = interpreter->pc->type; ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // expression struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_SPRITES - 1); if (nValue.type == ValueTypeError) return nValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { int n = nValue.v.floatValue; struct Sprite *sprite = &core->machine->spriteRegisters.sprites[n]; switch (type) { case TokenSPRITEX: value.v.floatValue = sprite->x - SPRITE_OFFSET_X; break; case TokenSPRITEY: value.v.floatValue = sprite->y - SPRITE_OFFSET_Y; break; case TokenSPRITEC: value.v.floatValue = sprite->character; break; case TokenSPRITEA: value.v.floatValue = sprite->attr.value; break; default: assert(0); break; } } return value; } struct TypedValue fnc_SPRITE_HIT(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // SPRITE ++interpreter->pc; // HIT if (interpreter->pc->type != TokenHIT) return val_makeError(ErrorSyntax); ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // sprite number struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_SPRITES - 1); if (nValue.type == ValueTypeError) return nValue; int first = 0; int last = NUM_SPRITES - 1; // other sprite number if (interpreter->pc->type == TokenComma) { ++interpreter->pc; struct TypedValue otherValue = itp_evaluateNumericExpression(core, 0, NUM_SPRITES - 1); if (otherValue.type == ValueTypeError) return otherValue; first = otherValue.v.floatValue; last = first; // last sprite number if (interpreter->pc->type == TokenTO) { ++interpreter->pc; struct TypedValue lastValue = itp_evaluateNumericExpression(core, 0, NUM_SPRITES - 1); if (lastValue.type == ValueTypeError) return lastValue; last = lastValue.v.floatValue; } } // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { bool hits = sprlib_checkCollision(&interpreter->spritesLib, nValue.v.floatValue, first, last); value.v.floatValue = hits ? BAS_TRUE : BAS_FALSE; } return value; } struct TypedValue fnc_HIT(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // HIT ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { value.v.floatValue = interpreter->spritesLib.lastHit; } return value; } ================================================ FILE: core/interpreter/cmd_sprites.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef cmd_sprites_h #define cmd_sprites_h #include #include "error.h" #include "value.h" struct Core; enum ErrorCode cmd_SPRITE(struct Core *core); enum ErrorCode cmd_SPRITE_A(struct Core *core); enum ErrorCode cmd_SPRITE_OFF(struct Core *core); struct TypedValue fnc_SPRITE(struct Core *core); struct TypedValue fnc_SPRITE_HIT(struct Core *core); struct TypedValue fnc_HIT(struct Core *core); #endif /* cmd_sprites_h */ ================================================ FILE: core/interpreter/cmd_strings.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "cmd_strings.h" #include "core.h" #include #include #include #include #include "rcstring.h" struct TypedValue fnc_ASC(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // ASC ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // expression struct TypedValue stringValue = itp_evaluateExpression(core, TypeClassString); if (stringValue.type == ValueTypeError) return stringValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { char ch = stringValue.v.stringValue->chars[0]; rcstring_release(stringValue.v.stringValue); if (ch == 0) return val_makeError(ErrorInvalidParameter); value.v.floatValue = ch; } return value; } struct TypedValue fnc_BIN_HEX(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // BIN$/HEX$ enum TokenType type = interpreter->pc->type; ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // x expression struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric); if (xValue.type == ValueTypeError) return xValue; int maxLen = (type == TokenHEX) ? 8 : 16; int width = 0; if (interpreter->pc->type == TokenComma) { // comma ++interpreter->pc; // width expression struct TypedValue widthValue = itp_evaluateNumericExpression(core, 0, maxLen); if (widthValue.type == ValueTypeError) return widthValue; width = widthValue.v.floatValue; } // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue resultValue; resultValue.type = ValueTypeString; if (interpreter->pass == PassRun) { int x = xValue.v.floatValue; struct RCString *rcstring = rcstring_new(NULL, maxLen); if (!rcstring) return val_makeError(ErrorOutOfMemory); if (type == TokenBIN) { txtlib_itobin(rcstring->chars, maxLen + 1, width, x); } else if (type == TokenHEX) { snprintf(rcstring->chars, maxLen + 1, "%0*X", width, x); } resultValue.v.stringValue = rcstring; interpreter->cycles += maxLen; } return resultValue; } struct TypedValue fnc_CHR(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // CHR$ ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // expression struct TypedValue numericValue = itp_evaluateNumericExpression(core, 0, 255); if (numericValue.type == ValueTypeError) return numericValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue resultValue; resultValue.type = ValueTypeString; if (interpreter->pass == PassRun) { char ch = numericValue.v.floatValue; struct RCString *rcstring = rcstring_new(&ch, 1); if (!rcstring) return val_makeError(ErrorOutOfMemory); resultValue.v.stringValue = rcstring; interpreter->cycles += 1; } return resultValue; } struct TypedValue fnc_INKEY(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // INKEY$ ++interpreter->pc; struct TypedValue resultValue; resultValue.type = ValueTypeString; if (interpreter->pass == PassRun) { if (!core->machine->ioRegisters.attr.keyboardEnabled) return val_makeError(ErrorKeyboardNotEnabled); char key = core->machine->ioRegisters.key; if (key) { core->machine->ioRegisters.key = 0; struct RCString *rcstring = rcstring_new(&key, 1); if (!rcstring) return val_makeError(ErrorOutOfMemory); resultValue.v.stringValue = rcstring; interpreter->cycles += 1; } else { resultValue.v.stringValue = interpreter->nullString; rcstring_retain(resultValue.v.stringValue); } } return resultValue; } struct TypedValue fnc_INSTR(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // INSTR ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // string expression struct TypedValue stringValue = itp_evaluateExpression(core, TypeClassString); if (stringValue.type == ValueTypeError) return stringValue; // comma if (interpreter->pc->type != TokenComma) return val_makeError(ErrorSyntax); ++interpreter->pc; // search value struct TypedValue searchValue = itp_evaluateExpression(core, TypeClassString); if (searchValue.type == ValueTypeError) return searchValue; int startIndex = 0; if (interpreter->pc->type == TokenComma) { // comma ++interpreter->pc; // number value struct TypedValue posValue = itp_evaluateExpression(core, TypeClassNumeric); if (posValue.type == ValueTypeError) return posValue; startIndex = posValue.v.floatValue - 1; } // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue resultValue; resultValue.type = ValueTypeFloat; if (interpreter->pass == PassRun) { char *string = stringValue.v.stringValue->chars; char *search = searchValue.v.stringValue->chars; size_t stringlen = strlen(string); if (startIndex >= stringlen || search[0] == 0) { resultValue.v.floatValue = 0; } else { char *found = strstr(&string[startIndex], search); if (found) { resultValue.v.floatValue = (found - string) + 1; } else { resultValue.v.floatValue = 0; } } rcstring_release(stringValue.v.stringValue); rcstring_release(searchValue.v.stringValue); } return resultValue; } struct TypedValue fnc_LEFTStr_RIGHTStr(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // LEFT$/RIGHT$ enum TokenType type = interpreter->pc->type; ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // expression struct TypedValue stringValue = itp_evaluateExpression(core, TypeClassString); if (stringValue.type == ValueTypeError) return stringValue; // comma if (interpreter->pc->type != TokenComma) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue numberValue = itp_evaluateExpression(core, TypeClassNumeric); if (numberValue.type == ValueTypeError) return numberValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue resultValue; resultValue.type = ValueTypeString; if (interpreter->pass == PassRun) { if (numberValue.v.floatValue < 0) return val_makeError(ErrorInvalidParameter); size_t len = strlen(stringValue.v.stringValue->chars); size_t number = numberValue.v.floatValue; if (number < len) { size_t start = (type == TokenLEFTStr) ? 0 : len - number; struct RCString *rcstring = rcstring_new(&stringValue.v.stringValue->chars[start], number); if (!rcstring) return val_makeError(ErrorOutOfMemory); resultValue.v.stringValue = rcstring; interpreter->cycles += number; } else { resultValue.v.stringValue = stringValue.v.stringValue; rcstring_retain(resultValue.v.stringValue); } rcstring_release(stringValue.v.stringValue); } return resultValue; } struct TypedValue fnc_LEN(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // LEN ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // expression struct TypedValue stringValue = itp_evaluateExpression(core, TypeClassString); if (stringValue.type == ValueTypeError) return stringValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { value.v.floatValue = strlen(stringValue.v.stringValue->chars); rcstring_release(stringValue.v.stringValue); } return value; } struct TypedValue fnc_MID(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // MID$ ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // string expression struct TypedValue stringValue = itp_evaluateExpression(core, TypeClassString); if (stringValue.type == ValueTypeError) return stringValue; // comma if (interpreter->pc->type != TokenComma) return val_makeError(ErrorSyntax); ++interpreter->pc; // position value struct TypedValue posValue = itp_evaluateExpression(core, TypeClassNumeric); if (posValue.type == ValueTypeError) return posValue; // comma if (interpreter->pc->type != TokenComma) return val_makeError(ErrorSyntax); ++interpreter->pc; // number value struct TypedValue numberValue = itp_evaluateExpression(core, TypeClassNumeric); if (numberValue.type == ValueTypeError) return numberValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue resultValue; resultValue.type = ValueTypeString; if (interpreter->pass == PassRun) { if (numberValue.v.floatValue < 0) return val_makeError(ErrorInvalidParameter); if (posValue.v.floatValue < 1) return val_makeError(ErrorInvalidParameter); size_t len = strlen(stringValue.v.stringValue->chars); size_t index = posValue.v.floatValue - 1; size_t number = numberValue.v.floatValue; if (index >= len) { resultValue.v.stringValue = interpreter->nullString; rcstring_retain(resultValue.v.stringValue); } else if (index > 0 || number < len) { if (index + number > len) { number = len - index; } struct RCString *rcstring = rcstring_new(&stringValue.v.stringValue->chars[index], number); if (!rcstring) return val_makeError(ErrorOutOfMemory); resultValue.v.stringValue = rcstring; interpreter->cycles += number; } else { resultValue.v.stringValue = stringValue.v.stringValue; rcstring_retain(resultValue.v.stringValue); } rcstring_release(stringValue.v.stringValue); } return resultValue; } struct TypedValue fnc_STR(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // STR$ ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // expression struct TypedValue numericValue = itp_evaluateExpression(core, TypeClassNumeric); if (numericValue.type == ValueTypeError) return numericValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue resultValue; resultValue.type = ValueTypeString; if (interpreter->pass == PassRun) { struct RCString *rcstring = rcstring_new(NULL, 20); if (!rcstring) return val_makeError(ErrorOutOfMemory); snprintf(rcstring->chars, 20, "%0.7g", numericValue.v.floatValue); resultValue.v.stringValue = rcstring; interpreter->cycles += strlen(rcstring->chars); } return resultValue; } struct TypedValue fnc_VAL(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // VAL ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // expression struct TypedValue stringValue = itp_evaluateExpression(core, TypeClassString); if (stringValue.type == ValueTypeError) return stringValue; // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { value.v.floatValue = atof(stringValue.v.stringValue->chars); rcstring_release(stringValue.v.stringValue); } return value; } enum ErrorCode cmd_LEFT_RIGHT(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // LEFT$/RIGHT$ enum TokenType type = interpreter->pc->type; ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return ErrorSyntax; ++interpreter->pc; // variable enum ErrorCode errorCode = ErrorNone; enum ValueType valueType = ValueTypeNull; union Value *varValue = itp_readVariable(core, &valueType, &errorCode, true); if (!varValue) return errorCode; if (valueType != ValueTypeString) return ErrorTypeMismatch; size_t number = SIZE_MAX; if (interpreter->pc->type == TokenComma) { // comma ++interpreter->pc; // number expression struct TypedValue numberValue = itp_evaluateExpression(core, TypeClassNumeric); if (numberValue.type == ValueTypeError) return numberValue.v.errorCode; number = numberValue.v.floatValue; } // bracket close if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax; ++interpreter->pc; // equal sign if (interpreter->pc->type != TokenEq) return ErrorSyntax; ++interpreter->pc; // replace expression struct TypedValue replaceValue = itp_evaluateExpression(core, TypeClassString); if (replaceValue.type == ValueTypeError) return replaceValue.v.errorCode; if (interpreter->pass == PassRun) { size_t resultLen = strlen(varValue->stringValue->chars); struct RCString *resultRCString = varValue->stringValue; if (resultRCString->refCount > 1) { // copy string if shared resultRCString = rcstring_new(varValue->stringValue->chars, resultLen); rcstring_release(varValue->stringValue); varValue->stringValue = resultRCString; } char *resultString = resultRCString->chars; char *replaceString = replaceValue.v.stringValue->chars; size_t replaceLen = strlen(replaceString); if (number > replaceLen) { number = replaceLen; } if (number > resultLen) { number = resultLen; } if (type == TokenLEFTStr) { for (size_t i = 0; i < number; i++) { resultString[i] = replaceString[i]; } } else if (type == TokenRIGHTStr) { for (size_t i = 0; i < number; i++) { resultString[resultLen - 1 - i] = replaceString[replaceLen - 1 - i]; } } interpreter->cycles += number; rcstring_release(replaceValue.v.stringValue); } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_MID(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // MID$ ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return ErrorSyntax; ++interpreter->pc; // variable enum ErrorCode errorCode = ErrorNone; enum ValueType valueType = ValueTypeNull; union Value *varValue = itp_readVariable(core, &valueType, &errorCode, true); if (!varValue) return errorCode; if (valueType != ValueTypeString) return ErrorTypeMismatch; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // position expression struct TypedValue posValue = itp_evaluateExpression(core, TypeClassNumeric); if (posValue.type == ValueTypeError) return posValue.v.errorCode; size_t number = SIZE_MAX; if (interpreter->pc->type == TokenComma) { // comma ++interpreter->pc; // number expression struct TypedValue numberValue = itp_evaluateExpression(core, TypeClassNumeric); if (numberValue.type == ValueTypeError) return numberValue.v.errorCode; number = numberValue.v.floatValue; } // bracket close if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax; ++interpreter->pc; // equal sign if (interpreter->pc->type != TokenEq) return ErrorSyntax; ++interpreter->pc; // replace expression struct TypedValue replaceValue = itp_evaluateExpression(core, TypeClassString); if (replaceValue.type == ValueTypeError) return replaceValue.v.errorCode; if (interpreter->pass == PassRun) { size_t index = posValue.v.floatValue - 1; size_t resultLen = strlen(varValue->stringValue->chars); struct RCString *resultRCString = varValue->stringValue; if (resultRCString->refCount > 1) { // copy string if shared resultRCString = rcstring_new(varValue->stringValue->chars, resultLen); rcstring_release(varValue->stringValue); varValue->stringValue = resultRCString; } if (index < resultLen) { char *resultString = resultRCString->chars; char *replaceString = replaceValue.v.stringValue->chars; size_t replaceLen = strlen(replaceString); if (number > replaceLen) { number = replaceLen; } if (index + number > resultLen) { number = resultLen - index; } for (size_t i = 0; i < number; i++) { resultString[index + i] = replaceString[i]; } interpreter->cycles += number; } rcstring_release(replaceValue.v.stringValue); } return itp_endOfCommand(interpreter); } ================================================ FILE: core/interpreter/cmd_strings.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef cmd_strings_h #define cmd_strings_h #include #include "value.h" struct Core; struct TypedValue fnc_ASC(struct Core *core); struct TypedValue fnc_BIN_HEX(struct Core *core); struct TypedValue fnc_CHR(struct Core *core); struct TypedValue fnc_INKEY(struct Core *core); struct TypedValue fnc_INSTR(struct Core *core); struct TypedValue fnc_LEFTStr_RIGHTStr(struct Core *core); struct TypedValue fnc_LEN(struct Core *core); struct TypedValue fnc_MID(struct Core *core); struct TypedValue fnc_STR(struct Core *core); struct TypedValue fnc_VAL(struct Core *core); enum ErrorCode cmd_LEFT_RIGHT(struct Core *core); enum ErrorCode cmd_MID(struct Core *core); #endif /* cmd_strings_h */ ================================================ FILE: core/interpreter/cmd_subs.c ================================================ // // Copyright 2018-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "cmd_subs.h" #include "core.h" enum ErrorCode cmd_CALL(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // CALL struct Token *tokenCALL = interpreter->pc; ++interpreter->pc; // Identifier if (interpreter->pc->type != TokenIdentifier) return ErrorExpectedSubprogramName; struct Token *tokenSubIdentifier = interpreter->pc; ++interpreter->pc; if (interpreter->pass == PassPrepare) { struct SubItem *item = tok_getSub(&interpreter->tokenizer, tokenSubIdentifier->symbolIndex); if (!item) return ErrorUndefinedSubprogram; tokenCALL->jumpToken = item->token; } // optional arguments int numArguments = 0; if (interpreter->pc->type == TokenBracketOpen) { do { // bracket or comma ++interpreter->pc; // argument struct Token *tokens = interpreter->pc; if ((interpreter->pc->type == TokenIdentifier || interpreter->pc->type == TokenStringIdentifier) && tokens[1].type == TokenBracketOpen && tokens[2].type == TokenBracketClose) { // pass array by reference if (interpreter->pass == PassRun) { struct ArrayVariable *variable = var_getArrayVariable(interpreter, interpreter->pc->symbolIndex, interpreter->subLevel); if (!variable) return ErrorArrayNotDimensionized; enum ErrorCode errorCode = ErrorNone; var_createArrayVariable(interpreter, &errorCode, numArguments + 1, interpreter->subLevel + 1, variable); if (errorCode != ErrorNone) return errorCode; } interpreter->pc += 3; } else { // expression struct TypedValue value = itp_evaluateExpression(core, TypeClassAny); if (value.type == ValueTypeError) return value.v.errorCode; if (interpreter->pass == PassRun) { enum ErrorCode errorCode = ErrorNone; if (interpreter->lastVariableValue) { // pass by reference (simple variable or array element) enum ErrorCode errorCode = ErrorNone; var_createSimpleVariable(interpreter, &errorCode, numArguments + 1, interpreter->subLevel + 1, value.type, interpreter->lastVariableValue); if (errorCode != ErrorNone) return errorCode; } else { // pass by value struct SimpleVariable *variable = var_createSimpleVariable(interpreter, &errorCode, numArguments + 1, interpreter->subLevel + 1, value.type, NULL); if (!variable) return errorCode; variable->v = value.v; } } } ++numArguments; } while (interpreter->pc->type == TokenComma); if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax; ++interpreter->pc; } if (interpreter->pass == PassRun) { enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeCALL, interpreter->pc); if (errorCode != ErrorNone) return errorCode; interpreter->pc = tokenCALL->jumpToken; // after sub name interpreter->subLevel++; // parameters if (interpreter->pc->type == TokenBracketOpen) { int parameterIndex = 0; do { if (parameterIndex >= numArguments) return ErrorArgumentCountMismatch; // bracket or comma ++interpreter->pc; // parameter struct Token *tokenIdentifier = interpreter->pc; if (tokenIdentifier->type != TokenIdentifier && tokenIdentifier->type != TokenStringIdentifier) return ErrorSyntax; enum ValueType varType = itp_getIdentifierTokenValueType(tokenIdentifier); struct Token *nextToken = interpreter->pc + 1; if (nextToken->type == TokenBracketOpen) { // array struct ArrayVariable *variable = var_getArrayVariable(interpreter, parameterIndex + 1, interpreter->subLevel); if (!variable || variable->type != varType) return ErrorTypeMismatch; variable->symbolIndex = tokenIdentifier->symbolIndex; interpreter->pc += 2; if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax; ++interpreter->pc; } else { // simple variable struct SimpleVariable *variable = var_getSimpleVariable(interpreter, parameterIndex + 1, interpreter->subLevel); if (!variable || variable->type != varType) return ErrorTypeMismatch; variable->symbolIndex = tokenIdentifier->symbolIndex; ++interpreter->pc; } ++parameterIndex; } while (interpreter->pc->type == TokenComma); if (parameterIndex < numArguments) return ErrorArgumentCountMismatch; if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax; ++interpreter->pc; } else if (numArguments > 0) { return ErrorArgumentCountMismatch; } return ErrorNone; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_SUB(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // SUB struct Token *tokenSUB = interpreter->pc; ++interpreter->pc; // Identifier if (interpreter->pc->type != TokenIdentifier) return ErrorExpectedSubprogramName; ++interpreter->pc; // parameters if (interpreter->pc->type == TokenBracketOpen) { do { // bracket or comma ++interpreter->pc; // parameter struct Token *tokenIdentifier = interpreter->pc; if (tokenIdentifier->type != TokenIdentifier && tokenIdentifier->type != TokenStringIdentifier) return ErrorSyntax; ++interpreter->pc; if (interpreter->pc->type == TokenBracketOpen) { ++interpreter->pc; if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax; ++interpreter->pc; } } while (interpreter->pc->type == TokenComma); if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax; ++interpreter->pc; } if (interpreter->pass == PassPrepare) { if (interpreter->numLabelStackItems > 0) { return ErrorSubCannotBeNested; } enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeSUB, tokenSUB); if (errorCode != ErrorNone) return errorCode; interpreter->subLevel++; // Eol if (interpreter->pc->type != TokenEol) return ErrorSyntax; ++interpreter->pc; } else if (interpreter->pass == PassRun) { interpreter->pc = tokenSUB->jumpToken; // after END SUB } return ErrorNone; } enum ErrorCode cmd_END_SUB(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // END SUB ++interpreter->pc; ++interpreter->pc; if (interpreter->pass == PassPrepare) { struct LabelStackItem *item = lab_popLabelStackItem(interpreter); if (!item) { return ErrorEndSubWithoutSub; } else if (item->type == LabelTypeSUB) { item->token->jumpToken = interpreter->pc; } else { enum ErrorCode errorCode = itp_labelStackError(item); return errorCode != ErrorNone ? errorCode : ErrorEndSubWithoutSub; } // Eol if (interpreter->pc->type != TokenEol) return ErrorSyntax; ++interpreter->pc; } else if (interpreter->pass == PassRun) { struct LabelStackItem *itemCALL = lab_popLabelStackItem(interpreter); if (!itemCALL) return ErrorEndSubWithoutSub; // clean local variables var_freeSimpleVariables(interpreter, interpreter->subLevel); var_freeArrayVariables(interpreter, interpreter->subLevel); if (itemCALL->type == LabelTypeONCALL) { // exit from interrupt interpreter->exitEvaluation = true; } else if (itemCALL->type == LabelTypeCALL) { // jump back interpreter->pc = itemCALL->token; // after CALL } else { return ErrorEndSubWithoutSub; } } interpreter->subLevel--; return ErrorNone; } /* enum ErrorCode cmd_SHARED(struct Core *core) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pass == PassPrepare && interpreter->subLevel == 0) return ErrorSharedOutsideOfASubprogram; do { // SHARED or comma ++interpreter->pc; // identifier struct Token *tokenIdentifier = interpreter->pc; if (tokenIdentifier->type != TokenIdentifier && tokenIdentifier->type != TokenStringIdentifier) return ErrorExpectedVariableIdentifier; ++interpreter->pc; enum ValueType varType = itp_getIdentifierTokenValueType(tokenIdentifier); int symbolIndex = tokenIdentifier->symbolIndex; if (interpreter->pc->type == TokenBracketOpen) { // array ++interpreter->pc; if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax; ++interpreter->pc; if (interpreter->pass == PassRun) { struct ArrayVariable *globalVariable = var_getArrayVariable(interpreter, symbolIndex, 0); if (!globalVariable) return ErrorArrayNotDimensionized; enum ErrorCode errorCode = ErrorNone; var_createArrayVariable(interpreter, &errorCode, symbolIndex, interpreter->subLevel, globalVariable); if (errorCode != ErrorNone) return errorCode; } } else { // simple variable if (interpreter->pass == PassRun) { struct SimpleVariable *globalVariable = var_getSimpleVariable(interpreter, symbolIndex, 0); if (!globalVariable) return ErrorVariableNotInitialized; enum ErrorCode errorCode = ErrorNone; var_createSimpleVariable(interpreter, &errorCode, symbolIndex, interpreter->subLevel, varType, &globalVariable->v); if (errorCode != ErrorNone) return errorCode; } } } while (interpreter->pc->type == TokenComma); return itp_endOfCommand(interpreter); } */ enum ErrorCode cmd_GLOBAL(struct Core *core) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pass == PassPrepare && interpreter->subLevel > 0) return ErrorGlobalInsideOfASubprogram; do { // GLOBAL or comma ++interpreter->pc; // identifier struct Token *tokenIdentifier = interpreter->pc; if (tokenIdentifier->type != TokenIdentifier && tokenIdentifier->type != TokenStringIdentifier) return ErrorSyntax; ++interpreter->pc; int symbolIndex = tokenIdentifier->symbolIndex; if (interpreter->pass == PassRun) { struct SimpleVariable *variable = var_getSimpleVariable(interpreter, symbolIndex, 0); if (variable) { variable->subLevel = SUB_LEVEL_GLOBAL; } else { enum ValueType varType = itp_getIdentifierTokenValueType(tokenIdentifier); enum ErrorCode errorCode = ErrorNone; variable = var_createSimpleVariable(interpreter, &errorCode, symbolIndex, SUB_LEVEL_GLOBAL, varType, NULL); if (!variable) return errorCode; } } } while (interpreter->pc->type == TokenComma); return itp_endOfCommand(interpreter); } enum ErrorCode cmd_EXIT_SUB(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // EXIT ++interpreter->pc; // SUB if (interpreter->pc->type != TokenSUB) return ErrorSyntax; ++interpreter->pc; if (interpreter->pass == PassPrepare) { if (interpreter->subLevel == 0) return ErrorExitSubOutsideOfASubprogram; return itp_endOfCommand(interpreter); } else if (interpreter->pass == PassRun) { struct LabelStackItem *itemCALL = lab_popLabelStackItem(interpreter); if (!itemCALL) return ErrorExitSubOutsideOfASubprogram; // clean local variables var_freeSimpleVariables(interpreter, interpreter->subLevel); var_freeArrayVariables(interpreter, interpreter->subLevel); if (itemCALL->type == LabelTypeONCALL) { // exit from interrupt interpreter->exitEvaluation = true; } else if (itemCALL->type == LabelTypeCALL) { // jump back interpreter->pc = itemCALL->token; // after CALL } else { return ErrorExitSubOutsideOfASubprogram; } interpreter->subLevel--; } return ErrorNone; } ================================================ FILE: core/interpreter/cmd_subs.h ================================================ // // Copyright 2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef cmd_subs_h #define cmd_subs_h #include #include "error.h" struct Core; enum ErrorCode cmd_CALL(struct Core *core); enum ErrorCode cmd_SUB(struct Core *core); enum ErrorCode cmd_END_SUB(struct Core *core); //enum ErrorCode cmd_SHARED(struct Core *core); enum ErrorCode cmd_GLOBAL(struct Core *core); enum ErrorCode cmd_EXIT_SUB(struct Core *core); #endif /* cmd_subs_h */ ================================================ FILE: core/interpreter/cmd_text.c ================================================ // // Copyright 2017-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "cmd_text.h" #include #include #include #include #include #include "core.h" #include "text_lib.h" enum ErrorCode cmd_PRINT(struct Core *core) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt; struct TextLib *lib = &interpreter->textLib; bool newLine = true; // PRINT ++interpreter->pc; while (!itp_isEndOfCommand(interpreter)) { struct TypedValue value = itp_evaluateExpression(core, TypeClassAny); if (value.type == ValueTypeError) return value.v.errorCode; if (interpreter->pass == PassRun) { if (value.type == ValueTypeString) { txtlib_printText(lib, value.v.stringValue->chars); } else if (value.type == ValueTypeFloat) { char buffer[20]; snprintf(buffer, 20, "%0.7g", value.v.floatValue); txtlib_printText(lib, buffer); } } if (interpreter->pc->type == TokenComma) { if (interpreter->pass == PassRun) { txtlib_printText(lib, " "); } ++interpreter->pc; newLine = false; } else if (interpreter->pc->type == TokenSemicolon) { ++interpreter->pc; newLine = false; } else if (itp_isEndOfCommand(interpreter)) { newLine = true; } else { return ErrorSyntax; } } if (interpreter->pass == PassRun && newLine) { txtlib_printText(lib, "\n"); } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_INPUT(struct Core *core) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt; struct TextLib *lib = &interpreter->textLib; // INPUT ++interpreter->pc; if (interpreter->pc->type == TokenString) { // prompt if (interpreter->pass == PassRun) { txtlib_printText(lib, interpreter->pc->stringValue->chars); } ++interpreter->pc; // semicolon if (interpreter->pc->type != TokenSemicolon) return ErrorSyntax; ++interpreter->pc; } if (interpreter->pass == PassRun) { txtlib_inputBegin(lib); interpreter->state = StateInput; } else { return cmd_endINPUT(core); } return ErrorNone; } enum ErrorCode cmd_endINPUT(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // identifier enum ErrorCode errorCode = ErrorNone; enum ValueType valueType = ValueTypeNull; union Value *varValue = itp_readVariable(core, &valueType, &errorCode, true); if (!varValue) return errorCode; if (interpreter->pass == PassRun) { if (valueType == ValueTypeString) { struct RCString *rcstring = rcstring_new(interpreter->textLib.inputBuffer, interpreter->textLib.inputLength); if (!rcstring) return ErrorOutOfMemory; if (varValue->stringValue) { rcstring_release(varValue->stringValue); } varValue->stringValue = rcstring; } else if (valueType == ValueTypeFloat) { varValue->floatValue = atof(interpreter->textLib.inputBuffer); } } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_TEXT(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // TEXT ++interpreter->pc; // x value struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric); if (xValue.type == ValueTypeError) return xValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // y value struct TypedValue yValue = itp_evaluateExpression(core, TypeClassNumeric); if (yValue.type == ValueTypeError) return yValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // string value struct TypedValue stringValue = itp_evaluateExpression(core, TypeClassString); if (stringValue.type == ValueTypeError) return stringValue.v.errorCode; if (interpreter->pass == PassRun) { struct TextLib *lib = &interpreter->textLib; txtlib_writeText(lib, stringValue.v.stringValue->chars, floorf(xValue.v.floatValue), floorf(yValue.v.floatValue)); } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_NUMBER(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // NUMBER ++interpreter->pc; // x value struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric); if (xValue.type == ValueTypeError) return xValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // y value struct TypedValue yValue = itp_evaluateExpression(core, TypeClassNumeric); if (yValue.type == ValueTypeError) return yValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // number value struct TypedValue numberValue = itp_evaluateExpression(core, TypeClassNumeric); if (numberValue.type == ValueTypeError) return numberValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // digits value struct TypedValue digitsValue = itp_evaluateExpression(core, TypeClassNumeric); if (digitsValue.type == ValueTypeError) return digitsValue.v.errorCode; if (interpreter->pass == PassRun) { int digits = digitsValue.v.floatValue; struct TextLib *lib = &interpreter->textLib; txtlib_writeNumber(lib, numberValue.v.floatValue, digits, floorf(xValue.v.floatValue), floorf(yValue.v.floatValue)); } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_CLS(struct Core *core) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt; struct TextLib *lib = &interpreter->textLib; // CLS ++interpreter->pc; if (itp_isEndOfCommand(interpreter)) { if (interpreter->pass == PassRun) { // clear all txtlib_clearScreen(lib); } } else { // bg value struct TypedValue bgValue = itp_evaluateNumericExpression(core, 0, 1); if (bgValue.type == ValueTypeError) return bgValue.v.errorCode; if (interpreter->pass == PassRun) { // clear bg txtlib_clearBackground(lib, bgValue.v.floatValue); } } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_WINDOW(struct Core *core) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt; // WINDOW ++interpreter->pc; // x value struct TypedValue xValue = itp_evaluateNumericExpression(core, 0, PLANE_COLUMNS - 1); if (xValue.type == ValueTypeError) return xValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // y value struct TypedValue yValue = itp_evaluateNumericExpression(core, 0, PLANE_ROWS - 1); if (yValue.type == ValueTypeError) return yValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // w value struct TypedValue wValue = itp_evaluateNumericExpression(core, 1, PLANE_COLUMNS); if (wValue.type == ValueTypeError) return wValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // h value struct TypedValue hValue = itp_evaluateNumericExpression(core, 1, PLANE_ROWS); if (hValue.type == ValueTypeError) return hValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // bg value struct TypedValue bgValue = itp_evaluateNumericExpression(core, 0, 1); if (bgValue.type == ValueTypeError) return bgValue.v.errorCode; if (interpreter->pass == PassRun) { core->interpreter->textLib.windowX = xValue.v.floatValue; core->interpreter->textLib.windowY = yValue.v.floatValue; core->interpreter->textLib.windowWidth = wValue.v.floatValue; core->interpreter->textLib.windowHeight = hValue.v.floatValue; core->interpreter->textLib.bg = bgValue.v.floatValue; core->interpreter->textLib.cursorX = 0; core->interpreter->textLib.cursorY = 0; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_FONT(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // FONT ++interpreter->pc; // char value struct TypedValue cValue = itp_evaluateNumericExpression(core, 0, NUM_CHARACTERS - 1); if (cValue.type == ValueTypeError) return cValue.v.errorCode; if (interpreter->pass == PassRun) { interpreter->textLib.fontCharOffset = cValue.v.floatValue; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_LOCATE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt; // LOCATE ++interpreter->pc; // x value struct TypedValue xValue = itp_evaluateNumericExpression(core, 0, core->interpreter->textLib.windowWidth - 1); if (xValue.type == ValueTypeError) return xValue.v.errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // y value struct TypedValue yValue = itp_evaluateNumericExpression(core, 0, core->interpreter->textLib.windowHeight - 1); if (yValue.type == ValueTypeError) return yValue.v.errorCode; if (interpreter->pass == PassRun) { core->interpreter->textLib.cursorX = xValue.v.floatValue; core->interpreter->textLib.cursorY = yValue.v.floatValue; } return itp_endOfCommand(interpreter); } struct TypedValue fnc_CURSOR(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // CURSOR.? enum TokenType type = interpreter->pc->type; ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { switch (type) { case TokenCURSORX: value.v.floatValue = interpreter->textLib.cursorX; break; case TokenCURSORY: value.v.floatValue = interpreter->textLib.cursorY; break; default: assert(0); break; } } return value; } enum ErrorCode cmd_CLW(struct Core *core) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt; // CLW ++interpreter->pc; if (interpreter->pass == PassRun) { txtlib_clearWindow(&interpreter->textLib); } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_TRACE(struct Core *core) { struct Interpreter *interpreter = core->interpreter; struct TextLib *lib = &core->overlay->textLib; bool debug = interpreter->debug; do { // TRACE or comma bool separate = (interpreter->pc->type == TokenComma); ++interpreter->pc; struct TypedValue value = itp_evaluateExpression(core, TypeClassAny); if (value.type == ValueTypeError) return value.v.errorCode; if (interpreter->pass == PassRun) { if (separate && debug) { txtlib_printText(lib, " "); } if (value.type == ValueTypeString) { if (debug) { txtlib_printText(lib, value.v.stringValue->chars); } rcstring_release(value.v.stringValue); } else if (value.type == ValueTypeFloat) { if (debug) { char buffer[20]; snprintf(buffer, 20, "%0.7g", value.v.floatValue); txtlib_printText(lib, buffer); } } } } while (interpreter->pc->type == TokenComma); if (interpreter->pass == PassRun && debug) { txtlib_printText(lib, "\n"); } return itp_endOfCommand(interpreter); } ================================================ FILE: core/interpreter/cmd_text.h ================================================ // // Copyright 2017-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef cmd_text_h #define cmd_text_h #include #include #include "error.h" struct Core; enum ErrorCode cmd_PRINT(struct Core *core); enum ErrorCode cmd_INPUT(struct Core *core); enum ErrorCode cmd_endINPUT(struct Core *core); enum ErrorCode cmd_TEXT(struct Core *core); enum ErrorCode cmd_NUMBER(struct Core *core); enum ErrorCode cmd_CLS(struct Core *core); enum ErrorCode cmd_WINDOW(struct Core *core); enum ErrorCode cmd_FONT(struct Core *core); enum ErrorCode cmd_LOCATE(struct Core *core); struct TypedValue fnc_CURSOR(struct Core *core); enum ErrorCode cmd_CLW(struct Core *core); enum ErrorCode cmd_TRACE(struct Core *core); #endif /* cmd_text_h */ ================================================ FILE: core/interpreter/cmd_variables.c ================================================ // // Copyright 2017-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "cmd_variables.h" #include "core.h" enum ErrorCode cmd_LET(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // LET keyword is optional if (interpreter->pc->type == TokenLET) { ++interpreter->pc; if (interpreter->pc->type != TokenIdentifier && interpreter->pc->type != TokenStringIdentifier) return ErrorSyntax; } // identifier enum ErrorCode errorCode = ErrorNone; enum ValueType valueType = ValueTypeNull; union Value *varValue = itp_readVariable(core, &valueType, &errorCode, true); if (!varValue) return errorCode; if (interpreter->pc->type != TokenEq) return ErrorSyntax; ++interpreter->pc; // value struct TypedValue value = itp_evaluateExpression(core, TypeClassAny); if (value.type == ValueTypeError) return value.v.errorCode; if (value.type != valueType) return ErrorTypeMismatch; if (interpreter->pass == PassRun) { if (valueType == ValueTypeString && varValue->stringValue) { rcstring_release(varValue->stringValue); } *varValue = value.v; } return itp_endOfCommand(interpreter); } enum ErrorCode cmd_DIM(struct Core *core) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt; bool isGlobal = false; struct Token *nextToken = interpreter->pc + 1; if (nextToken->type == TokenGLOBAL) { ++interpreter->pc; if (interpreter->pass == PassPrepare && interpreter->subLevel > 0) return ErrorGlobalInsideOfASubprogram; isGlobal = true; } do { // DIM, GLOBAL or comma ++interpreter->pc; // identifier struct Token *tokenIdentifier = interpreter->pc; ++interpreter->pc; if (tokenIdentifier->type != TokenIdentifier && tokenIdentifier->type != TokenStringIdentifier) { return ErrorSyntax; } int numDimensions = 0; int dimensionSizes[MAX_ARRAY_DIMENSIONS]; if (interpreter->pc->type != TokenBracketOpen) return ErrorSyntax; ++interpreter->pc; for (int i = 0; i < MAX_ARRAY_DIMENSIONS; i++) { struct TypedValue value = itp_evaluateExpression(core, TypeClassNumeric); if (value.type == ValueTypeError) return value.v.errorCode; dimensionSizes[i] = value.v.floatValue + 1; // value is max index, so size is +1 numDimensions++; if (interpreter->pc->type == TokenComma) { ++interpreter->pc; } else { break; } } if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax; ++interpreter->pc; if (interpreter->pass == PassRun) { enum ErrorCode errorCode = ErrorNone; struct ArrayVariable *variable = var_dimVariable(interpreter, &errorCode, tokenIdentifier->symbolIndex, numDimensions, dimensionSizes); if (!variable) return errorCode; variable->type = (tokenIdentifier->type == TokenStringIdentifier) ? ValueTypeString : ValueTypeFloat; if (isGlobal) { variable->subLevel = SUB_LEVEL_GLOBAL; } interpreter->cycles += variable->numValues; } } while (interpreter->pc->type == TokenComma); return itp_endOfCommand(interpreter); } struct TypedValue fnc_UBOUND(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // UBOUND ++interpreter->pc; // bracket open if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax); ++interpreter->pc; // array if (interpreter->pc->type != TokenIdentifier && interpreter->pc->type != TokenStringIdentifier) return val_makeError(ErrorSyntax); int symbolIndex = interpreter->pc->symbolIndex; ++interpreter->pc; int d = 0; if (interpreter->pc->type == TokenComma) { // comma ++interpreter->pc; // dimension value struct TypedValue dValue = itp_evaluateNumericExpression(core, 1, MAX_ARRAY_DIMENSIONS); if (dValue.type == ValueTypeError) return val_makeError(dValue.v.errorCode); d = dValue.v.floatValue - 1; } // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); ++interpreter->pc; struct TypedValue value; value.type = ValueTypeFloat; if (interpreter->pass == PassRun) { struct ArrayVariable *variable = var_getArrayVariable(interpreter, symbolIndex, interpreter->subLevel); if (!variable) return val_makeError(ErrorArrayNotDimensionized); value.v.floatValue = variable->dimensionSizes[d] - 1; } return value; } enum ErrorCode cmd_SWAP(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // SWAP ++interpreter->pc; enum ErrorCode errorCode = ErrorNone; // x identifier enum ValueType xValueType = ValueTypeNull; union Value *xVarValue = itp_readVariable(core, &xValueType, &errorCode, false); if (!xVarValue) return errorCode; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; // y identifier enum ValueType yValueType = ValueTypeNull; union Value *yVarValue = itp_readVariable(core, &yValueType, &errorCode, false); if (!yVarValue) return errorCode; if (xValueType != yValueType) return ErrorTypeMismatch; if (interpreter->pass == PassRun) { union Value spareValue = *xVarValue; *xVarValue = *yVarValue; *yVarValue = spareValue; } return itp_endOfCommand(interpreter); } ================================================ FILE: core/interpreter/cmd_variables.h ================================================ // // Copyright 2017-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef cmd_variables_h #define cmd_variables_h #include #include "error.h" struct Core; enum ErrorCode cmd_LET(struct Core *core); enum ErrorCode cmd_DIM(struct Core *core); struct TypedValue fnc_UBOUND(struct Core *core); enum ErrorCode cmd_SWAP(struct Core *core); #endif /* cmd_variables_h */ ================================================ FILE: core/interpreter/data.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "data.h" #include "interpreter.h" void dat_nextData(struct Interpreter *interpreter) { interpreter->currentDataValueToken++; if (interpreter->currentDataValueToken->type == TokenComma) { // value follows interpreter->currentDataValueToken++; } else { // next DATA line interpreter->currentDataToken = interpreter->currentDataToken->jumpToken; if (interpreter->currentDataToken) { interpreter->currentDataValueToken = interpreter->currentDataToken + 1; // after DATA } else { interpreter->currentDataValueToken = NULL; } } } void dat_restoreData(struct Interpreter *interpreter, struct Token *jumpToken) { if (jumpToken) { struct Token *dataToken = interpreter->firstData; while (dataToken && dataToken < jumpToken) { dataToken = dataToken->jumpToken; } interpreter->currentDataToken = dataToken; } else { interpreter->currentDataToken = interpreter->firstData; } if (interpreter->currentDataToken) { interpreter->currentDataValueToken = interpreter->currentDataToken + 1; // after DATA } else { interpreter->currentDataValueToken = NULL; } } ================================================ FILE: core/interpreter/data.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef data_h #define data_h #include struct Interpreter; struct Token; void dat_nextData(struct Interpreter *interpreter); void dat_restoreData(struct Interpreter *interpreter, struct Token *jumpToken); #endif /* data_h */ ================================================ FILE: core/interpreter/error.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "error.h" const char *ErrorStrings[] = { "OK", "Could Not Open Program", "Too Many Tokens", "ROM Is Full", "Index Already Defined", "Unterminated String", "Unexpected Character", "Reserved Keyword", "Syntax Error", "Symbol Name Too Long", "Too Many Symbols", "Type Mismatch", "Out Of Memory", "ELSE Without IF", "END IF Without IF", "Expected Command", "NEXT Without FOR", "LOOP Without DO", "UNTIL Without REPEAT", "WEND Without WHILE", "Label Already Defined", "Too Many Labels", "ErrorExpectedLabel", "Undefined Label", "Array Not Dimensionized", "Array Already Dimensionized", "Variable Already Used", "Index Out Of Bounds", "Wrong Number Of Dimensions", "Invalid Parameter", "RETURN Without GOSUB", "Stack Overflow", "Out Of Data", "Illegal Memory Access", "Too Many CPU Cycles In Interrupt", "Not Allowed In Interrupt", "IF Without END IF", "FOR Without NEXT", "DO Without LOOP", "REPEAT Without UNTIL", "WHILE Without WEND", "EXIT Not Inside Loop", "Directory Not Loaded", "Division By Zero", "Variable Not Initialized", "Array Variable Without Index", "END SUB Without SUB", "SUB Without END SUB", "SUB Cannot Be Nested", "Undefined Subprogram", "Expected Subprogram Name", "Argument Count Mismatch", "SUB Already Defined", "Too Many Subprograms", "SHARED Outside Of A Subprogram", "GLOBAL Inside Of A Subprogram", "EXIT SUB Outside Of A Subprogram", "Keyboard Not Enabled", "Automatic Pause Not Disabled", "Gamepad Not Enabled", "Touch Not Enabled", "Input Change Not Allowed", }; const char *err_getString(enum ErrorCode errorCode) { return ErrorStrings[errorCode]; } struct CoreError err_makeCoreError(enum ErrorCode code, int sourcePosition) { struct CoreError error = {code, sourcePosition}; return error; } struct CoreError err_noCoreError(void) { struct CoreError error = {ErrorNone, 0}; return error; } ================================================ FILE: core/interpreter/error.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef error_h #define error_h #include enum ErrorCode { ErrorNone, ErrorCouldNotOpenProgram, ErrorTooManyTokens, ErrorRomIsFull, ErrorIndexAlreadyDefined, ErrorUnterminatedString, ErrorUnexpectedCharacter, ErrorReservedKeyword, ErrorSyntax, ErrorSymbolNameTooLong, ErrorTooManySymbols, ErrorTypeMismatch, ErrorOutOfMemory, ErrorElseWithoutIf, ErrorEndIfWithoutIf, ErrorExpectedCommand, ErrorNextWithoutFor, ErrorLoopWithoutDo, ErrorUntilWithoutRepeat, ErrorWendWithoutWhile, ErrorLabelAlreadyDefined, ErrorTooManyLabels, ErrorExpectedLabel, ErrorUndefinedLabel, ErrorArrayNotDimensionized, ErrorArrayAlreadyDimensionized, ErrorVariableAlreadyUsed, ErrorIndexOutOfBounds, ErrorWrongNumberOfDimensions, ErrorInvalidParameter, ErrorReturnWithoutGosub, ErrorStackOverflow, ErrorOutOfData, ErrorIllegalMemoryAccess, ErrorTooManyCPUCyclesInInterrupt, ErrorNotAllowedInInterrupt, ErrorIfWithoutEndIf, ErrorForWithoutNext, ErrorDoWithoutLoop, ErrorRepeatWithoutUntil, ErrorWhileWithoutWend, ErrorExitNotInsideLoop, ErrorDirectoryNotLoaded, ErrorDivisionByZero, ErrorVariableNotInitialized, ErrorArrayVariableWithoutIndex, ErrorEndSubWithoutSub, ErrorSubWithoutEndSub, ErrorSubCannotBeNested, ErrorUndefinedSubprogram, ErrorExpectedSubprogramName, ErrorArgumentCountMismatch, ErrorSubAlreadyDefined, ErrorTooManySubprograms, ErrorSharedOutsideOfASubprogram, ErrorGlobalInsideOfASubprogram, ErrorExitSubOutsideOfASubprogram, ErrorKeyboardNotEnabled, ErrorAutomaticPauseNotDisabled, ErrorGamepadNotEnabled, ErrorTouchNotEnabled, ErrorInputChangeNotAllowed, }; struct CoreError { enum ErrorCode code; int sourcePosition; }; const char *err_getString(enum ErrorCode errorCode); struct CoreError err_makeCoreError(enum ErrorCode code, int sourcePosition); struct CoreError err_noCoreError(void); #endif /* error_h */ ================================================ FILE: core/interpreter/interpreter.c ================================================ // // Copyright 2016-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "interpreter.h" #include #include #include #include #include #include "core.h" #include "default_characters.h" #include "cmd_audio.h" #include "cmd_control.h" #include "cmd_variables.h" #include "cmd_data.h" #include "cmd_strings.h" #include "cmd_memory.h" #include "cmd_text.h" #include "cmd_maths.h" #include "cmd_background.h" #include "cmd_screen.h" #include "cmd_sprites.h" #include "cmd_io.h" #include "cmd_files.h" #include "cmd_subs.h" #include "string_utils.h" struct TypedValue itp_evaluateExpressionLevel(struct Core *core, int level); struct TypedValue itp_evaluatePrimaryExpression(struct Core *core); struct TypedValue itp_evaluateFunction(struct Core *core); enum ErrorCode itp_evaluateCommand(struct Core *core); void itp_init(struct Core *core) { struct Interpreter *interpreter = core->interpreter; interpreter->romDataManager.data = core->machine->cartridgeRom; // global null string interpreter->nullString = rcstring_new(NULL, 0); if (!interpreter->nullString) exit(EXIT_FAILURE); } void itp_deinit(struct Core *core) { struct Interpreter *interpreter = core->interpreter; itp_freeProgram(core); // Free null string if (interpreter->nullString) { rcstring_release(interpreter->nullString); interpreter->nullString = NULL; } } struct CoreError itp_compileProgram(struct Core *core, const char *sourceCode) { struct Interpreter *interpreter = core->interpreter; itp_freeProgram(core); // Parse source code interpreter->sourceCode = uppercaseString(sourceCode); if (!interpreter->sourceCode) return err_makeCoreError(ErrorOutOfMemory, -1); struct CoreError error = tok_tokenizeUppercaseProgram(&interpreter->tokenizer, interpreter->sourceCode); if (error.code != ErrorNone) { return error; } struct DataManager *romDataManager = &interpreter->romDataManager; error = data_uppercaseImport(romDataManager, interpreter->sourceCode, false); if (error.code != ErrorNone) return error; // add default characters if ROM entry 0 is unused struct DataEntry *entry0 = &romDataManager->entries[0]; if (entry0->length == 0 && (DATA_SIZE - data_currentSize(romDataManager)) >= 1024) { data_setEntry(romDataManager, 0, "FONT", (uint8_t *)DefaultCharacters, 1024); } // Prepare commands interpreter->pc = interpreter->tokenizer.tokens; interpreter->pass = PassPrepare; interpreter->exitEvaluation = false; interpreter->subLevel = 0; interpreter->numLabelStackItems = 0; interpreter->isSingleLineIf = false; enum ErrorCode errorCode; do { errorCode = itp_evaluateCommand(core); } while (errorCode == ErrorNone && interpreter->pc->type != TokenUndefined); if (errorCode != ErrorNone) return err_makeCoreError(errorCode, interpreter->pc->sourcePosition); if (interpreter->numLabelStackItems > 0) { struct LabelStackItem *item = &interpreter->labelStackItems[interpreter->numLabelStackItems - 1]; errorCode = itp_labelStackError(item); if (errorCode != ErrorNone) { return err_makeCoreError(errorCode, item->token->sourcePosition); } } // prepare for run interpreter->pc = interpreter->tokenizer.tokens; interpreter->cycles = 0; interpreter->interruptOverCycles = 0; interpreter->pass = PassRun; interpreter->state = StateEvaluate; interpreter->mode = ModeNone; interpreter->handlesPause = true; interpreter->currentDataToken = interpreter->firstData; interpreter->currentDataValueToken = interpreter->firstData ? interpreter->firstData + 1 : NULL; interpreter->isSingleLineIf = false; interpreter->lastFrameIOStatus.value = 0; interpreter->seed = 0; interpreter->isKeyboardOptional = false; memset(&interpreter->textLib, 0, sizeof(struct TextLib)); memset(&interpreter->spritesLib, 0, sizeof(struct SpritesLib)); memset(&interpreter->audioLib, 0, sizeof(struct AudioLib)); interpreter->textLib.core = core; interpreter->spritesLib.core = core; interpreter->audioLib.core = core; return err_noCoreError(); } void itp_runProgram(struct Core *core) { struct Interpreter *interpreter = core->interpreter; switch (interpreter->state) { case StateEvaluate: { if (interpreter->waitCount > 0) { --interpreter->waitCount; break; } interpreter->mode = ModeMain; interpreter->exitEvaluation = false; enum ErrorCode errorCode = ErrorNone; while ( errorCode == ErrorNone && interpreter->cycles < MAX_CYCLES_TOTAL_PER_FRAME && interpreter->state == StateEvaluate && !interpreter->exitEvaluation) { errorCode = itp_evaluateCommand(core); } if (interpreter->cycles >= MAX_CYCLES_TOTAL_PER_FRAME) { machine_suspendEnergySaving(core, 2); } interpreter->mode = ModeNone; if (errorCode != ErrorNone) { itp_endProgram(core); delegate_interpreterDidFail(core, err_makeCoreError(errorCode, interpreter->pc->sourcePosition)); } break; } case StateInput: { if (txtlib_inputUpdate(&interpreter->textLib)) { interpreter->state = StateEvaluate; cmd_endINPUT(core); } break; } case StateNoProgram: case StatePaused: case StateEnd: case StateWaitForDisk: break; } } void itp_runInterrupt(struct Core *core, enum InterruptType type) { struct Interpreter *interpreter = core->interpreter; switch (interpreter->state) { case StateEvaluate: case StateInput: case StatePaused: case StateWaitForDisk: { struct Token *startToken = NULL; int maxCycles; int mainCycles = interpreter->cycles; interpreter->cycles = 0; switch (type) { case InterruptTypeRaster: startToken = interpreter->currentOnRasterToken; maxCycles = MAX_CYCLES_PER_RASTER; break; case InterruptTypeVBL: startToken = interpreter->currentOnVBLToken; maxCycles = MAX_CYCLES_PER_VBL; // update audio player audlib_update(&interpreter->audioLib); break; } if (startToken) { interpreter->mode = ModeInterrupt; interpreter->exitEvaluation = false; struct Token *pc = interpreter->pc; interpreter->pc = startToken; interpreter->subLevel++; enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeONCALL, NULL); while ( errorCode == ErrorNone // cycles can exceed interrupt limit (see interruptOverCycles), but there is still a hard limit for extreme cases && interpreter->cycles < MAX_CYCLES_TOTAL_PER_FRAME && !interpreter->exitEvaluation) { errorCode = itp_evaluateCommand(core); } interpreter->mode = ModeNone; if (interpreter->cycles >= MAX_CYCLES_TOTAL_PER_FRAME) { itp_endProgram(core); delegate_interpreterDidFail(core, err_makeCoreError(ErrorTooManyCPUCyclesInInterrupt, interpreter->pc->sourcePosition)); } else if (errorCode != ErrorNone) { itp_endProgram(core); delegate_interpreterDidFail(core, err_makeCoreError(errorCode, interpreter->pc->sourcePosition)); } else { interpreter->pc = pc; } } // calculate cycles exceeding limit interpreter->interruptOverCycles += interpreter->cycles - maxCycles; if (interpreter->interruptOverCycles < 0) { interpreter->interruptOverCycles = 0; } // sum of interrupt's and main cycle count interpreter->cycles += mainCycles; break; } case StateNoProgram: case StateEnd: break; } } void itp_didFinishVBL(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // remember this frame's IO for (int i = 0; i < NUM_GAMEPADS; i++) { interpreter->lastFrameGamepads[i] = core->machine->ioRegisters.gamepads[i]; } interpreter->lastFrameIOStatus = core->machine->ioRegisters.status; // timer interpreter->timer++; if (interpreter->timer >= TIMER_WRAP_VALUE) { interpreter->timer = 0; } // pause if (core->machine->ioRegisters.status.pause) { if (interpreter->handlesPause && interpreter->state == StateEvaluate) { interpreter->state = StatePaused; overlay_updateState(core); core->machine->ioRegisters.status.pause = 0; } else if (interpreter->state == StatePaused) { interpreter->state = StateEvaluate; overlay_updateState(core); core->machine->ioRegisters.status.pause = 0; } } // CPU load (rounded up) int currentCpuLoad = (interpreter->cycles * 100 + MAX_CYCLES_TOTAL_PER_FRAME - 1) / MAX_CYCLES_TOTAL_PER_FRAME; if (currentCpuLoad > interpreter->cpuLoadMax) { interpreter->cpuLoadMax = currentCpuLoad; } ++interpreter->cpuLoadTimer; if (interpreter->cpuLoadTimer >= 30) { interpreter->cpuLoadTimer = 0; interpreter->cpuLoadDisplay = interpreter->cpuLoadMax; interpreter->cpuLoadMax = currentCpuLoad; } // reset CPU cycles interpreter->cycles = interpreter->cycles - MAX_CYCLES_TOTAL_PER_FRAME; if (interpreter->cycles < 0) { interpreter->cycles = 0; } } void itp_endProgram(struct Core *core) { struct Interpreter *interpreter = core->interpreter; interpreter->state = StateEnd; interpreter->interruptOverCycles = 0; } void itp_freeProgram(struct Core *core) { struct Interpreter *interpreter = core->interpreter; interpreter->state = StateNoProgram; interpreter->firstData = NULL; interpreter->lastData = NULL; interpreter->currentDataToken = NULL; interpreter->currentDataValueToken = NULL; interpreter->currentOnRasterToken = NULL; interpreter->currentOnVBLToken = NULL; interpreter->lastVariableValue = NULL; var_freeSimpleVariables(interpreter, SUB_LEVEL_GLOBAL); var_freeArrayVariables(interpreter, SUB_LEVEL_GLOBAL); tok_freeTokens(&interpreter->tokenizer); if (interpreter->sourceCode) { free((void *)interpreter->sourceCode); interpreter->sourceCode = NULL; } } enum ValueType itp_getIdentifierTokenValueType(struct Token *token) { if (token->type == TokenIdentifier) { return ValueTypeFloat; } else if (token->type == TokenStringIdentifier) { return ValueTypeString; } return ValueTypeNull; } union Value *itp_readVariable(struct Core *core, enum ValueType *type, enum ErrorCode *errorCode, bool forWriting) { struct Interpreter *interpreter = core->interpreter; struct Token *tokenIdentifier = interpreter->pc; if (tokenIdentifier->type != TokenIdentifier && tokenIdentifier->type != TokenStringIdentifier) { *errorCode = ErrorSyntax; return NULL; } enum ValueType varType = itp_getIdentifierTokenValueType(tokenIdentifier); if (type) { *type = varType; } int symbolIndex = tokenIdentifier->symbolIndex; ++interpreter->pc; ++interpreter->cycles; if (interpreter->pc->type == TokenBracketOpen) { // array ++interpreter->pc; struct ArrayVariable *variable = NULL; if (interpreter->pass == PassRun) { variable = var_getArrayVariable(interpreter, symbolIndex, interpreter->subLevel); if (!variable) { *errorCode = ErrorArrayNotDimensionized; return NULL; } } int indices[MAX_ARRAY_DIMENSIONS]; int numDimensions = 0; for (int i = 0; i < MAX_ARRAY_DIMENSIONS; i++) { struct TypedValue indexValue = itp_evaluateExpression(core, TypeClassNumeric); if (indexValue.type == ValueTypeError) { *errorCode = indexValue.v.errorCode; return NULL; } numDimensions++; if (interpreter->pass == PassRun) { if (numDimensions <= variable->numDimensions && (indexValue.v.floatValue < 0 || indexValue.v.floatValue >= variable->dimensionSizes[i])) { *errorCode = ErrorIndexOutOfBounds; return NULL; } indices[i] = indexValue.v.floatValue; } if (interpreter->pc->type == TokenComma) { ++interpreter->pc; } else { break; } } if (interpreter->pc->type != TokenBracketClose) { *errorCode = ErrorSyntax; return NULL; } ++interpreter->pc; if (interpreter->pass == PassRun) { if (numDimensions != variable->numDimensions) { *errorCode = ErrorWrongNumberOfDimensions; return NULL; } return var_getArrayValue(interpreter, variable, indices); } } else { // simple variable if (interpreter->pass == PassRun) { struct SimpleVariable *variable = var_getSimpleVariable(interpreter, symbolIndex, interpreter->subLevel); if (!variable) { // check if variable name is already used for array if (var_getArrayVariable(interpreter, symbolIndex, interpreter->subLevel)) { *errorCode = ErrorArrayVariableWithoutIndex; return NULL; } if (!forWriting) { *errorCode = ErrorVariableNotInitialized; return NULL; } variable = var_createSimpleVariable(interpreter, errorCode, symbolIndex, interpreter->subLevel, varType, NULL); if (!variable) return NULL; } if (variable->isReference) { return variable->v.reference; } return &variable->v; } } return &ValueDummy; } enum ErrorCode itp_checkTypeClass(struct Interpreter *interpreter, enum ValueType valueType, enum TypeClass typeClass) { if (interpreter->pass == PassPrepare && valueType != ValueTypeError) { if (typeClass == TypeClassString && valueType != ValueTypeString) { return ErrorTypeMismatch; } else if (typeClass == TypeClassNumeric && valueType != ValueTypeFloat) { return ErrorTypeMismatch; } } return ErrorNone; } struct TypedValue itp_evaluateExpression(struct Core *core, enum TypeClass typeClass) { struct TypedValue value = itp_evaluateExpressionLevel(core, 0); if (value.type != ValueTypeError) { enum ErrorCode errorCode = itp_checkTypeClass(core->interpreter, value.type, typeClass); if (errorCode != ErrorNone) { value.type = ValueTypeError; value.v.errorCode = errorCode; } } return value; } struct TypedValue itp_evaluateNumericExpression(struct Core *core, int min, int max) { struct TypedValue value = itp_evaluateExpressionLevel(core, 0); if (value.type != ValueTypeError) { enum ErrorCode errorCode = ErrorNone; if (core->interpreter->pass == PassPrepare) { if (value.type != ValueTypeFloat) { errorCode = ErrorTypeMismatch; } } else if (core->interpreter->pass == PassRun) { if ((int)value.v.floatValue < min || (int)value.v.floatValue > max) { errorCode = ErrorInvalidParameter; } } if (errorCode != ErrorNone) { value.type = ValueTypeError; value.v.errorCode = errorCode; } } return value; } struct TypedValue itp_evaluateOptionalExpression(struct Core *core, enum TypeClass typeClass) { if (core->interpreter->pc->type == TokenComma || core->interpreter->pc->type == TokenBracketClose || itp_isEndOfCommand(core->interpreter)) { struct TypedValue value; value.type = ValueTypeNull; return value; } return itp_evaluateExpression(core, typeClass); } struct TypedValue itp_evaluateOptionalNumericExpression(struct Core *core, int min, int max) { if (core->interpreter->pc->type == TokenComma || core->interpreter->pc->type == TokenBracketClose || itp_isEndOfCommand(core->interpreter)) { struct TypedValue value; value.type = ValueTypeNull; return value; } return itp_evaluateNumericExpression(core, min, max); } bool itp_isTokenLevel(enum TokenType token, int level) { switch (level) { case 0: return token == TokenXOR || token == TokenOR; case 1: return token == TokenAND; // case 2: // return token == TokenNOT; case 3: return token == TokenEq || token == TokenUneq || token == TokenGr || token == TokenLe || token == TokenGrEq || token == TokenLeEq; case 4: return token == TokenPlus || token == TokenMinus; case 5: return token == TokenMOD; case 6: return token == TokenMul || token == TokenDiv || token == TokenDivInt; // case 7: // return token == TokenPlus || token == TokenMinus; // unary case 8: return token == TokenPow; } return false; } struct TypedValue itp_evaluateExpressionLevel(struct Core *core, int level) { struct Interpreter *interpreter = core->interpreter; enum TokenType type = interpreter->pc->type; if (level == 2 && type == TokenNOT) { ++interpreter->pc; ++interpreter->cycles; struct TypedValue value = itp_evaluateExpressionLevel(core, level + 1); if (value.type == ValueTypeError) return value; enum ErrorCode errorCode = itp_checkTypeClass(core->interpreter, value.type, TypeClassNumeric); if (errorCode != ErrorNone) { value.type = ValueTypeError; value.v.errorCode = errorCode; } else { value.v.floatValue = ~((int)value.v.floatValue); } interpreter->lastVariableValue = NULL; return value; } if (level == 7 && (type == TokenPlus || type == TokenMinus)) // unary { ++interpreter->pc; ++interpreter->cycles; struct TypedValue value = itp_evaluateExpressionLevel(core, level + 1); if (value.type == ValueTypeError) return value; enum ErrorCode errorCode = itp_checkTypeClass(core->interpreter, value.type, TypeClassNumeric); if (errorCode != ErrorNone) { value.type = ValueTypeError; value.v.errorCode = errorCode; } else if (type == TokenMinus) { value.v.floatValue = -value.v.floatValue; } interpreter->lastVariableValue = NULL; return value; } if (level == 9) { return itp_evaluatePrimaryExpression(core); } struct TypedValue value = itp_evaluateExpressionLevel(core, level + 1); if (value.type == ValueTypeError) return value; while (itp_isTokenLevel(interpreter->pc->type, level)) { enum TokenType type = interpreter->pc->type; ++interpreter->pc; ++interpreter->cycles; struct TypedValue rightValue = itp_evaluateExpressionLevel(core, level + 1); if (rightValue.type == ValueTypeError) return rightValue; struct TypedValue newValue; if (value.type != rightValue.type) { newValue.type = ValueTypeError; newValue.v.errorCode = ErrorTypeMismatch; return newValue; } if (value.type == ValueTypeFloat) { newValue.type = ValueTypeFloat; switch (type) { case TokenXOR: { int leftInt = value.v.floatValue; int rightInt = rightValue.v.floatValue; newValue.v.floatValue = (leftInt ^ rightInt); break; } case TokenOR: { int leftInt = value.v.floatValue; int rightInt = rightValue.v.floatValue; newValue.v.floatValue = (leftInt | rightInt); break; } case TokenAND: { int leftInt = value.v.floatValue; int rightInt = rightValue.v.floatValue; newValue.v.floatValue = (leftInt & rightInt); break; } case TokenEq: { newValue.v.floatValue = (value.v.floatValue == rightValue.v.floatValue) ? BAS_TRUE : BAS_FALSE; break; } case TokenUneq: { newValue.v.floatValue = (value.v.floatValue != rightValue.v.floatValue) ? BAS_TRUE : BAS_FALSE; break; } case TokenGr: { newValue.v.floatValue = (value.v.floatValue > rightValue.v.floatValue) ? BAS_TRUE : BAS_FALSE; break; } case TokenLe: { newValue.v.floatValue = (value.v.floatValue < rightValue.v.floatValue) ? BAS_TRUE : BAS_FALSE; break; } case TokenGrEq: { newValue.v.floatValue = (value.v.floatValue >= rightValue.v.floatValue) ? BAS_TRUE : BAS_FALSE; break; } case TokenLeEq: { newValue.v.floatValue = (value.v.floatValue <= rightValue.v.floatValue) ? BAS_TRUE : BAS_FALSE; break; } case TokenPlus: { newValue.v.floatValue = value.v.floatValue + rightValue.v.floatValue; break; } case TokenMinus: { newValue.v.floatValue = value.v.floatValue - rightValue.v.floatValue; break; } case TokenMOD: { if (interpreter->pass == PassRun) { int rightInt = (int)rightValue.v.floatValue; if (rightInt == 0) { newValue.type = ValueTypeError; newValue.v.errorCode = ErrorDivisionByZero; } else { newValue.v.floatValue = (int)value.v.floatValue % rightInt; } } break; } case TokenMul: { newValue.v.floatValue = value.v.floatValue * rightValue.v.floatValue; break; } case TokenDiv: { if (interpreter->pass == PassRun) { if (rightValue.v.floatValue == 0.0f) { newValue.type = ValueTypeError; newValue.v.errorCode = ErrorDivisionByZero; } else { newValue.v.floatValue = value.v.floatValue / rightValue.v.floatValue; } } break; } case TokenDivInt: { if (interpreter->pass == PassRun) { int rightInt = (int)rightValue.v.floatValue; if (rightInt == 0) { newValue.type = ValueTypeError; newValue.v.errorCode = ErrorDivisionByZero; } else { newValue.v.floatValue = (int)value.v.floatValue / rightInt; } } break; } case TokenPow: { newValue.v.floatValue = powf(value.v.floatValue, rightValue.v.floatValue); break; } default: { newValue.type = ValueTypeError; newValue.v.errorCode = ErrorSyntax; } } } else if (value.type == ValueTypeString) { switch (type) { case TokenEq: { newValue.type = ValueTypeFloat; if (interpreter->pass == PassRun) { newValue.v.floatValue = (strcmp(value.v.stringValue->chars, rightValue.v.stringValue->chars) == 0) ? BAS_TRUE : BAS_FALSE; } break; } case TokenUneq: { newValue.type = ValueTypeFloat; if (interpreter->pass == PassRun) { newValue.v.floatValue = (strcmp(value.v.stringValue->chars, rightValue.v.stringValue->chars) != 0) ? BAS_TRUE : BAS_FALSE; } break; } case TokenGr: { newValue.type = ValueTypeFloat; if (interpreter->pass == PassRun) { newValue.v.floatValue = (strcmp(value.v.stringValue->chars, rightValue.v.stringValue->chars) > 0) ? BAS_TRUE : BAS_FALSE; } break; } case TokenLe: { newValue.type = ValueTypeFloat; if (interpreter->pass == PassRun) { newValue.v.floatValue = (strcmp(value.v.stringValue->chars, rightValue.v.stringValue->chars) < 0) ? BAS_TRUE : BAS_FALSE; } break; } case TokenGrEq: { newValue.type = ValueTypeFloat; if (interpreter->pass == PassRun) { newValue.v.floatValue = (strcmp(value.v.stringValue->chars, rightValue.v.stringValue->chars) >= 0) ? BAS_TRUE : BAS_FALSE; } break; } case TokenLeEq: { newValue.type = ValueTypeFloat; if (interpreter->pass == PassRun) { newValue.v.floatValue = (strcmp(value.v.stringValue->chars, rightValue.v.stringValue->chars) <= 0) ? BAS_TRUE : BAS_FALSE; } break; } case TokenPlus: { newValue.type = ValueTypeString; if (interpreter->pass == PassRun) { size_t len1 = strlen(value.v.stringValue->chars); size_t len2 = strlen(rightValue.v.stringValue->chars); newValue.v.stringValue = rcstring_new(NULL, len1 + len2); strcpy(newValue.v.stringValue->chars, value.v.stringValue->chars); strcpy(&newValue.v.stringValue->chars[len1], rightValue.v.stringValue->chars); interpreter->cycles += len1 + len2; } break; } case TokenXOR: case TokenOR: case TokenAND: case TokenMinus: case TokenMOD: case TokenMul: case TokenDiv: case TokenDivInt: case TokenPow: { newValue.type = ValueTypeError; newValue.v.errorCode = ErrorTypeMismatch; break; } default: { newValue.type = ValueTypeError; newValue.v.errorCode = ErrorSyntax; } } if (interpreter->pass == PassRun) { rcstring_release(value.v.stringValue); rcstring_release(rightValue.v.stringValue); } } else { assert(0); newValue.v.floatValue = 0; } value = newValue; interpreter->lastVariableValue = NULL; if (value.type == ValueTypeError) break; } return value; } struct TypedValue itp_evaluatePrimaryExpression(struct Core *core) { struct Interpreter *interpreter = core->interpreter; // check for function struct TypedValue value = itp_evaluateFunction(core); if (value.type != ValueTypeNull) { ++interpreter->cycles; interpreter->lastVariableValue = NULL; return value; } interpreter->lastVariableValue = NULL; // native types switch (interpreter->pc->type) { case TokenFloat: { value.type = ValueTypeFloat; value.v.floatValue = interpreter->pc->floatValue; ++interpreter->pc; ++interpreter->cycles; break; } case TokenString: { value.type = ValueTypeString; value.v.stringValue = interpreter->pc->stringValue; if (interpreter->pass == PassRun) { rcstring_retain(interpreter->pc->stringValue); } ++interpreter->pc; ++interpreter->cycles; break; } case TokenIdentifier: case TokenStringIdentifier: { enum ErrorCode errorCode = ErrorNone; enum ValueType valueType = ValueTypeNull; union Value *varValue = itp_readVariable(core, &valueType, &errorCode, false); if (varValue) { value.type = valueType; value.v = *varValue; interpreter->lastVariableValue = varValue; if (interpreter->pass == PassRun && valueType == ValueTypeString) { rcstring_retain(varValue->stringValue); } } else { value.type = ValueTypeError; value.v.errorCode = errorCode; } break; } case TokenBracketOpen: { ++interpreter->pc; value = itp_evaluateExpression(core, TypeClassAny); if (value.type == ValueTypeError) return value; if (interpreter->pc->type != TokenBracketClose) { value.type = ValueTypeError; value.v.errorCode = ErrorSyntax; } else { ++interpreter->pc; interpreter->lastVariableValue = NULL; } break; } default: { value.type = ValueTypeError; value.v.errorCode = ErrorSyntax; } } return value; } bool itp_isEndOfCommand(struct Interpreter *interpreter) { enum TokenType type = interpreter->pc->type; return (type == TokenEol || type == TokenELSE); } enum ErrorCode itp_endOfCommand(struct Interpreter *interpreter) { enum TokenType type = interpreter->pc->type; if (type == TokenEol) { interpreter->isSingleLineIf = false; ++interpreter->pc; return ErrorNone; } return (type == TokenELSE) ? ErrorNone : ErrorSyntax; } enum TokenType itp_getNextTokenType(struct Interpreter *interpreter) { return (interpreter->pc + 1)->type; } struct TypedValue itp_evaluateFunction(struct Core *core) { struct Interpreter *interpreter = core->interpreter; switch (interpreter->pc->type) { case TokenASC: return fnc_ASC(core); case TokenBIN: case TokenHEX: return fnc_BIN_HEX(core); case TokenCHR: return fnc_CHR(core); case TokenINSTR: return fnc_INSTR(core); case TokenLEFTStr: case TokenRIGHTStr: return fnc_LEFTStr_RIGHTStr(core); case TokenLEN: return fnc_LEN(core); case TokenMID: return fnc_MID(core); case TokenSTR: return fnc_STR(core); case TokenVAL: return fnc_VAL(core); case TokenPEEK: case TokenPEEKW: case TokenPEEKL: return fnc_PEEK(core); case TokenPI: return fnc_math0(core); case TokenABS: case TokenACOS: case TokenASIN: case TokenATAN: case TokenCOS: case TokenEXP: case TokenHCOS: case TokenHSIN: case TokenHTAN: case TokenINT: case TokenLOG: case TokenSGN: case TokenSIN: case TokenSQR: case TokenTAN: return fnc_math1(core); case TokenMAX: case TokenMIN: return fnc_math2(core); case TokenRND: return fnc_RND(core); case TokenINKEY: return fnc_INKEY(core); case TokenUBOUND: return fnc_UBOUND(core); case TokenROM: case TokenSIZE: return fnc_ROM_SIZE(core); case TokenCOLOR: return fnc_COLOR(core); case TokenTIMER: case TokenRASTER: case TokenDISPLAY: return fnc_screen0(core); case TokenSCROLLX: case TokenSCROLLY: return fnc_SCROLL_X_Y(core); case TokenCELLA: case TokenCELLC: return fnc_CELL(core); case TokenMCELLA: case TokenMCELLC: return fnc_MCELL(core); case TokenCURSORX: case TokenCURSORY: return fnc_CURSOR(core); case TokenUP: case TokenDOWN: case TokenLEFT: case TokenRIGHT: return fnc_UP_DOWN_LEFT_RIGHT(core); case TokenBUTTON: return fnc_BUTTON(core); case TokenSPRITEX: case TokenSPRITEY: case TokenSPRITEC: case TokenSPRITEA: return fnc_SPRITE(core); case TokenSPRITE: return fnc_SPRITE_HIT(core); case TokenHIT: return fnc_HIT(core); case TokenTOUCH: return fnc_TOUCH(core); case TokenTAP: return fnc_TAP(core); case TokenTOUCHX: case TokenTOUCHY: return fnc_TOUCH_X_Y(core); case TokenFILE: return fnc_FILE(core); case TokenFSIZE: return fnc_FSIZE(core); case TokenPAUSE: return fnc_PAUSE(core); case TokenMUSIC: return fnc_MUSIC(core); default: break; } struct TypedValue value; value.type = ValueTypeNull; return value; } enum ErrorCode itp_evaluateCommand(struct Core *core) { struct Interpreter *interpreter = core->interpreter; enum TokenType type = interpreter->pc->type; if (type != TokenREM && type != TokenApostrophe && type != TokenEol && type != TokenUndefined) { ++interpreter->cycles; } switch (type) { case TokenUndefined: if (interpreter->pass == PassRun) { itp_endProgram(core); } break; case TokenREM: case TokenApostrophe: ++interpreter->pc; break; case TokenLabel: ++interpreter->pc; if (interpreter->pc->type != TokenEol) return ErrorSyntax; ++interpreter->pc; break; case TokenEol: interpreter->isSingleLineIf = false; ++interpreter->pc; break; case TokenEND: switch (itp_getNextTokenType(interpreter)) { case TokenIF: return cmd_END_IF(core); case TokenSUB: return cmd_END_SUB(core); default: return cmd_END(core); } break; case TokenLET: case TokenIdentifier: case TokenStringIdentifier: return cmd_LET(core); case TokenDIM: return cmd_DIM(core); case TokenPRINT: return cmd_PRINT(core); case TokenCLS: return cmd_CLS(core); case TokenINPUT: return cmd_INPUT(core); case TokenIF: return cmd_IF(core, false); case TokenELSE: return cmd_ELSE(core); case TokenFOR: return cmd_FOR(core); case TokenNEXT: return cmd_NEXT(core); case TokenGOTO: return cmd_GOTO(core); case TokenGOSUB: return cmd_GOSUB(core); case TokenRETURN: return cmd_RETURN(core); case TokenDATA: return cmd_DATA(core); case TokenREAD: return cmd_READ(core); case TokenRESTORE: return cmd_RESTORE(core); case TokenPOKE: case TokenPOKEW: case TokenPOKEL: return cmd_POKE(core); case TokenFILL: return cmd_FILL(core); case TokenCOPY: return cmd_COPY(core); case TokenROL: case TokenROR: return cmd_ROL_ROR(core); case TokenWAIT: return cmd_WAIT(core); case TokenON: return cmd_ON(core); case TokenSWAP: return cmd_SWAP(core); case TokenTEXT: return cmd_TEXT(core); case TokenNUMBER: return cmd_NUMBER(core); case TokenDO: return cmd_DO(core); case TokenLOOP: return cmd_LOOP(core); case TokenREPEAT: return cmd_REPEAT(core); case TokenUNTIL: return cmd_UNTIL(core); case TokenWHILE: return cmd_WHILE(core); case TokenWEND: return cmd_WEND(core); case TokenSYSTEM: return cmd_SYSTEM(core); case TokenRANDOMIZE: return cmd_RANDOMIZE(core); case TokenADD: return cmd_ADD(core); case TokenINC: case TokenDEC: return cmd_INC_DEC(core); case TokenLEFTStr: case TokenRIGHTStr: return cmd_LEFT_RIGHT(core); case TokenMID: return cmd_MID(core); case TokenWINDOW: return cmd_WINDOW(core); case TokenFONT: return cmd_FONT(core); case TokenLOCATE: return cmd_LOCATE(core); case TokenCLW: return cmd_CLW(core); case TokenBG: switch (itp_getNextTokenType(interpreter)) { case TokenSOURCE: return cmd_BG_SOURCE(core); case TokenCOPY: return cmd_BG_COPY(core); case TokenSCROLL: return cmd_BG_SCROLL(core); case TokenFILL: return cmd_BG_FILL(core); case TokenTINT: return cmd_BG_TINT(core); case TokenVIEW: return cmd_BG_VIEW(core); default: return cmd_BG(core); } break; case TokenATTR: return cmd_ATTR(core); case TokenPAL: return cmd_PAL(core); case TokenFLIP: return cmd_FLIP(core); case TokenPRIO: return cmd_PRIO(core); case TokenCELL: switch (itp_getNextTokenType(interpreter)) { case TokenSIZE: return cmd_CELL_SIZE(core); default: return cmd_CELL(core); } break; case TokenTINT: return cmd_TINT(core); case TokenMCELL: return cmd_MCELL(core); case TokenPALETTE: return cmd_PALETTE(core); case TokenSCROLL: return cmd_SCROLL(core); case TokenDISPLAY: return cmd_DISPLAY(core); case TokenSPRITEA: return cmd_SPRITE_A(core); case TokenSPRITE: switch (itp_getNextTokenType(interpreter)) { case TokenOFF: return cmd_SPRITE_OFF(core); case TokenVIEW: return cmd_SPRITE_VIEW(core); default: return cmd_SPRITE(core); } break; case TokenSAVE: return cmd_SAVE(core); case TokenLOAD: return cmd_LOAD(core); case TokenFILES: return cmd_FILES(core); case TokenGAMEPAD: return cmd_GAMEPAD(core); case TokenKEYBOARD: return cmd_KEYBOARD(core); case TokenTOUCHSCREEN: return cmd_TOUCHSCREEN(core); case TokenTRACE: return cmd_TRACE(core); case TokenCALL: return cmd_CALL(core); case TokenSUB: return cmd_SUB(core); // case TokenSHARED: // return cmd_SHARED(core); case TokenGLOBAL: return cmd_GLOBAL(core); case TokenEXIT: switch (itp_getNextTokenType(interpreter)) { case TokenSUB: return cmd_EXIT_SUB(core); default: return cmd_EXIT(core); } break; case TokenPAUSE: return cmd_PAUSE(core); case TokenSOUND: switch (itp_getNextTokenType(interpreter)) { // case TokenCOPY: // return cmd_SOUND_COPY(core); case TokenSOURCE: return cmd_SOUND_SOURCE(core); default: return cmd_SOUND(core); } break; case TokenVOLUME: return cmd_VOLUME(core); case TokenENVELOPE: return cmd_ENVELOPE(core); case TokenLFO: switch (itp_getNextTokenType(interpreter)) { case TokenWAVE: return cmd_LFO_WAVE(core); default: return cmd_LFO(core); } break; case TokenLFOA: return cmd_LFO_A(core); case TokenPLAY: return cmd_PLAY(core); case TokenSTOP: return cmd_STOP(core); case TokenMUSIC: return cmd_MUSIC(core); case TokenTRACK: return cmd_TRACK(core); default: printf("Command not implemented: %s\n", TokenStrings[interpreter->pc->type]); return ErrorSyntax; } return ErrorNone; } enum ErrorCode itp_labelStackError(struct LabelStackItem *item) { switch (item->type) { case LabelTypeIF: case LabelTypeELSEIF: case LabelTypeELSE: return ErrorIfWithoutEndIf; case LabelTypeFOR: return ErrorForWithoutNext; case LabelTypeDO: return ErrorDoWithoutLoop; case LabelTypeREPEAT: return ErrorRepeatWithoutUntil; case LabelTypeWHILE: return ErrorWhileWithoutWend; case LabelTypeSUB: return ErrorSubWithoutEndSub; case LabelTypeFORVar: case LabelTypeFORLimit: case LabelTypeGOSUB: case LabelTypeCALL: case LabelTypeONCALL: // should not happen in compile time return ErrorSyntax; } } ================================================ FILE: core/interpreter/interpreter.h ================================================ // // Copyright 2016-2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef interpreter_h #define interpreter_h #include #include #include "interpreter_config.h" #include "tokenizer.h" #include "token.h" #include "error.h" #include "value.h" #include "labels.h" #include "variables.h" #include "data.h" #include "text_lib.h" #include "sprites_lib.h" #include "audio_lib.h" #include "io_chip.h" #include "data_manager.h" #define BAS_TRUE -1.0f #define BAS_FALSE 0.0f struct Core; enum Pass { PassPrepare, PassRun }; enum State { StateNoProgram, StateEvaluate, StateInput, StatePaused, StateWaitForDisk, StateEnd }; enum Mode { ModeNone, ModeMain, ModeInterrupt }; enum InterruptType { InterruptTypeRaster, InterruptTypeVBL }; struct Interpreter { const char *sourceCode; enum Pass pass; enum State state; enum Mode mode; struct Token *pc; int subLevel; int cycles; int interruptOverCycles; bool debug; bool handlesPause; int cpuLoadDisplay; int cpuLoadMax; int cpuLoadTimer; struct Tokenizer tokenizer; struct DataManager romDataManager; struct LabelStackItem labelStackItems[MAX_LABEL_STACK_ITEMS]; int numLabelStackItems; bool isSingleLineIf; struct SimpleVariable simpleVariables[MAX_SIMPLE_VARIABLES]; int numSimpleVariables; struct ArrayVariable arrayVariables[MAX_ARRAY_VARIABLES]; int numArrayVariables; struct RCString *nullString; struct Token *firstData; struct Token *lastData; struct Token *currentDataToken; struct Token *currentDataValueToken; struct Token *currentOnRasterToken; struct Token *currentOnVBLToken; int waitCount; bool exitEvaluation; union Gamepad lastFrameGamepads[NUM_GAMEPADS]; union IOStatus lastFrameIOStatus; float timer; int seed; bool isKeyboardOptional; union Value *lastVariableValue; struct TextLib textLib; struct SpritesLib spritesLib; struct AudioLib audioLib; }; void itp_init(struct Core *core); void itp_deinit(struct Core *core); struct CoreError itp_compileProgram(struct Core *core, const char *sourceCode); void itp_runProgram(struct Core *core); void itp_runInterrupt(struct Core *core, enum InterruptType type); void itp_didFinishVBL(struct Core *core); void itp_endProgram(struct Core *core); void itp_freeProgram(struct Core *core); enum ValueType itp_getIdentifierTokenValueType(struct Token *token); union Value *itp_readVariable(struct Core *core, enum ValueType *type, enum ErrorCode *errorCode, bool forWriting); struct TypedValue itp_evaluateExpression(struct Core *core, enum TypeClass typeClass); struct TypedValue itp_evaluateNumericExpression(struct Core *core, int min, int max); struct TypedValue itp_evaluateOptionalExpression(struct Core *core, enum TypeClass typeClass); struct TypedValue itp_evaluateOptionalNumericExpression(struct Core *core, int min, int max); bool itp_isEndOfCommand(struct Interpreter *interpreter); enum ErrorCode itp_endOfCommand(struct Interpreter *interpreter); enum ErrorCode itp_labelStackError(struct LabelStackItem *item); #endif /* interpreter_h */ ================================================ FILE: core/interpreter/interpreter_config.h ================================================ // // Copyright 2017-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef interpreter_config_h #define interpreter_config_h #define MAX_TOKENS 16384 #define MAX_SYMBOLS 2048 #define MAX_LABEL_STACK_ITEMS 128 #define MAX_JUMP_LABEL_ITEMS 256 #define MAX_SUB_ITEMS 256 #define MAX_SIMPLE_VARIABLES 256 #define MAX_ARRAY_VARIABLES 256 #define SYMBOL_NAME_SIZE 21 #define MAX_ARRAY_DIMENSIONS 4 #define MAX_ARRAY_SIZE 32768 #define MAX_CYCLES_TOTAL_PER_FRAME 17556 #define MAX_CYCLES_PER_VBL 1140 #define MAX_CYCLES_PER_RASTER 51 #define TIMER_WRAP_VALUE 5184000 #endif /* interpreter_config_h */ ================================================ FILE: core/interpreter/interpreter_utils.c ================================================ // // Copyright 2017-2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "interpreter_utils.h" #include "core.h" enum ErrorCode itp_evaluateSimpleAttributes(struct Core *core, struct SimpleAttributes *attrs) { struct Interpreter *interpreter = core->interpreter; attrs->pal = -1; attrs->flipX = -1; attrs->flipY = -1; attrs->prio = -1; attrs->size = -1; bool changed = false; bool checked = false; do { checked = false; // PAL if (interpreter->pc->type == TokenPAL && attrs->pal == -1) { ++interpreter->pc; struct TypedValue value = itp_evaluateNumericExpression(core, 0, NUM_PALETTES - 1); if (value.type == ValueTypeError) return value.v.errorCode; attrs->pal = value.v.floatValue; checked = true; } // FLIP if (interpreter->pc->type == TokenFLIP && attrs->flipX == -1) { ++interpreter->pc; struct TypedValue fxValue = itp_evaluateNumericExpression(core, -1, 1); if (fxValue.type == ValueTypeError) return fxValue.v.errorCode; attrs->flipX = fxValue.v.floatValue ? 1 : 0; // comma if (interpreter->pc->type != TokenComma) return ErrorSyntax; ++interpreter->pc; struct TypedValue fyValue = itp_evaluateNumericExpression(core, -1, 1); if (fyValue.type == ValueTypeError) return fyValue.v.errorCode; attrs->flipY = fyValue.v.floatValue ? 1 : 0; checked = true; } // PRIO if (interpreter->pc->type == TokenPRIO && attrs->prio == -1) { ++interpreter->pc; struct TypedValue value = itp_evaluateNumericExpression(core, -1, 1); if (value.type == ValueTypeError) return value.v.errorCode; attrs->prio = value.v.floatValue ? 1 : 0; checked = true; } // SIZE if (interpreter->pc->type == TokenSIZE && attrs->size == -1) { ++interpreter->pc; struct TypedValue value = itp_evaluateNumericExpression(core, 0, 3); if (value.type == ValueTypeError) return value.v.errorCode; attrs->size = value.v.floatValue; checked = true; } changed |= checked; } while (checked); if (!changed) return ErrorSyntax; return ErrorNone; } struct TypedValue itp_evaluateCharAttributes(struct Core *core, union CharacterAttributes oldAttr) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pc->type == TokenBracketOpen) { // bracket open interpreter->pc++; // obsolete syntax! union CharacterAttributes resultAttr = oldAttr; struct TypedValue palValue = {ValueTypeNull, 0}; struct TypedValue fxValue = {ValueTypeNull, 0}; struct TypedValue fyValue = {ValueTypeNull, 0}; struct TypedValue priValue = {ValueTypeNull, 0}; struct TypedValue sValue = {ValueTypeNull, 0}; // palette value palValue = itp_evaluateOptionalNumericExpression(core, 0, NUM_PALETTES - 1); if (palValue.type == ValueTypeError) return palValue; // comma if (interpreter->pc->type == TokenComma) { ++interpreter->pc; // flip x value fxValue = itp_evaluateOptionalNumericExpression(core, -1, 1); if (fxValue.type == ValueTypeError) return fxValue; // comma if (interpreter->pc->type == TokenComma) { ++interpreter->pc; // flip y value fyValue = itp_evaluateOptionalNumericExpression(core, -1, 1); if (fyValue.type == ValueTypeError) return fyValue; // comma if (interpreter->pc->type == TokenComma) { ++interpreter->pc; // priority value priValue = itp_evaluateOptionalNumericExpression(core, -1, 1); if (priValue.type == ValueTypeError) return priValue; // comma if (interpreter->pc->type == TokenComma) { ++interpreter->pc; // size value sValue = itp_evaluateOptionalNumericExpression(core, 0, 3); if (sValue.type == ValueTypeError) return sValue; } } } } // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); interpreter->pc++; if (interpreter->pass == PassRun) { if (palValue.type != ValueTypeNull) resultAttr.palette = palValue.v.floatValue; if (fxValue.type != ValueTypeNull) resultAttr.flipX = fxValue.v.floatValue; if (fyValue.type != ValueTypeNull) resultAttr.flipY = fyValue.v.floatValue; if (priValue.type != ValueTypeNull) resultAttr.priority = priValue.v.floatValue; if (sValue.type != ValueTypeNull) resultAttr.size = sValue.v.floatValue; } struct TypedValue resultValue; resultValue.type = ValueTypeFloat; resultValue.v.floatValue = resultAttr.value; return resultValue; } else { return itp_evaluateNumericExpression(core, 0, 255); } } struct TypedValue itp_evaluateDisplayAttributes(struct Core *core, union DisplayAttributes oldAttr) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pc->type == TokenBracketOpen) { // bracket open interpreter->pc++; union DisplayAttributes resultAttr = oldAttr; struct TypedValue sValue = {ValueTypeNull, 0}; struct TypedValue bg0Value = {ValueTypeNull, 0}; struct TypedValue bg1Value = {ValueTypeNull, 0}; struct TypedValue bg0SizeValue = {ValueTypeNull, 0}; struct TypedValue bg1SizeValue = {ValueTypeNull, 0}; // sprites value sValue = itp_evaluateOptionalNumericExpression(core, -1, 1); if (sValue.type == ValueTypeError) return sValue; // comma if (interpreter->pc->type == TokenComma) { ++interpreter->pc; // bg0 value bg0Value = itp_evaluateOptionalNumericExpression(core, -1, 1); if (bg0Value.type == ValueTypeError) return bg0Value; // comma if (interpreter->pc->type == TokenComma) { ++interpreter->pc; // bg1 value bg1Value = itp_evaluateOptionalNumericExpression(core, -1, 1); if (bg1Value.type == ValueTypeError) return bg1Value; // comma if (interpreter->pc->type == TokenComma) { ++interpreter->pc; // bg0 cell size value bg0SizeValue = itp_evaluateOptionalNumericExpression(core, -1, 1); if (bg0SizeValue.type == ValueTypeError) return bg0SizeValue; // comma if (interpreter->pc->type == TokenComma) { ++interpreter->pc; // bg1 cell size value bg1SizeValue = itp_evaluateOptionalNumericExpression(core, -1, 1); if (bg1SizeValue.type == ValueTypeError) return bg1SizeValue; } } } } // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); interpreter->pc++; if (interpreter->pass == PassRun) { if (sValue.type != ValueTypeNull) resultAttr.spritesEnabled = sValue.v.floatValue; if (bg0Value.type != ValueTypeNull) resultAttr.planeAEnabled = bg0Value.v.floatValue; if (bg1Value.type != ValueTypeNull) resultAttr.planeBEnabled = bg1Value.v.floatValue; if (bg0SizeValue.type != ValueTypeNull) resultAttr.planeACellSize = bg0SizeValue.v.floatValue; if (bg1SizeValue.type != ValueTypeNull) resultAttr.planeBCellSize = bg1SizeValue.v.floatValue; } struct TypedValue resultValue; resultValue.type = ValueTypeFloat; resultValue.v.floatValue = resultAttr.value; return resultValue; } else { return itp_evaluateNumericExpression(core, 0, 255); } } struct TypedValue itp_evaluateLFOAttributes(struct Core *core, union LFOAttributes oldAttr) { struct Interpreter *interpreter = core->interpreter; if (interpreter->pc->type == TokenBracketOpen) { // bracket open interpreter->pc++; union LFOAttributes resultAttr = oldAttr; struct TypedValue wavValue = {ValueTypeNull, 0}; struct TypedValue invValue = {ValueTypeNull, 0}; struct TypedValue envValue = {ValueTypeNull, 0}; struct TypedValue triValue = {ValueTypeNull, 0}; // wave value wavValue = itp_evaluateOptionalNumericExpression(core, 0, 3); if (wavValue.type == ValueTypeError) return wavValue; // comma if (interpreter->pc->type == TokenComma) { ++interpreter->pc; // invert value invValue = itp_evaluateOptionalNumericExpression(core, -1, 1); if (invValue.type == ValueTypeError) return invValue; // comma if (interpreter->pc->type == TokenComma) { ++interpreter->pc; // env mode value envValue = itp_evaluateOptionalNumericExpression(core, -1, 1); if (envValue.type == ValueTypeError) return envValue; // comma if (interpreter->pc->type == TokenComma) { ++interpreter->pc; // trigger value triValue = itp_evaluateOptionalNumericExpression(core, -1, 1); if (triValue.type == ValueTypeError) return triValue; } } } // bracket close if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax); interpreter->pc++; if (interpreter->pass == PassRun) { if (wavValue.type != ValueTypeNull) resultAttr.wave = wavValue.v.floatValue; if (invValue.type != ValueTypeNull) resultAttr.invert = invValue.v.floatValue; if (envValue.type != ValueTypeNull) resultAttr.envMode = envValue.v.floatValue; if (triValue.type != ValueTypeNull) resultAttr.trigger = triValue.v.floatValue; } struct TypedValue resultValue; resultValue.type = ValueTypeFloat; resultValue.v.floatValue = resultAttr.value; return resultValue; } else { return itp_evaluateNumericExpression(core, 0, 255); } } ================================================ FILE: core/interpreter/interpreter_utils.h ================================================ // // Copyright 2017-2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef interpreter_utils_h #define interpreter_utils_h #include #include #include "value.h" #include "video_chip.h" #include "audio_chip.h" struct Core; struct SimpleAttributes { int pal; int flipX; int flipY; int prio; int size; }; enum ErrorCode itp_evaluateSimpleAttributes(struct Core *core, struct SimpleAttributes *attrs); struct TypedValue itp_evaluateCharAttributes(struct Core *core, union CharacterAttributes oldAttr); struct TypedValue itp_evaluateDisplayAttributes(struct Core *core, union DisplayAttributes oldAttr); struct TypedValue itp_evaluateLFOAttributes(struct Core *core, union LFOAttributes oldAttr); #endif /* interpreter_utils_h */ ================================================ FILE: core/interpreter/labels.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "labels.h" #include "interpreter.h" enum ErrorCode lab_pushLabelStackItem(struct Interpreter *interpreter, enum LabelType type, struct Token *token) { if (interpreter->numLabelStackItems >= MAX_LABEL_STACK_ITEMS) return ErrorStackOverflow; struct LabelStackItem *item = &interpreter->labelStackItems[interpreter->numLabelStackItems]; item->type = type; item->token = token; interpreter->numLabelStackItems++; return ErrorNone; } struct LabelStackItem *lab_popLabelStackItem(struct Interpreter *interpreter) { if (interpreter->numLabelStackItems > 0) { interpreter->numLabelStackItems--; return &interpreter->labelStackItems[interpreter->numLabelStackItems]; } return NULL; } struct LabelStackItem *lab_peekLabelStackItem(struct Interpreter *interpreter) { if (interpreter->numLabelStackItems > 0) { return &interpreter->labelStackItems[interpreter->numLabelStackItems - 1]; } return NULL; } struct LabelStackItem *lab_searchLabelStackItem(struct Interpreter *interpreter, enum LabelType types[], int numTypes) { int i = interpreter->numLabelStackItems - 1; while (i >= 0) { struct LabelStackItem *item = &interpreter->labelStackItems[i]; for (int j = 0; j < numTypes; j++) { if (item->type == types[j]) { return item; } } --i; } return NULL; } ================================================ FILE: core/interpreter/labels.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef labels_h #define labels_h #include struct Interpreter; struct Token; enum LabelType { LabelTypeIF, LabelTypeELSE, LabelTypeELSEIF, LabelTypeFOR, LabelTypeFORVar, LabelTypeFORLimit, LabelTypeGOSUB, LabelTypeDO, LabelTypeREPEAT, LabelTypeWHILE, LabelTypeSUB, LabelTypeCALL, LabelTypeONCALL, }; struct LabelStackItem { enum LabelType type; struct Token *token; }; enum ErrorCode lab_pushLabelStackItem(struct Interpreter *interpreter, enum LabelType type, struct Token *token); struct LabelStackItem *lab_popLabelStackItem(struct Interpreter *interpreter); struct LabelStackItem *lab_peekLabelStackItem(struct Interpreter *interpreter); struct LabelStackItem *lab_searchLabelStackItem(struct Interpreter *interpreter, enum LabelType types[], int numTypes); #endif /* labels_h */ ================================================ FILE: core/interpreter/rcstring.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "rcstring.h" #include #include struct RCString *rcstring_new(const char *chars, size_t len) { size_t size = sizeof(struct RCString) + len; struct RCString *string = malloc(size); if (string) { string->refCount = 1; // retain if (chars) { memcpy(string->chars, chars, len); } string->chars[len] = 0; // end of string } return string; } void rcstring_retain(struct RCString *string) { string->refCount++; } void rcstring_release(struct RCString *string) { string->refCount--; if (string->refCount == 0) { free((void *)string); } } ================================================ FILE: core/interpreter/rcstring.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef string_h #define string_h #include struct RCString { int refCount; char chars[1]; // ... }; struct RCString *rcstring_new(const char *chars, size_t len); void rcstring_retain(struct RCString *string); void rcstring_release(struct RCString *string); #endif /* string_h */ ================================================ FILE: core/interpreter/string_utils.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "string_utils.h" #include #include const char *uppercaseString(const char *source) { size_t len = strlen(source); char *buffer = malloc(len + 1); if (buffer) { const char *sourceChar = source; char *destChar = buffer; char finalChar = 0; while (*sourceChar) { finalChar = *sourceChar++; if (finalChar >= 'a' && finalChar <= 'z') { finalChar -= 32; } *destChar++ = finalChar; } *destChar = 0; } return buffer; } const char *lineString(const char *source, int pos) { const char *start = &source[pos]; const char *end = &source[pos]; while (start - 1 >= source && *(start - 1) != '\n') { start--; } while (*(end + 1) != 0 && *end != '\n' && *end != 0) { end++; } if (end > start) { size_t len = end - start; char *buffer = malloc(len + 1); if (buffer) { strncpy(buffer, start, len); buffer[len] = 0; return buffer; } } return NULL; } int lineNumber(const char *source, int pos) { int line = 1; for (int i = 0; i < pos; i++) { if (source[i] == '\n') { line++; } } return line; } void stringConvertCopy(char *dest, const char *source, size_t length) { char *currDstChar = dest; for (int i = 0; i < length; i++) { char currSrcChar = source[i]; if (currSrcChar != '\r') { *currDstChar = currSrcChar; currDstChar++; } } *currDstChar = 0; } ================================================ FILE: core/interpreter/string_utils.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef string_utils_h #define string_utils_h #include const char *uppercaseString(const char *source); const char *lineString(const char *source, int pos); int lineNumber(const char *source, int pos); void stringConvertCopy(char *dest, const char *source, size_t length); #endif /* string_utils_h */ ================================================ FILE: core/interpreter/token.c ================================================ // // Copyright 2017-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "token.h" const char *TokenStrings[] = { NULL, NULL, NULL, NULL, NULL, NULL, // Signs ":", ",", ";", "'", NULL, // Operators "=", ">=", "<=", "<>", ">", "<", "(", ")", "+", "-", "*", "/", "\\", "^", "AND", "NOT", "OR", "XOR", "MOD", // Commands/Functions "ABS", "ACOS", "ADD", "ASC", "ASIN", "ATAN", "ATTR", "BG", "BIN$", "BUTTON", "CALL", "CELL.A", "CELL.C", "CELL", "CHAR", "CHR$", "CLS", "CLW", "COLOR", "COPY", "COS", "CURSOR.X", "CURSOR.Y", "DATA", "DEC", "DIM", "DISPLAY", "DOWN", "DO", "ELSE", "END", "ENVELOPE", "EXIT", "EXP", "FILE$", "FILES", "FILL", "FLIP", "FONT", "FOR", "FSIZE", "GAMEPAD", "GLOBAL", "GOSUB", "GOTO", "HEX$", "HCOS", "HIT", "HSIN", "HTAN", "IF", "INC", "INKEY$", "INPUT", "INSTR", "INT", "KEYBOARD", "LEFT$", "LEFT", "LEN", "LET", "LFO.A", "LFO", "LOAD", "LOCATE", "LOG", "LOOP", "MAX", "MCELL.A", "MCELL.C", "MCELL", "MID$", "MIN", "MUSIC", "NEXT", "NUMBER", "OFF", "ON", "OPTIONAL", "PALETTE", "PAL", "PAUSE", "PEEKL", "PEEKW", "PEEK", "PI", "PLAY", "POKEL", "POKEW", "POKE", "PRINT", "PRIO", "RANDOMIZE", "RASTER", "READ", "REM", "REPEAT", "RESTORE", "RETURN", "RIGHT$", "RIGHT", "RND", "ROL", "ROM", "ROR", "SAVE", "SCROLL.X", "SCROLL.Y", "SCROLL", "SGN", "SIN", "SIZE", "SOUND", "SOURCE", "SPRITE.A", "SPRITE.C", "SPRITE.X", "SPRITE.Y", "SPRITE", "SQR", "STEP", "STOP", "STR$", "SUB", "SWAP", "SYSTEM", "TAN", "TAP", "TEXT", "THEN", "TIMER", "TINT", "TOUCHSCREEN", "TOUCH.X", "TOUCH.Y", "TOUCH", "TO", "TRACE", "TRACK", "UBOUND", "UNTIL", "UP", "VAL", "VBL", "VIEW", "VOLUME", "WAIT", "WAVE", "WEND", "WHILE", "WINDOW", // Reserved Keywords NULL, "ANIM", "CLOSE", "DECLARE", "DEF", "FLASH", "FN", "FUNCTION", "LBOUND", "OPEN", "OUTPUT", "SHARED", "STATIC", "TEMPO", "VOICE", "WRITE", NULL }; ================================================ FILE: core/interpreter/token.h ================================================ // // Copyright 2017-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef token_h #define token_h #include #include "rcstring.h" enum TokenType { TokenUndefined, TokenIdentifier, TokenStringIdentifier, TokenLabel, TokenFloat, TokenString, // Signs TokenColon, TokenComma, TokenSemicolon, TokenApostrophe, TokenEol, // Operators TokenEq, TokenGrEq, TokenLeEq, TokenUneq, TokenGr, TokenLe, TokenBracketOpen, TokenBracketClose, TokenPlus, TokenMinus, TokenMul, TokenDiv, TokenDivInt, TokenPow, TokenAND, TokenNOT, TokenOR, TokenXOR, TokenMOD, // Commands/Functions TokenABS, TokenACOS, TokenADD, TokenASC, TokenASIN, TokenATAN, TokenATTR, TokenBG, TokenBIN, TokenBUTTON, TokenCALL, TokenCELLA, TokenCELLC, TokenCELL, TokenCHAR, TokenCHR, TokenCLS, TokenCLW, TokenCOLOR, TokenCOPY, TokenCOS, TokenCURSORX, TokenCURSORY, TokenDATA, TokenDEC, TokenDIM, TokenDISPLAY, TokenDOWN, TokenDO, TokenELSE, TokenEND, TokenENVELOPE, TokenEXIT, TokenEXP, TokenFILE, TokenFILES, TokenFILL, TokenFLIP, TokenFONT, TokenFOR, TokenFSIZE, TokenGAMEPAD, TokenGLOBAL, TokenGOSUB, TokenGOTO, TokenHEX, TokenHCOS, TokenHIT, TokenHSIN, TokenHTAN, TokenIF, TokenINC, TokenINKEY, TokenINPUT, TokenINSTR, TokenINT, TokenKEYBOARD, TokenLEFTStr, TokenLEFT, TokenLEN, TokenLET, TokenLFOA, TokenLFO, TokenLOAD, TokenLOCATE, TokenLOG, TokenLOOP, TokenMAX, TokenMCELLA, TokenMCELLC, TokenMCELL, TokenMID, TokenMIN, TokenMUSIC, TokenNEXT, TokenNUMBER, TokenOFF, TokenON, TokenOPTIONAL, TokenPALETTE, TokenPAL, TokenPAUSE, TokenPEEKL, TokenPEEKW, TokenPEEK, TokenPI, TokenPLAY, TokenPOKEL, TokenPOKEW, TokenPOKE, TokenPRINT, TokenPRIO, TokenRANDOMIZE, TokenRASTER, TokenREAD, TokenREM, TokenREPEAT, TokenRESTORE, TokenRETURN, TokenRIGHTStr, TokenRIGHT, TokenRND, TokenROL, TokenROM, TokenROR, TokenSAVE, TokenSCROLLX, TokenSCROLLY, TokenSCROLL, TokenSGN, TokenSIN, TokenSIZE, TokenSOUND, TokenSOURCE, TokenSPRITEA, TokenSPRITEC, TokenSPRITEX, TokenSPRITEY, TokenSPRITE, TokenSQR, TokenSTEP, TokenSTOP, TokenSTR, TokenSUB, TokenSWAP, TokenSYSTEM, TokenTAN, TokenTAP, TokenTEXT, TokenTHEN, TokenTIMER, TokenTINT, TokenTOUCHSCREEN, TokenTOUCHX, TokenTOUCHY, TokenTOUCH, TokenTO, TokenTRACE, TokenTRACK, TokenUBOUND, TokenUNTIL, TokenUP, TokenVAL, TokenVBL, TokenVIEW, TokenVOLUME, TokenWAIT, TokenWAVE, TokenWEND, TokenWHILE, TokenWINDOW, // Reserved Keywords Token_reserved, TokenANIM, TokenCLOSE, TokenDECLARE, TokenDEF, TokenFLASH, TokenFN, TokenFUNCTION, TokenLBOUND, TokenOPEN, TokenOUTPUT, TokenSHARED, TokenSTATIC, TokenTEMPO, TokenVOICE, TokenWRITE, Token_count }; struct Token { enum TokenType type; union { float floatValue; struct RCString *stringValue; int symbolIndex; struct Token *jumpToken; }; int sourcePosition; }; extern const char *TokenStrings[]; #endif /* token_h */ ================================================ FILE: core/interpreter/tokenizer.c ================================================ // // Copyright 2016-2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "tokenizer.h" #include "error.h" #include "charsets.h" #include #include #include "string_utils.h" struct CoreError tok_tokenizeProgram(struct Tokenizer *tokenizer, const char *sourceCode) { const char *uppercaseSourceCode = uppercaseString(sourceCode); if (!uppercaseSourceCode) return err_makeCoreError(ErrorOutOfMemory, -1); struct CoreError error = tok_tokenizeUppercaseProgram(tokenizer, uppercaseSourceCode); free((void *)uppercaseSourceCode); return error; } struct CoreError tok_tokenizeUppercaseProgram(struct Tokenizer *tokenizer, const char *sourceCode) { const char *character = sourceCode; // PROGRAM while (*character && *character != '#') { int tokenSourcePosition = (int)(character - sourceCode); if (tokenizer->numTokens >= MAX_TOKENS - 1) { return err_makeCoreError(ErrorTooManyTokens, tokenSourcePosition); } struct Token *token = &tokenizer->tokens[tokenizer->numTokens]; token->sourcePosition = tokenSourcePosition; // line break \n or \n\r if (*character == '\n') { token->type = TokenEol; tokenizer->numTokens++; character++; if (*character == '\r') { character++; } continue; } // line break \r or \r\n if (*character == '\r') { token->type = TokenEol; tokenizer->numTokens++; character++; if (*character == '\n') { character++; } continue; } // space if (*character == ' ' || *character == '\t') { character++; continue; } // string if (*character == '"') { character++; const char *firstCharacter = character; while (*character && *character != '"') { if (*character == '\n') { return err_makeCoreError(ErrorUnterminatedString, (int)(character - sourceCode)); } else if (*character < 0) { return err_makeCoreError(ErrorUnexpectedCharacter, (int)(character - sourceCode)); } character++; } int len = (int)(character - firstCharacter); struct RCString *string = rcstring_new(firstCharacter, len); if (!string) return err_makeCoreError(ErrorOutOfMemory, tokenSourcePosition); token->type = TokenString; token->stringValue = string; tokenizer->numTokens++; character++; continue; } // number if (strchr(CharSetDigits, *character)) { float number = 0; int afterDot = 0; while (*character) { if (strchr(CharSetDigits, *character)) { int digit = (int)*character - (int)'0'; if (afterDot == 0) { number *= 10; number += digit; } else { number += (float)digit / afterDot; afterDot *= 10; } character++; } else if (*character == '.' && afterDot == 0) { afterDot = 10; character++; } else { break; } } token->type = TokenFloat; token->floatValue = number; tokenizer->numTokens++; continue; } // hex number if (*character == '$') { character++; int number = 0; while (*character) { char *spos = strchr(CharSetHex, *character); if (spos) { int digit = (int)(spos - CharSetHex); number <<= 4; number += digit; character++; } else { break; } } token->type = TokenFloat; token->floatValue = number; tokenizer->numTokens++; continue; } // bin number if (*character == '%') { character++; int number = 0; while (*character) { if (*character == '0' || *character == '1') { int digit = *character - '0'; number <<= 1; number += digit; character++; } else { break; } } token->type = TokenFloat; token->floatValue = number; tokenizer->numTokens++; continue; } // Keyword enum TokenType foundKeywordToken = TokenUndefined; for (int i = 0; i < Token_count; i++) { const char *keyword = TokenStrings[i]; if (keyword) { size_t keywordLen = strlen(keyword); int keywordIsAlphaNum = strchr(CharSetAlphaNum, keyword[0]) != NULL; for (int pos = 0; pos <= keywordLen; pos++) { char textCharacter = character[pos]; if (pos < keywordLen) { char symbCharacter = keyword[pos]; if (symbCharacter != textCharacter) { // not matching break; } } else if (keywordIsAlphaNum && textCharacter && strchr(CharSetAlphaNum, textCharacter)) { // matching, but word is longer, so seems to be an identifier break; } else { // symbol found! foundKeywordToken = i; character += keywordLen; break; } } if (foundKeywordToken != TokenUndefined) { break; } } } if (foundKeywordToken != TokenUndefined) { if (foundKeywordToken == TokenREM || foundKeywordToken == TokenApostrophe) { // REM comment, skip until end of line while (*character) { if (*character < 0) { return err_makeCoreError(ErrorUnexpectedCharacter, (int)(character - sourceCode)); } if (*character == '\n') { character++; break; } character++; } } else if (foundKeywordToken > Token_reserved) { return err_makeCoreError(ErrorReservedKeyword, tokenSourcePosition); } token->type = foundKeywordToken; tokenizer->numTokens++; continue; } // Symbol if (strchr(CharSetLetters, *character)) { const char *firstCharacter = character; char isString = 0; while (*character) { if (strchr(CharSetAlphaNum, *character)) { character++; } else { if (*character == '$') { isString = 1; character++; } break; } } if (tokenizer->numSymbols >= MAX_SYMBOLS) { return err_makeCoreError(ErrorTooManySymbols, tokenSourcePosition); } int len = (int)(character - firstCharacter); if (len >= SYMBOL_NAME_SIZE) { return err_makeCoreError(ErrorSymbolNameTooLong, tokenSourcePosition); } char symbolName[SYMBOL_NAME_SIZE]; memcpy(symbolName, firstCharacter, len); symbolName[len] = 0; int symbolIndex = -1; // find existing symbol for (int i = 0; i < MAX_SYMBOLS && tokenizer->symbols[i].name[0] != 0; i++) { if (strcmp(symbolName, tokenizer->symbols[i].name) == 0) { symbolIndex = i; break; } } if (symbolIndex == -1) { // add new symbol strcpy(tokenizer->symbols[tokenizer->numSymbols].name, symbolName); symbolIndex = tokenizer->numSymbols++; } if (isString) { token->type = TokenStringIdentifier; } else if (*character == ':') { token->type = TokenLabel; character++; enum ErrorCode errorCode = tok_setJumpLabel(tokenizer, symbolIndex, token + 1); if (errorCode != ErrorNone) return err_makeCoreError(errorCode, tokenSourcePosition); } else { token->type = TokenIdentifier; if (tokenizer->numTokens > 0 && tokenizer->tokens[tokenizer->numTokens - 1].type == TokenSUB) { enum ErrorCode errorCode = tok_setSub(tokenizer, symbolIndex, token + 1); if (errorCode != ErrorNone) return err_makeCoreError(errorCode, tokenSourcePosition); } } token->symbolIndex = symbolIndex; tokenizer->numTokens++; continue; } // Unexpected character return err_makeCoreError(ErrorUnexpectedCharacter, tokenSourcePosition); } // add EOL to the end struct Token *token = &tokenizer->tokens[tokenizer->numTokens]; token->sourcePosition = (int)(character - sourceCode); token->type = TokenEol; tokenizer->numTokens++; return err_noCoreError(); } void tok_freeTokens(struct Tokenizer *tokenizer) { // Free string tokens for (int i = 0; i < tokenizer->numTokens; i++) { struct Token *token = &tokenizer->tokens[i]; if (token->type == TokenString) { rcstring_release(token->stringValue); } } memset(tokenizer, 0, sizeof(struct Tokenizer)); } struct JumpLabelItem *tok_getJumpLabel(struct Tokenizer *tokenizer, int symbolIndex) { struct JumpLabelItem *item; for (int i = 0; i < tokenizer->numJumpLabelItems; i++) { item = &tokenizer->jumpLabelItems[i]; if (item->symbolIndex == symbolIndex) { return item; } } return NULL; } enum ErrorCode tok_setJumpLabel(struct Tokenizer *tokenizer, int symbolIndex, struct Token *token) { if (tok_getJumpLabel(tokenizer, symbolIndex) != NULL) { return ErrorLabelAlreadyDefined; } if (tokenizer->numJumpLabelItems >= MAX_JUMP_LABEL_ITEMS) { return ErrorTooManyLabels; } struct JumpLabelItem *item = &tokenizer->jumpLabelItems[tokenizer->numJumpLabelItems]; item->symbolIndex = symbolIndex; item->token = token; tokenizer->numJumpLabelItems++; return ErrorNone; } struct SubItem *tok_getSub(struct Tokenizer *tokenizer, int symbolIndex) { struct SubItem *item; for (int i = 0; i < tokenizer->numSubItems; i++) { item = &tokenizer->subItems[i]; if (item->symbolIndex == symbolIndex) { return item; } } return NULL; } enum ErrorCode tok_setSub(struct Tokenizer *tokenizer, int symbolIndex, struct Token *token) { if (tok_getSub(tokenizer, symbolIndex) != NULL) { return ErrorSubAlreadyDefined; } if (tokenizer->numSubItems >= MAX_SUB_ITEMS) { return ErrorTooManySubprograms; } struct SubItem *item = &tokenizer->subItems[tokenizer->numSubItems]; item->symbolIndex = symbolIndex; item->token = token; tokenizer->numSubItems++; return ErrorNone; } ================================================ FILE: core/interpreter/tokenizer.h ================================================ // // Copyright 2016-2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef tokenizer_h #define tokenizer_h #include #include "interpreter_config.h" #include "token.h" struct Symbol { char name[SYMBOL_NAME_SIZE]; }; struct JumpLabelItem { int symbolIndex; struct Token *token; }; struct SubItem { int symbolIndex; struct Token *token; }; struct Tokenizer { struct Token tokens[MAX_TOKENS]; int numTokens; struct Symbol symbols[MAX_SYMBOLS]; int numSymbols; struct JumpLabelItem jumpLabelItems[MAX_JUMP_LABEL_ITEMS]; int numJumpLabelItems; struct SubItem subItems[MAX_SUB_ITEMS]; int numSubItems; }; struct CoreError tok_tokenizeProgram(struct Tokenizer *tokenizer, const char *sourceCode); struct CoreError tok_tokenizeUppercaseProgram(struct Tokenizer *tokenizer, const char *sourceCode); void tok_freeTokens(struct Tokenizer *tokenizer); struct JumpLabelItem *tok_getJumpLabel(struct Tokenizer *tokenizer, int symbolIndex); enum ErrorCode tok_setJumpLabel(struct Tokenizer *tokenizer, int symbolIndex, struct Token *token); struct SubItem *tok_getSub(struct Tokenizer *tokenizer, int symbolIndex); enum ErrorCode tok_setSub(struct Tokenizer *tokenizer, int symbolIndex, struct Token *token); #endif /* tokenizer_h */ ================================================ FILE: core/interpreter/value.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "value.h" union Value ValueDummy = {0}; struct TypedValue val_makeError(enum ErrorCode errorCode) { struct TypedValue value; value.type = ValueTypeError; value.v.errorCode = errorCode; return value; } ================================================ FILE: core/interpreter/value.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef value_h #define value_h #include #include "error.h" #include "rcstring.h" enum ValueType { ValueTypeNull, ValueTypeError, ValueTypeFloat, ValueTypeString }; union Value { float floatValue; struct RCString *stringValue; union Value *reference; enum ErrorCode errorCode; }; struct TypedValue { enum ValueType type; union Value v; }; enum TypeClass { TypeClassAny, TypeClassNumeric, TypeClassString }; extern union Value ValueDummy; struct TypedValue val_makeError(enum ErrorCode errorCode); #endif /* value_h */ ================================================ FILE: core/interpreter/variables.c ================================================ // // Copyright 2017-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "variables.h" #include "core.h" #include #include struct SimpleVariable *var_getSimpleVariable(struct Interpreter *interpreter, int symbolIndex, int subLevel) { struct SimpleVariable *variable = NULL; for (int i = interpreter->numSimpleVariables - 1; i >= 0; i--) { variable = &interpreter->simpleVariables[i]; if (variable->symbolIndex == symbolIndex && (variable->subLevel == subLevel || variable->subLevel == SUB_LEVEL_GLOBAL)) { // variable found return variable; } } return NULL; } struct SimpleVariable *var_createSimpleVariable(struct Interpreter *interpreter, enum ErrorCode *errorCode, int symbolIndex, int subLevel, enum ValueType type, union Value *valueReference) { if (interpreter->numSimpleVariables >= MAX_SIMPLE_VARIABLES) { *errorCode = ErrorOutOfMemory; return NULL; } if (subLevel > 127) { *errorCode = ErrorStackOverflow; return NULL; } struct SimpleVariable *variable = &interpreter->simpleVariables[interpreter->numSimpleVariables]; interpreter->numSimpleVariables++; memset(variable, 0, sizeof(struct SimpleVariable)); variable->symbolIndex = symbolIndex; variable->subLevel = subLevel; variable->type = type; if (valueReference) { variable->isReference = 1; variable->v.reference = valueReference; } else { variable->isReference = 0; if (type == ValueTypeString) { // assign global NullString variable->v.stringValue = interpreter->nullString; rcstring_retain(variable->v.stringValue); } } return variable; } void var_freeSimpleVariables(struct Interpreter *interpreter, int minSubLevel) { for (int i = interpreter->numSimpleVariables - 1; i >= 0; i--) { struct SimpleVariable *variable = &interpreter->simpleVariables[i]; if (variable->subLevel < minSubLevel) { break; } else { if (!variable->isReference && variable->type == ValueTypeString) { rcstring_release(variable->v.stringValue); } interpreter->numSimpleVariables--; } } } struct ArrayVariable *var_getArrayVariable(struct Interpreter *interpreter, int symbolIndex, int subLevel) { struct ArrayVariable *variable = NULL; for (int i = interpreter->numArrayVariables - 1; i >= 0; i--) { variable = &interpreter->arrayVariables[i]; if (variable->symbolIndex == symbolIndex && (variable->subLevel == subLevel || variable->subLevel == SUB_LEVEL_GLOBAL)) { // variable found return variable; } } return NULL; } union Value *var_getArrayValue(struct Interpreter *interpreter, struct ArrayVariable *variable, int *indices) { int offset = 0; int factor = 1; for (int i = variable->numDimensions - 1; i >= 0; i--) { offset += indices[i] * factor; factor *= variable->dimensionSizes[i]; } union Value *value = &variable->values[offset]; if (variable->type == ValueTypeString && !value->stringValue) { // string variable was still uninitialized, assign global NullString value->stringValue = interpreter->nullString; rcstring_retain(value->stringValue); } return value; } struct ArrayVariable *var_dimVariable(struct Interpreter *interpreter, enum ErrorCode *errorCode, int symbolIndex, int numDimensions, int *dimensionSizes) { if (var_getArrayVariable(interpreter, symbolIndex, interpreter->subLevel)) { *errorCode = ErrorArrayAlreadyDimensionized; return NULL; } if (var_getSimpleVariable(interpreter, symbolIndex, interpreter->subLevel)) { *errorCode = ErrorVariableAlreadyUsed; return NULL; } if (interpreter->numArrayVariables >= MAX_ARRAY_VARIABLES) { *errorCode = ErrorOutOfMemory; return NULL; } struct ArrayVariable *variable = &interpreter->arrayVariables[interpreter->numArrayVariables]; interpreter->numArrayVariables++; memset(variable, 0, sizeof(struct ArrayVariable)); variable->symbolIndex = symbolIndex; variable->subLevel = interpreter->subLevel; variable->isReference = 0; variable->numDimensions = numDimensions; size_t size = 1; for (int i = 0; i < numDimensions; i++) { size *= dimensionSizes[i]; variable->dimensionSizes[i] = dimensionSizes[i]; } if (size > MAX_ARRAY_SIZE) { *errorCode = ErrorOutOfMemory; return NULL; } variable->values = calloc(size, sizeof(union Value)); if (!variable->values) exit(EXIT_FAILURE); variable->numValues = (int)size; return variable; } struct ArrayVariable *var_createArrayVariable(struct Interpreter *interpreter, enum ErrorCode *errorCode, int symbolIndex, int subLevel, struct ArrayVariable *arrayReference) { if (interpreter->numArrayVariables >= MAX_ARRAY_VARIABLES) { *errorCode = ErrorOutOfMemory; return NULL; } if (subLevel > 127) { *errorCode = ErrorStackOverflow; return NULL; } struct ArrayVariable *variable = &interpreter->arrayVariables[interpreter->numArrayVariables]; interpreter->numArrayVariables++; memset(variable, 0, sizeof(struct ArrayVariable)); variable->symbolIndex = symbolIndex; variable->subLevel = subLevel; variable->isReference = 1; variable->type = arrayReference->type; int numDimensions = arrayReference->numDimensions; variable->numDimensions = numDimensions; for (int i = 0; i < numDimensions; i++) { variable->dimensionSizes[i] = arrayReference->dimensionSizes[i]; } variable->values = arrayReference->values; return variable; } void var_freeArrayVariables(struct Interpreter *interpreter, int minSubLevel) { for (int i = interpreter->numArrayVariables - 1; i >= 0; i--) { struct ArrayVariable *variable = &interpreter->arrayVariables[i]; if (variable->subLevel < minSubLevel) { break; } else { if (!variable->isReference) { if (variable->type == ValueTypeString) { int numElements = 1; for (int di = 0; di < variable->numDimensions; di++) { numElements *= variable->dimensionSizes[di]; } for (int ei = 0; ei < numElements; ei++) { union Value *value = &variable->values[ei]; if (value->stringValue) { rcstring_release(value->stringValue); } } } free(variable->values); variable->values = NULL; } interpreter->numArrayVariables--; } } } ================================================ FILE: core/interpreter/variables.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef variables_h #define variables_h #include #include #include #include "interpreter_config.h" #include "value.h" #define SUB_LEVEL_GLOBAL -1 struct Core; struct Interpreter; struct SimpleVariable { int symbolIndex; int8_t subLevel; int8_t isReference:1; enum ValueType type; union Value v; }; struct ArrayVariable { int symbolIndex; int8_t subLevel; int8_t isReference:1; enum ValueType type; int numDimensions; int dimensionSizes[MAX_ARRAY_DIMENSIONS]; int numValues; union Value *values; }; struct SimpleVariable *var_getSimpleVariable(struct Interpreter *interpreter, int symbolIndex, int subLevel); struct SimpleVariable *var_createSimpleVariable(struct Interpreter *interpreter, enum ErrorCode *errorCode, int symbolIndex, int subLevel, enum ValueType type, union Value *valueReference); void var_freeSimpleVariables(struct Interpreter *interpreter, int minSubLevel); struct ArrayVariable *var_getArrayVariable(struct Interpreter *interpreter, int symbolIndex, int subLevel); union Value *var_getArrayValue(struct Interpreter *interpreter, struct ArrayVariable *variable, int *indices); struct ArrayVariable *var_dimVariable(struct Interpreter *interpreter, enum ErrorCode *errorCode, int symbolIndex, int numDimensions, int *dimensionSizes); struct ArrayVariable *var_createArrayVariable(struct Interpreter *interpreter, enum ErrorCode *errorCode, int symbolIndex, int subLevel, struct ArrayVariable *arrayReference); void var_freeArrayVariables(struct Interpreter *interpreter, int minSubLevel); #endif /* variables_h */ ================================================ FILE: core/libraries/audio_lib.c ================================================ // // Copyright 2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "audio_lib.h" #include "core.h" #include #define SOUND_SIZE 8 #define PATTERN_SIZE 4 #define ROW_SIZE 3 #define PATTERNS_OFFSET (NUM_SOUNDS * SOUND_SIZE) #define TRACKS_OFFSET (PATTERNS_OFFSET + NUM_PATTERNS * PATTERN_SIZE) struct TrackRow { int note; int sound; int volume; int command; int parameter; }; void audlib_updateMusic(struct AudioLib *lib); void audlib_updateTrack(struct AudioLib *lib, int voiceIndex); void audlib_setPitch(struct Voice *voice, float pitch); bool audlib_isPatternEmpty(struct AudioLib *lib, int sourceAddress, int pattern); int audlib_getLoopStart(struct AudioLib *lib, int sourceAddress, int pattern); int audlib_getLoop(struct AudioLib *lib, int sourceAddress, int pattern, int param); int audlib_getTrack(struct AudioLib *lib, int sourceAddress, int pattern, int voice); struct TrackRow audlib_getTrackRow(struct AudioLib *lib, int sourceAddress, int track, int row); void audlib_playRow(struct AudioLib *lib, struct ComposerPlayer *player, int track, int voice); void audlib_command(struct AudioLib *lib, struct Voice *voice, struct ComposerPlayer *player, int command, int parameter); void audlib_play(struct AudioLib *lib, int voiceIndex, float pitch, int len, int sound) { struct Core *core = lib->core; struct Voice *voice = &core->machine->audioRegisters.voices[voiceIndex]; audlib_setPitch(voice, pitch); if (sound != -1) { audlib_copySound(lib, lib->sourceAddress, sound, voiceIndex); } if (len != -1) { voice->length = len; voice->attr.timeout = (len > 0) ? 1 : 0; } voice->status.init = 1; voice->status.gate = 1; machine_enableAudio(core); } void audlib_copySound(struct AudioLib *lib, int sourceAddress, int sound, int voiceIndex) { int addr = sourceAddress + sound * 8; int dest = 0xFF40 + voiceIndex * sizeof(struct Voice) + 4; for (int i = 0; i < 8; i++) { int peek = machine_peek(lib->core, addr++); machine_poke(lib->core, dest++, peek); } lib->core->interpreter->cycles += 8; } void audlib_playMusic(struct AudioLib *lib, int startPattern) { struct ComposerPlayer *player = &lib->musicPlayer; player->sourceAddress = lib->sourceAddress; player->index = startPattern; player->tick = -1; player->row = 0; player->speed = 8; player->willBreak = false; machine_enableAudio(lib->core); } void audlib_playTrack(struct AudioLib *lib, int track, int voiceIndex) { struct ComposerPlayer *player = &lib->trackPlayers[voiceIndex]; player->sourceAddress = lib->sourceAddress; player->index = track; player->tick = -1; player->row = 0; player->speed = 8; player->willBreak = false; machine_enableAudio(lib->core); } void audlib_stopAll(struct AudioLib *lib) { lib->musicPlayer.speed = 0; for (int i = 0; i < NUM_VOICES; i++) { struct Voice *voice = &lib->core->machine->audioRegisters.voices[i]; voice->status.gate = 0; lib->trackPlayers[i].speed = 0; } } void audlib_stopVoice(struct AudioLib *lib, int voiceIndex) { struct Voice *voice = &lib->core->machine->audioRegisters.voices[voiceIndex]; voice->status.gate = 0; lib->trackPlayers[voiceIndex].speed = 0; } void audlib_update(struct AudioLib *lib) { if (lib->musicPlayer.speed) { audlib_updateMusic(lib); } for (int v = 0; v < NUM_VOICES; v++) { if (lib->trackPlayers[v].speed) { audlib_updateTrack(lib, v); } } } void audlib_updateMusic(struct AudioLib *lib) { struct ComposerPlayer *player = &lib->musicPlayer; if (player->tick == -1) { player->tick = 0; } else if (player->tick == 0) { if (player->willBreak) { player->row = 0; player->willBreak = false; } else { player->row = (player->row + 1) % NUM_TRACK_ROWS; } if (player->row == 0 && player->speed) { if (audlib_getLoop(lib, player->sourceAddress, player->index, 2) == 1) { player->speed = 0; return; } if (audlib_getLoop(lib, player->sourceAddress, player->index, 1) == 1) { player->index = audlib_getLoopStart(lib, player->sourceAddress, player->index); } else { int p = player->index + 1; if (p < NUM_PATTERNS) { if (audlib_isPatternEmpty(lib, player->sourceAddress, p)) { player->speed = 0; return; } else { player->index = p; } } else { player->speed = 0; return; } } } } if (player->tick == 0) { for (int v = 0; v < NUM_VOICES; v++) { // play only if no other track is playing on that voice if (lib->trackPlayers[v].speed == 0) { int track = audlib_getTrack(lib, player->sourceAddress, player->index, v); if (track >= 0) { audlib_playRow(lib, player, track, v); } } } if (player->speed == 0) { return; } } player->tick = (player->tick + 1) % player->speed; } void audlib_updateTrack(struct AudioLib *lib, int voiceIndex) { struct ComposerPlayer *player = &lib->trackPlayers[voiceIndex]; if (player->tick == -1) { player->tick = 0; } else if (player->tick == 0) { player->row = (player->row + 1) % NUM_TRACK_ROWS; if (player->row == 0 || player->willBreak) { player->willBreak = false; player->speed = 0; return; } } if (player->tick == 0) { audlib_playRow(lib, player, player->index, voiceIndex); if (player->speed == 0) { return; } } player->tick = (player->tick + 1) % player->speed; } void audlib_setPitch(struct Voice *voice, float pitch) { int f = 16.0 * 440.0 * pow(2.0, (pitch - 58.0) / 12.0); voice->frequencyLow = f & 0xFF; voice->frequencyHigh = f >> 8; } bool audlib_isPatternEmpty(struct AudioLib *lib, int sourceAddress, int pattern) { for (int v = 0; v < NUM_VOICES; v++) { if (audlib_getTrack(lib, sourceAddress, pattern, v) >= 0) { return false; } } return true; } int audlib_getLoopStart(struct AudioLib *lib, int sourceAddress, int pattern) { for (int p = pattern; p >= 0; p--) { if (audlib_getLoop(lib, sourceAddress, p, 0) == 1) { return p; } } return 0; } int audlib_getLoop(struct AudioLib *lib, int sourceAddress, int pattern, int param) { int a = sourceAddress + PATTERNS_OFFSET + pattern * PATTERN_SIZE + param; return machine_peek(lib->core, a) >> 7; } int audlib_getTrack(struct AudioLib *lib, int sourceAddress, int pattern, int voice) { int a = sourceAddress + PATTERNS_OFFSET + pattern * PATTERN_SIZE + voice; int track = machine_peek(lib->core, a) & 0x7F; if (track == 0x40) { track = -1; } return track; } struct TrackRow audlib_getTrackRow(struct AudioLib *lib, int sourceAddress, int track, int row) { struct TrackRow trackRow; int a = sourceAddress + TRACKS_OFFSET + track * NUM_TRACK_ROWS * ROW_SIZE + row * ROW_SIZE; struct Core *core = lib->core; trackRow.note = machine_peek(core, a); int peek1 = machine_peek(core, a + 1); trackRow.sound = peek1 >> 4; trackRow.volume = peek1 & 0x0F; int peek2 = machine_peek(core, a + 2); trackRow.command = peek2 >> 4; trackRow.parameter = peek2 & 0x0F; return trackRow; } void audlib_playRow(struct AudioLib *lib, struct ComposerPlayer *player, int track, int voiceIndex) { struct Core *core = lib->core; struct Voice *voice = &core->machine->audioRegisters.voices[voiceIndex]; struct TrackRow trackRow = audlib_getTrackRow(lib, player->sourceAddress, track, player->row); if (trackRow.note > 0 && trackRow.note < 255) { audlib_copySound(lib, player->sourceAddress, trackRow.sound, voiceIndex); } if (trackRow.volume > 0) { voice->status.volume = trackRow.volume; } audlib_command(lib, voice, player, trackRow.command, trackRow.parameter); if (trackRow.note == 255) { voice->status.gate = 0; } else if (trackRow.note > 0) { audlib_setPitch(voice, trackRow.note); voice->status.init = 1; voice->status.gate = 1; } } void audlib_command(struct AudioLib *lib, struct Voice *voice, struct ComposerPlayer *player, int command, int parameter) { if (command == 0 && parameter == 0) return; switch (command) { case 0x00: voice->status.mix = parameter & 0x03; break; case 0x01: voice->envA = parameter; break; case 0x02: voice->envD = parameter; break; case 0x03: voice->envS = parameter; break; case 0x04: voice->envR = parameter; break; case 0x05: voice->lfoFrequency = parameter; break; case 0x06: voice->lfoOscAmount = parameter; break; case 0x07: voice->lfoVolAmount = parameter; break; case 0x08: voice->lfoPWAmount = parameter; break; case 0x09: voice->attr.pulseWidth = parameter; break; case 0x0D: player->speed = 0x10 | parameter; break; case 0x0E: player->speed = parameter; break; case 0x0F: switch (parameter) { case 0: player->willBreak = true; break; case 1: voice->status.gate = 0; voice->status.volume = 0; break; } break; } } ================================================ FILE: core/libraries/audio_lib.h ================================================ // // Copyright 2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef audio_lib_h #define audio_lib_h #include #include #include "audio_chip.h" #define NUM_SOUNDS 16 #define NUM_PATTERNS 64 #define NUM_TRACKS 64 #define NUM_TRACK_ROWS 32 struct Core; struct ComposerPlayer { int sourceAddress; int index; // pattern for music, otherwise track int speed; int tick; int row; bool willBreak; }; struct AudioLib { struct Core *core; int sourceAddress; struct ComposerPlayer musicPlayer; struct ComposerPlayer trackPlayers[NUM_VOICES]; }; void audlib_play(struct AudioLib *lib, int voiceIndex, float pitch, int len, int sound); void audlib_copySound(struct AudioLib *lib, int sourceAddress, int sound, int voiceIndex); void audlib_playMusic(struct AudioLib *lib, int startPattern); void audlib_playTrack(struct AudioLib *lib, int track, int voiceIndex); void audlib_stopAll(struct AudioLib *lib); void audlib_stopVoice(struct AudioLib *lib, int voiceIndex); void audlib_update(struct AudioLib *lib); #endif /* audio_lib_h */ ================================================ FILE: core/libraries/default_characters.c ================================================ // // Copyright 2016 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "default_characters.h" uint8_t DefaultCharacters[][16] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x18, 0x14, 0x04, 0x04, 0x0C, 0x10, 0x0C, 0x00, 0x00, 0x0C, 0x1C, 0x1C, 0x0C, 0x08, 0x0C}, {0x00, 0x48, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x24, 0x7E, 0x36, 0x12, 0x00, 0x00, 0x00}, {0x00, 0x24, 0x60, 0x1B, 0x12, 0x40, 0x1B, 0x12, 0x00, 0x00, 0x1E, 0x3F, 0x36, 0x3E, 0x3F, 0x12}, {0x00, 0x08, 0x30, 0x27, 0x10, 0x21, 0x17, 0x04, 0x00, 0x00, 0x0E, 0x1F, 0x1E, 0x1F, 0x1F, 0x04}, {0x00, 0x40, 0x11, 0x32, 0x04, 0x0C, 0x11, 0x23, 0x00, 0x22, 0x75, 0x3A, 0x14, 0x2A, 0x57, 0x23}, {0x00, 0x10, 0x2A, 0x02, 0x10, 0x13, 0x00, 0x1D, 0x00, 0x0C, 0x1E, 0x3A, 0x7E, 0x77, 0x3A, 0x1D}, {0x00, 0x10, 0x04, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x3C, 0x18, 0x00, 0x00, 0x00}, {0x00, 0x08, 0x16, 0x0C, 0x08, 0x00, 0x00, 0x06, 0x00, 0x04, 0x0E, 0x3C, 0x38, 0x18, 0x0C, 0x06}, {0x00, 0x30, 0x00, 0x00, 0x02, 0x06, 0x0C, 0x18, 0x00, 0x00, 0x18, 0x0C, 0x0E, 0x1E, 0x3C, 0x18}, {0x00, 0x00, 0x20, 0x12, 0x40, 0x27, 0x08, 0x12, 0x00, 0x00, 0x04, 0x0A, 0x3E, 0x3F, 0x2C, 0x12}, {0x00, 0x00, 0x18, 0x14, 0x40, 0x27, 0x04, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x3E, 0x3F, 0x1C, 0x0C}, {0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x3C, 0x18}, {0x00, 0x00, 0x00, 0x00, 0x60, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x3F, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x0C}, {0x00, 0x04, 0x0B, 0x06, 0x0C, 0x18, 0x30, 0x20, 0x00, 0x02, 0x07, 0x1E, 0x3C, 0x78, 0x70, 0x20}, {0x00, 0x20, 0x58, 0x11, 0x01, 0x19, 0x03, 0x1E, 0x00, 0x1C, 0x3E, 0x7F, 0x77, 0x7F, 0x3F, 0x1E}, {0x00, 0x10, 0x24, 0x04, 0x04, 0x04, 0x40, 0x3F, 0x00, 0x08, 0x1C, 0x1C, 0x1C, 0x1C, 0x3E, 0x3F}, {0x00, 0x20, 0x58, 0x33, 0x06, 0x0C, 0x00, 0x3F, 0x00, 0x1C, 0x3E, 0x3F, 0x1E, 0x3C, 0x7E, 0x3F}, {0x00, 0x20, 0x58, 0x33, 0x00, 0x41, 0x03, 0x1E, 0x00, 0x1C, 0x3E, 0x3F, 0x06, 0x27, 0x3F, 0x1E}, {0x00, 0x66, 0x55, 0x01, 0x39, 0x01, 0x01, 0x03, 0x00, 0x00, 0x33, 0x7F, 0x3F, 0x07, 0x07, 0x03}, {0x00, 0x60, 0x5F, 0x00, 0x38, 0x01, 0x43, 0x3E, 0x00, 0x1E, 0x3F, 0x7C, 0x3E, 0x07, 0x3F, 0x3E}, {0x00, 0x10, 0x2E, 0x00, 0x18, 0x11, 0x03, 0x1E, 0x00, 0x0C, 0x1E, 0x7C, 0x7E, 0x77, 0x3F, 0x1E}, {0x00, 0x60, 0x39, 0x03, 0x06, 0x0C, 0x08, 0x18, 0x00, 0x1E, 0x3F, 0x0F, 0x1E, 0x3C, 0x38, 0x18}, {0x00, 0x20, 0x58, 0x03, 0x18, 0x11, 0x03, 0x1E, 0x00, 0x1C, 0x3E, 0x3F, 0x7E, 0x77, 0x3F, 0x1E}, {0x00, 0x20, 0x58, 0x01, 0x19, 0x41, 0x03, 0x1E, 0x00, 0x1C, 0x3E, 0x3F, 0x1F, 0x27, 0x3F, 0x1E}, {0x00, 0x00, 0x00, 0x10, 0x0C, 0x10, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0C, 0x08, 0x0C, 0x00}, {0x00, 0x00, 0x00, 0x10, 0x0C, 0x10, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x08, 0x0C, 0x08, 0x3C, 0x18}, {0x00, 0x00, 0x08, 0x16, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x04, 0x0E, 0x3C, 0x18, 0x0C, 0x06}, {0x00, 0x00, 0x00, 0x60, 0x3F, 0x40, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x3F, 0x3E, 0x3F, 0x00}, {0x00, 0x00, 0x30, 0x00, 0x00, 0x06, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x18, 0x0C, 0x1E, 0x3C, 0x18}, {0x00, 0x20, 0x58, 0x33, 0x06, 0x0C, 0x10, 0x0C, 0x00, 0x1C, 0x3E, 0x3F, 0x1E, 0x0C, 0x08, 0x0C}, {0x00, 0x20, 0x58, 0x19, 0x11, 0x17, 0x00, 0x1E, 0x00, 0x1C, 0x3E, 0x77, 0x7F, 0x77, 0x3C, 0x1E}, {0x00, 0x10, 0x20, 0x18, 0x01, 0x19, 0x11, 0x33, 0x00, 0x08, 0x1C, 0x7E, 0x7F, 0x7F, 0x77, 0x33}, {0x00, 0x60, 0x58, 0x03, 0x18, 0x11, 0x03, 0x3E, 0x00, 0x1C, 0x3E, 0x7F, 0x7E, 0x77, 0x7F, 0x3E}, {0x00, 0x20, 0x58, 0x13, 0x10, 0x10, 0x03, 0x1E, 0x00, 0x1C, 0x3E, 0x73, 0x70, 0x76, 0x3F, 0x1E}, {0x00, 0x60, 0x50, 0x10, 0x11, 0x13, 0x06, 0x3C, 0x00, 0x18, 0x3C, 0x76, 0x77, 0x7F, 0x7E, 0x3C}, {0x00, 0x60, 0x5F, 0x00, 0x1C, 0x10, 0x00, 0x3F, 0x00, 0x1E, 0x3F, 0x78, 0x7C, 0x70, 0x7E, 0x3F}, {0x00, 0x60, 0x5F, 0x00, 0x1C, 0x10, 0x10, 0x30, 0x00, 0x1E, 0x3F, 0x78, 0x7C, 0x70, 0x70, 0x30}, {0x00, 0x20, 0x5E, 0x1C, 0x11, 0x11, 0x03, 0x1E, 0x00, 0x1C, 0x3E, 0x72, 0x77, 0x77, 0x3F, 0x1E}, {0x00, 0x66, 0x55, 0x01, 0x19, 0x11, 0x11, 0x33, 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x77, 0x77, 0x33}, {0x00, 0x30, 0x06, 0x04, 0x04, 0x04, 0x00, 0x1E, 0x00, 0x0C, 0x1E, 0x1C, 0x1C, 0x1C, 0x3C, 0x1E}, {0x00, 0x1C, 0x09, 0x01, 0x01, 0x41, 0x03, 0x1E, 0x00, 0x02, 0x0F, 0x07, 0x07, 0x27, 0x3F, 0x1E}, {0x00, 0x64, 0x5B, 0x06, 0x04, 0x10, 0x10, 0x33, 0x00, 0x02, 0x37, 0x7E, 0x7C, 0x7C, 0x76, 0x33}, {0x00, 0x60, 0x50, 0x10, 0x10, 0x10, 0x00, 0x3F, 0x00, 0x00, 0x30, 0x70, 0x70, 0x70, 0x7E, 0x3F}, {0x00, 0x42, 0x45, 0x09, 0x01, 0x19, 0x11, 0x33, 0x00, 0x00, 0x23, 0x77, 0x7F, 0x7F, 0x77, 0x33}, {0x00, 0x66, 0x45, 0x01, 0x11, 0x11, 0x11, 0x33, 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x77, 0x77, 0x33}, {0x00, 0x20, 0x58, 0x11, 0x11, 0x11, 0x03, 0x1E, 0x00, 0x1C, 0x3E, 0x77, 0x77, 0x77, 0x3F, 0x1E}, {0x00, 0x60, 0x58, 0x03, 0x1E, 0x10, 0x10, 0x30, 0x00, 0x1C, 0x3E, 0x7F, 0x7E, 0x70, 0x70, 0x30}, {0x00, 0x20, 0x58, 0x11, 0x11, 0x11, 0x00, 0x1F, 0x00, 0x1C, 0x3E, 0x77, 0x7B, 0x7D, 0x3E, 0x1F}, {0x00, 0x60, 0x58, 0x03, 0x06, 0x10, 0x10, 0x33, 0x00, 0x1C, 0x3E, 0x7F, 0x7E, 0x7C, 0x76, 0x33}, {0x00, 0x20, 0x5F, 0x00, 0x18, 0x01, 0x03, 0x3E, 0x00, 0x1E, 0x3F, 0x3C, 0x1E, 0x07, 0x7F, 0x3E}, {0x00, 0x70, 0x27, 0x04, 0x04, 0x04, 0x04, 0x0C, 0x00, 0x0E, 0x3F, 0x1C, 0x1C, 0x1C, 0x1C, 0x0C}, {0x00, 0x66, 0x55, 0x11, 0x11, 0x11, 0x03, 0x1E, 0x00, 0x00, 0x33, 0x77, 0x77, 0x77, 0x3F, 0x1E}, {0x00, 0x66, 0x55, 0x11, 0x11, 0x03, 0x06, 0x0C, 0x00, 0x00, 0x33, 0x77, 0x77, 0x3F, 0x1E, 0x0C}, {0x00, 0x66, 0x55, 0x15, 0x01, 0x19, 0x31, 0x21, 0x00, 0x00, 0x33, 0x6B, 0x7F, 0x7F, 0x73, 0x21}, {0x00, 0x64, 0x0B, 0x06, 0x00, 0x18, 0x11, 0x33, 0x00, 0x02, 0x37, 0x1E, 0x3C, 0x7E, 0x77, 0x33}, {0x00, 0x66, 0x55, 0x0B, 0x06, 0x04, 0x04, 0x0C, 0x00, 0x00, 0x33, 0x37, 0x1E, 0x1C, 0x1C, 0x0C}, {0x00, 0x60, 0x33, 0x06, 0x0C, 0x58, 0x00, 0x3F, 0x00, 0x1E, 0x3F, 0x1E, 0x3C, 0x38, 0x7E, 0x3F}, {0x00, 0x30, 0x2E, 0x08, 0x08, 0x08, 0x00, 0x1E, 0x00, 0x0C, 0x1E, 0x38, 0x38, 0x38, 0x3C, 0x1E}, {0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01}, {0x00, 0x38, 0x12, 0x02, 0x02, 0x02, 0x02, 0x1E, 0x00, 0x04, 0x1E, 0x0E, 0x0E, 0x0E, 0x3E, 0x1E}, {0x00, 0x10, 0x20, 0x18, 0x33, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x7E, 0x33, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x3F}, }; ================================================ FILE: core/libraries/default_characters.h ================================================ // // Copyright 2016 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef default_characters_h #define default_characters_h #include #include extern uint8_t DefaultCharacters[][16]; #endif /* default_characters_h */ ================================================ FILE: core/libraries/sprites_lib.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "sprites_lib.h" #include "core.h" #include bool sprlib_isSpriteOnScreen(struct Sprite *sprite) { int size = (sprite->attr.size + 1) << 3; return ( sprite->x < SCREEN_WIDTH + SPRITE_OFFSET_X && sprite->y < SCREEN_HEIGHT + SPRITE_OFFSET_Y && sprite->x + size > SPRITE_OFFSET_X && sprite->y + size > SPRITE_OFFSET_Y); } bool sprlib_checkSingleCollision(struct SpritesLib *lib, struct Sprite *sprite, struct Sprite *otherSprite) { if (sprlib_isSpriteOnScreen(otherSprite)) { int ax1 = sprite->x; int ay1 = sprite->y; int ax2 = otherSprite->x; int ay2 = otherSprite->y; int s1 = (sprite->attr.size + 1) << 3; int s2 = (otherSprite->attr.size + 1) << 3; int bx1 = ax1 + s1; int by1 = ay1 + s1; int bx2 = ax2 + s2; int by2 = ay2 + s2; // rectangle check if (bx1 > ax2 && by1 > ay2 && ax1 < bx2 && ay1 < by2) { // pixel exact check int diffX = ax2 - ax1; int diffY = ay2 - ay1; struct Character *characters = lib->core->machine->videoRam.characters; int c1 = sprite->character; int c2 = otherSprite->character; for (int line = 0; line < s1; line++) { if (line - diffY >= 0 && line - diffY < s2) { int line1 = sprite->attr.flipY ? (s1 - line - 1) : line; int line2 = otherSprite->attr.flipY ? (s2 - (line - diffY) - 1) : (line - diffY); bool flx1 = sprite->attr.flipX; bool flx2 = otherSprite->attr.flipX; uint32_t source1 = 0; int chLine1 = line1 & 7; int rc1 = c1 + (line1 >> 3 << 4); for (int i = 0; i <= sprite->attr.size; i++) { uint8_t *data = characters[flx1 ? (rc1 + sprite->attr.size - i) : (rc1 + i)].data; uint32_t val = (data[chLine1] | data[chLine1 + 8]); if (flx1) { // reverse bits val = (((val * 0x0802LU & 0x22110LU) | (val * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16) & 0xFF; } source1 |= val << (24 - (i << 3)); } uint32_t source2 = 0; int chLine2 = line2 & 7; int rc2 = c2 + (line2 >> 3 << 4); for (int i = 0; i <= otherSprite->attr.size; i++) { uint8_t *data = characters[flx2 ? (rc2 + otherSprite->attr.size - i) : (rc2 + i)].data; uint32_t val = (data[chLine2] | data[chLine2 + 8]); if (flx2) { // reverse bits val = (((val * 0x0802LU & 0x22110LU) | (val * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16) & 0xFF; } int shift = (24 - (i << 3) - diffX); if (shift >= 0 && shift < 32) { source2 |= val << shift; } else if (shift > -32 && shift < 0) { source2 |= val >> -shift; } } if (source1 & source2) { return true; } } } } } return false; } bool sprlib_checkCollision(struct SpritesLib *lib, int checkIndex, int firstIndex, int lastIndex) { struct Sprite *sprites = lib->core->machine->spriteRegisters.sprites; struct Sprite *sprite = &sprites[checkIndex]; if (sprlib_isSpriteOnScreen(sprite)) { for (int i = firstIndex; i <= lastIndex; i++) { if (i != checkIndex) { if (sprlib_checkSingleCollision(lib, sprite, &sprites[i])) { lib->lastHit = i; return true; } } } } return false; } ================================================ FILE: core/libraries/sprites_lib.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef sprites_lib_h #define sprites_lib_h #include #include struct Core; struct Sprite; struct SpritesLib { struct Core *core; int lastHit; }; bool sprlib_isSpriteOnScreen(struct Sprite *sprite); bool sprlib_checkCollision(struct SpritesLib *lib, int checkIndex, int firstIndex, int lastIndex); #endif /* sprites_lib_h */ ================================================ FILE: core/libraries/startup_sequence.c ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "startup_sequence.h" #include "core.h" #include #include #define FONT_CHAR_OFFSET 192 void runStartupSequence(struct Core *core) { struct DataEntry *entries = core->interpreter->romDataManager.entries; // init font and window struct TextLib *textLib = &core->interpreter->textLib; textLib->fontCharOffset = FONT_CHAR_OFFSET; txtlib_clearScreen(textLib); // default characters/font if (strcmp(entries[0].comment, "FONT") == 0) { memcpy(&core->machine->videoRam.characters[FONT_CHAR_OFFSET], &core->machine->cartridgeRom[entries[0].start], entries[0].length); } // default palettes uint8_t *colors = core->machine->colorRegisters.colors; colors[0] = (0 << 4) | (1 << 2) | 1; colors[1] = (3 << 4) | (3 << 2) | 3; colors[2] = (2 << 4) | (3 << 2) | 3; colors[3] = (0 << 4) | (0 << 2) | 0; colors[4] = 0; colors[5] = (3 << 4) | (2 << 2) | 0; colors[6] = (3 << 4) | (1 << 2) | 0; colors[7] = (0 << 4) | (0 << 2) | 0; colors[8] = 0; colors[9] = (3 << 4) | (3 << 2) | 0; colors[10] = (0 << 4) | (3 << 2) | 0; colors[11] = (0 << 4) | (0 << 2) | 0; colors[12] = 0; colors[13] = (3 << 4) | (3 << 2) | 3; colors[14] = (3 << 4) | (3 << 2) | 0; colors[15] = (0 << 4) | (0 << 2) | 0; for (int i = 0; i < 16; i += 4) { colors[16 + i] = 0; colors[17 + i] = (3 << 4) | (3 << 2) | 3; colors[18 + i] = (2 << 4) | (2 << 2) | 2; colors[19 + i] = (1 << 4) | (1 << 2) | 1; } // main palettes int palLen = entries[1].length; if (palLen > 32) palLen = 32; memcpy(core->machine->colorRegisters.colors, &core->machine->cartridgeRom[entries[1].start], palLen); // main characters memcpy(core->machine->videoRam.characters, &core->machine->cartridgeRom[entries[2].start], entries[2].length); // main background source int bgStart = entries[3].start; core->interpreter->textLib.sourceAddress = bgStart + 4; core->interpreter->textLib.sourceWidth = core->machine->cartridgeRom[bgStart + 2]; core->interpreter->textLib.sourceHeight = core->machine->cartridgeRom[bgStart + 3]; // voices for (int i = 0; i < NUM_VOICES; i++) { struct Voice *voice = &core->machine->audioRegisters.voices[i]; voice->attr.pulseWidth = 8; voice->status.volume = 15; voice->status.mix = 3; voice->envS = 15; } // main sound source core->interpreter->audioLib.sourceAddress = entries[15].start; } ================================================ FILE: core/libraries/startup_sequence.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef startup_sequence_h #define startup_sequence_h #include struct Core; void runStartupSequence(struct Core *core); #endif /* startup_sequence_h */ ================================================ FILE: core/libraries/text_lib.c ================================================ // // Copyright 2016 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "text_lib.h" #include "core.h" #include #include struct Plane *txtlib_getBackground(struct TextLib *lib, int bg) { switch (bg) { case 0: return &lib->core->machine->videoRam.planeA; case 1: return &lib->core->machine->videoRam.planeB; case OVERLAY_BG: return &lib->core->overlay->plane; default: assert(0); return NULL; } } void txtlib_setCellAt(struct Plane *plane, int x, int y, int character, union CharacterAttributes attr) { struct Cell *cell = &plane->cells[y & 0x1F][x & 0x1F]; if (character >= 0) { cell->character = character; } cell->attr = attr; } void txtlib_scrollRow(struct Plane *plane, int fromX, int toX, int y, int deltaX, int deltaY) { if (deltaX > 0) { for (int x = toX; x > fromX; x--) { plane->cells[y][x] = plane->cells[(y - deltaY) & 0x1F][(x - deltaX) & 0x1F]; } } else if (deltaX < 0) { for (int x = fromX; x < toX; x++) { plane->cells[y][x] = plane->cells[(y - deltaY) & 0x1F][(x - deltaX) & 0x1F]; } } else { for (int x = fromX; x <= toX; x++) { plane->cells[y][x] = plane->cells[(y - deltaY) & 0x1F][(x - deltaX) & 0x1F]; } } } void txtlib_scroll(struct Plane *plane, int fromX, int fromY, int toX, int toY, int deltaX, int deltaY) { if (deltaY > 0) { for (int y = toY; y > fromY; y--) { txtlib_scrollRow(plane, fromX, toX, y, deltaX, deltaY); } } else if (deltaY < 0) { for (int y = fromY; y < toY; y++) { txtlib_scrollRow(plane, fromX, toX, y, deltaX, deltaY); } } else { for (int y = fromY; y <= toY; y++) { txtlib_scrollRow(plane, fromX, toX, y, deltaX, deltaY); } } } void txtlib_scrollWindowIfNeeded(struct TextLib *lib) { struct Plane *plane = txtlib_getBackground(lib, lib->windowBg); if (lib->cursorY >= lib->windowHeight) { // scroll txtlib_scroll(plane, lib->windowX, lib->windowY, lib->windowX + lib->windowWidth - 1, lib->windowY + lib->windowHeight - 1, 0, -1); // clear bottom line int py = lib->windowY + lib->windowHeight - 1; for (int x = 0; x < lib->windowWidth; x++) { int px = x + lib->windowX; txtlib_setCellAt(plane, px, py, lib->fontCharOffset, lib->charAttr); // space } lib->cursorY = lib->windowHeight - 1; struct Interpreter *interpreter = lib->core->interpreter; if (interpreter->state == StateEvaluate && lib->windowBg != OVERLAY_BG) { interpreter->waitCount = 1; interpreter->exitEvaluation = true; interpreter->cycles += lib->windowWidth * lib->windowHeight * 2; } } } void txtlib_printText(struct TextLib *lib, const char *text) { struct Plane *plane = txtlib_getBackground(lib, lib->windowBg); const char *letter = text; while (*letter) { txtlib_scrollWindowIfNeeded(lib); if (*letter >= 32) { char printableLetter = *letter; if (printableLetter >= 'a' && printableLetter <= 'z') { printableLetter -= 32; } txtlib_setCellAt(plane, lib->cursorX + lib->windowX, lib->cursorY + lib->windowY, lib->fontCharOffset + (printableLetter - 32), lib->charAttr); if (lib->windowBg != OVERLAY_BG) { lib->core->interpreter->cycles += 2; } lib->cursorX++; } else if (*letter == '\n') { lib->cursorX = 0; lib->cursorY++; } if (lib->cursorX >= lib->windowWidth) { lib->cursorX = 0; lib->cursorY++; } letter++; } } bool txtlib_deleteBackward(struct TextLib *lib) { struct Plane *plane = txtlib_getBackground(lib, lib->windowBg); // clear cursor txtlib_setCellAt(plane, lib->cursorX + lib->windowX, lib->cursorY + lib->windowY, lib->fontCharOffset, lib->charAttr); // move back cursor if (lib->cursorX > 0) { lib->cursorX--; } else if (lib->cursorY > 0) { lib->cursorX = lib->windowX + lib->windowWidth - 1; lib->cursorY--; } else { return false; } // clear cell txtlib_setCellAt(plane, lib->cursorX + lib->windowX, lib->cursorY + lib->windowY, lib->fontCharOffset, lib->charAttr); lib->core->interpreter->cycles += 4; return true; } void txtlib_writeText(struct TextLib *lib, const char *text, int x, int y) { struct Plane *plane = txtlib_getBackground(lib, lib->bg); const char *letter = text; while (*letter) { if (*letter >= 32) { char printableLetter = *letter; if (printableLetter >= 'a' && printableLetter <= 'z') { printableLetter -= 32; } txtlib_setCellAt(plane, x, y, lib->fontCharOffset + (printableLetter - 32), lib->charAttr); if (lib->windowBg != OVERLAY_BG) { lib->core->interpreter->cycles += 2; } x++; } letter++; } } void txtlib_writeNumber(struct TextLib *lib, int number, int digits, int x, int y) { struct Plane *plane = txtlib_getBackground(lib, lib->bg); if (number < 0) { // negative number number *= -1; txtlib_setCellAt(plane, x, y, lib->fontCharOffset + 13, lib->charAttr); // "-" x += digits; digits--; } else { x += digits; } int div = 1; for (int i = 0; i < digits; i++) { x--; txtlib_setCellAt(plane, x, y, lib->fontCharOffset + ((number / div) % 10 + 16), lib->charAttr); div *= 10; } if (lib->windowBg != OVERLAY_BG) { lib->core->interpreter->cycles += digits * 2; } } void txtlib_inputBegin(struct TextLib *lib) { lib->inputBuffer[0] = 0; lib->inputLength = 0; lib->blink = 0; lib->core->machine->ioRegisters.key = 0; lib->core->machine->ioRegisters.attr.keyboardEnabled = 1; lib->core->interpreter->isKeyboardOptional = false; delegate_controlsDidChange(lib->core); txtlib_scrollWindowIfNeeded(lib); } bool txtlib_inputUpdate(struct TextLib *lib) { struct Plane *plane = txtlib_getBackground(lib, lib->windowBg); char key = lib->core->machine->ioRegisters.key; bool done = false; if (key) { if (key == CoreInputKeyBackspace) { if (lib->inputLength > 0) { if (txtlib_deleteBackward(lib)) { lib->inputBuffer[--lib->inputLength] = 0; } } } else if (key == CoreInputKeyReturn) { // clear cursor txtlib_setCellAt(plane, lib->cursorX + lib->windowX, lib->cursorY + lib->windowY, lib->fontCharOffset, lib->charAttr); txtlib_printText(lib, "\n"); done = true; } else if (key >= 32) { if (lib->inputLength < INPUT_BUFFER_SIZE - 2) { char text[2] = {key, 0}; txtlib_printText(lib, text); lib->inputBuffer[lib->inputLength++] = key; lib->inputBuffer[lib->inputLength] = 0; txtlib_scrollWindowIfNeeded(lib); } } lib->blink = 0; lib->core->machine->ioRegisters.key = 0; } if (!done) { txtlib_setCellAt(plane, lib->cursorX + lib->windowX, lib->cursorY + lib->windowY, lib->fontCharOffset + (lib->blink++ < 30 ? 63 : 0), lib->charAttr); if (lib->blink == 60) { lib->blink = 0; } } return done; } void txtlib_clearWindow(struct TextLib *lib) { struct Plane *plane = txtlib_getBackground(lib, lib->windowBg); lib->cursorX = 0; lib->cursorY = 0; for (int y = 0; y < lib->windowHeight; y++) { int py = y + lib->windowY; for (int x = 0; x < lib->windowWidth; x++) { int px = x + lib->windowX; txtlib_setCellAt(plane, px, py, lib->fontCharOffset, lib->charAttr); } } lib->core->interpreter->cycles += lib->windowWidth * lib->windowHeight * 2; } void txtlib_clearScreen(struct TextLib *lib) { struct VideoRegisters *reg = &lib->core->machine->videoRegisters; memset(&lib->core->machine->videoRam.planeA, 0, sizeof(struct Plane)); memset(&lib->core->machine->videoRam.planeB, 0, sizeof(struct Plane)); reg->scrollAX = 0; reg->scrollAY = 0; reg->scrollBX = 0; reg->scrollBY = 0; reg->attr.spritesEnabled = 1; reg->attr.planeAEnabled = 1; reg->attr.planeBEnabled = 1; lib->windowX = 0; lib->windowY = 0; lib->windowWidth = 20; lib->windowHeight = 16; lib->cursorX = 0; lib->cursorY = 0; lib->bg = 0; lib->core->interpreter->cycles += PLANE_COLUMNS * PLANE_ROWS * 2 * 2; } void txtlib_clearBackground(struct TextLib *lib, int bg) { struct Plane *plane = txtlib_getBackground(lib, bg); memset(plane, 0, sizeof(struct Plane)); lib->core->interpreter->cycles += PLANE_COLUMNS * PLANE_ROWS * 2; } struct Cell *txtlib_getCell(struct TextLib *lib, int x, int y) { struct Plane *plane = txtlib_getBackground(lib, lib->bg); return &plane->cells[y & 0x1F][x & 0x1F]; } void txtlib_setCell(struct TextLib *lib, int x, int y, int character) { struct Plane *plane = txtlib_getBackground(lib, lib->bg); txtlib_setCellAt(plane, x, y, character, lib->charAttr); } void txtlib_setCells(struct TextLib *lib, int fromX, int fromY, int toX, int toY, int character) { struct Plane *plane = txtlib_getBackground(lib, lib->bg); for (int y = fromY; y <= toY; y++) { for (int x = fromX; x <= toX; x++) { txtlib_setCellAt(plane, x, y, character, lib->charAttr); } } lib->core->interpreter->cycles += (toX - fromX + 1) * (toY - fromY + 1) * 2; } void txtlib_setCellsAttr(struct TextLib *lib, int fromX, int fromY, int toX, int toY, int pal, int flipX, int flipY, int prio) { struct Plane *plane = txtlib_getBackground(lib, lib->bg); for (int y = fromY; y <= toY; y++) { for (int x = fromX; x <= toX; x++) { struct Cell *cell = &plane->cells[y & 0x1F][x & 0x1F]; if (pal >= 0) cell->attr.palette = pal; if (flipX >= 0) cell->attr.flipX = flipX; if (flipY >= 0) cell->attr.flipY = flipY; if (prio >= 0) cell->attr.priority = prio; } } lib->core->interpreter->cycles += (toX - fromX + 1) * (toY - fromY + 1) * 2; } void txtlib_scrollBackground(struct TextLib *lib, int fromX, int fromY, int toX, int toY, int deltaX, int deltaY) { struct Plane *plane = txtlib_getBackground(lib, lib->bg); txtlib_scroll(plane, fromX, fromY, toX, toY, deltaX, deltaY); lib->core->interpreter->cycles += (toX - fromX + 1) * (toY - fromY + 1) * 2; } void txtlib_copyBackground(struct TextLib *lib, int srcX, int srcY, int width, int height, int dstX, int dstY) { struct Plane *plane = txtlib_getBackground(lib, lib->bg); for (int y = 0; y < height; y++) { int py = dstY + y; int addr = lib->sourceAddress + ((srcY + y) * lib->sourceWidth + srcX) * 2; for (int x = 0; x < width; x++) { int px = dstX + x; struct Cell *cell = &plane->cells[py & 0x1F][px & 0x1F]; cell->character = machine_peek(lib->core, addr++); cell->attr.value = machine_peek(lib->core, addr++); } } lib->core->interpreter->cycles += width * height * 2; } int txtlib_getSourceCell(struct TextLib *lib, int x, int y, bool getAttrs) { if (x >= 0 && y >= 0 && x < lib->sourceWidth && y < lib->sourceHeight) { int address = lib->sourceAddress + ((y * lib->sourceWidth) + x) * 2; if (getAttrs) { return machine_peek(lib->core, address + 1); } else { return machine_peek(lib->core, address); } } return -1; } bool txtlib_setSourceCell(struct TextLib *lib, int x, int y, int character) { int address = lib->sourceAddress + ((y * lib->sourceWidth) + x) * 2; // only working RAM is allowed if (address < 0xA000 || address + 1 >= 0xE000) { return false; } if (character >= 0) { machine_poke(lib->core, address, character); } machine_poke(lib->core, address + 1, lib->charAttr.value); return true; } void txtlib_itobin(char *buffer, size_t buffersize, size_t width, int value) { if (width < 1) { width = 1; } unsigned int mask = 1 << 15; int p = 0; bool active = false; while (mask && p < buffersize - 1) { if (active || (value & mask) || mask < (1 << width)) { buffer[p++] = (value & mask) ? '1' : '0'; active = true; } mask = (mask >> 1) & 0x7FFF; } buffer[p] = 0; } ================================================ FILE: core/libraries/text_lib.h ================================================ // // Copyright 2016-2019 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef text_lib_h #define text_lib_h #include #include #include #include "video_chip.h" #define INPUT_BUFFER_SIZE 256 #define OVERLAY_BG 2 struct Core; struct TextLib { struct Core *core; union CharacterAttributes charAttr; int fontCharOffset; int windowX; int windowY; int windowWidth; int windowHeight; int windowBg; int cursorX; int cursorY; int bg; int sourceAddress; int sourceWidth; int sourceHeight; char inputBuffer[INPUT_BUFFER_SIZE]; int inputLength; int blink; }; void txtlib_printText(struct TextLib *lib, const char *text); bool txtlib_deleteBackward(struct TextLib *lib); void txtlib_writeText(struct TextLib *lib, const char *text, int x, int y); void txtlib_writeNumber(struct TextLib *lib, int number, int digits, int x, int y); void txtlib_inputBegin(struct TextLib *lib); bool txtlib_inputUpdate(struct TextLib *lib); void txtlib_clearWindow(struct TextLib *lib); void txtlib_clearScreen(struct TextLib *lib); void txtlib_clearBackground(struct TextLib *lib, int bg); struct Cell *txtlib_getCell(struct TextLib *lib, int x, int y); void txtlib_setCell(struct TextLib *lib, int x, int y, int character); void txtlib_setCells(struct TextLib *lib, int fromX, int fromY, int toX, int toY, int character); void txtlib_setCellsAttr(struct TextLib *lib, int fromX, int fromY, int toX, int toY, int pal, int flipX, int flipY, int prio); void txtlib_scrollBackground(struct TextLib *lib, int fromX, int fromY, int toX, int toY, int deltaX, int deltaY); void txtlib_copyBackground(struct TextLib *lib, int srcX, int srcY, int width, int height, int dstX, int dstY); int txtlib_getSourceCell(struct TextLib *lib, int x, int y, bool getAttrs); bool txtlib_setSourceCell(struct TextLib *lib, int x, int y, int character); void txtlib_itobin(char *buffer, size_t buffersize, size_t width, int value); #endif /* text_lib_h */ ================================================ FILE: core/machine/audio_chip.c ================================================ // // Copyright 2016-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "audio_chip.h" #include "core.h" #include #include const double envRates[16] = { 256.0 / 0.002, 256.0 / 0.03, 256.0 / 0.06, 256.0 / 0.09, 256.0 / 0.14, 256.0 / 0.21, 256.0 / 0.31, 256.0 / 0.47, 256.0 / 0.70, 256.0 / 1.0, 256.0 / 1.6, 256.0 / 2.4, 256.0 / 3.5, 256.0 / 5.0, 256.0 / 8.0, 256.0 / 12.0 }; const double lfoRates[16] = { 0.12 * 256.0, 0.16 * 256.0, 0.23 * 256.0, 0.32 * 256.0, 0.44 * 256.0, 0.62 * 256.0, 0.87 * 256.0, 1.2 * 256.0, 1.7 * 256.0, 2.4 * 256.0, 3.3 * 256.0, 4.7 * 256.0, 6.6 * 256.0, 9.2 * 256.0, 12.9 * 256.0, 18.0 * 256.0 }; const int lfoAmounts[16] = { 0, 1, 2, 4, 6, 9, 12, 17, 24, 34, 48, 67, 93, 131, 183, 256 }; void audio_renderAudioBuffer(struct AudioRegisters *lifeRegisters, struct AudioRegisters *registers, struct AudioInternals *internals, int16_t *stereoOutput, int numSamples, int outputFrequency, int volume); void audio_reset(struct Core *core) { struct AudioInternals *internals = &core->machineInternals->audioInternals; for (int i = 0; i < NUM_VOICES; i++) { struct VoiceInternals *voiceIn = &internals->voices[i]; voiceIn->noiseRandom = 0xABCD; voiceIn->lfoRandom = 0xABCD; } internals->writeBufferIndex = -1; } void audio_bufferRegisters(struct Core *core) { struct AudioRegisters *registers = &core->machine->audioRegisters; struct AudioInternals *internals = &core->machineInternals->audioInternals; // next buffer int writeBufferIndex = internals->writeBufferIndex; if (writeBufferIndex >= 0) { writeBufferIndex = (writeBufferIndex + 1) % NUM_AUDIO_BUFFERS; } else { writeBufferIndex = NUM_AUDIO_BUFFERS / 2; } // copy registers to buffer memcpy(&internals->buffers[writeBufferIndex], registers, sizeof(struct AudioRegisters)); // reset "init" flags for (int v = 0; v < NUM_VOICES; v++) { struct Voice *voice = ®isters->voices[v]; voice->status.init = 0; } internals->writeBufferIndex = writeBufferIndex; } void audio_renderAudio(struct Core *core, int16_t *stereoOutput, int numSamples, int outputFrequency, int volume) { struct AudioInternals *internals = &core->machineInternals->audioInternals; struct AudioRegisters *lifeRegisters = &core->machine->audioRegisters; int numSamplesPerUpdate = outputFrequency / 60 * NUM_CHANNELS; int offset = 0; while (offset < numSamples) { if (offset + numSamplesPerUpdate > numSamples) { numSamplesPerUpdate = numSamples - offset; } int readBufferIndex = internals->readBufferIndex; audio_renderAudioBuffer(lifeRegisters, &internals->buffers[readBufferIndex], internals, &stereoOutput[offset], numSamplesPerUpdate, outputFrequency, volume); if (internals->writeBufferIndex != -1 && internals->writeBufferIndex != readBufferIndex) { internals->readBufferIndex = (readBufferIndex + 1) % NUM_AUDIO_BUFFERS; } offset += numSamplesPerUpdate; } } void audio_renderAudioBuffer(struct AudioRegisters *lifeRegisters, struct AudioRegisters *registers, struct AudioInternals *internals, int16_t *stereoOutput, int numSamples, int outputFrequency, int volume) { double overflow = 0xFFFFFF; for (int v = 0; v < NUM_VOICES; v++) { struct Voice *voice = ®isters->voices[v]; if (voice->status.init) { voice->status.init = 0; struct VoiceInternals *voiceIn = &internals->voices[v]; voiceIn->envState = EnvStateAttack; voiceIn->lfoHold = false; voiceIn->timeoutCounter = voice->length; if (voice->lfoAttr.envMode || voice->lfoAttr.trigger) { voiceIn->lfoAccumulator = 0.0; } } } int i = 0; while (i < numSamples) { int16_t leftOutput = 0; int16_t rightOutput = 0; if (internals->audioEnabled) { for (int v = 0; v < NUM_VOICES; v++) { struct Voice *voice = ®isters->voices[v]; struct VoiceInternals *voiceIn = &internals->voices[v]; int freq = (voice->frequencyHigh << 8) | voice->frequencyLow; if (freq == 0) continue; int volume = voice->status.volume << 4; int pulseWidth = voice->attr.pulseWidth << 4; // --- LFO --- uint8_t lfoAccu8Last = voiceIn->lfoAccumulator; if (!voiceIn->lfoHold) { double lfoRate = lfoRates[voice->lfoFrequency]; double lfoAccumulator = voiceIn->lfoAccumulator + lfoRate / (double)outputFrequency; if (voice->lfoAttr.envMode && lfoAccumulator >= 255.0) { lfoAccumulator = 255.0; voiceIn->lfoHold = true; } else if (lfoAccumulator >= 256.0) { // avoid overflow and loss of precision lfoAccumulator -= 256.0; } voiceIn->lfoAccumulator = lfoAccumulator; } uint8_t lfoAccu8 = voiceIn->lfoAccumulator; uint8_t lfoSample = 0; enum LFOWaveType lfoWaveType = voice->lfoAttr.wave; switch (lfoWaveType) { case LFOWaveTypeTriangle: { lfoSample = ((lfoAccu8 & 0x80) ? ~(lfoAccu8 << 1) : (lfoAccu8 << 1)); break; } case LFOWaveTypeSawtooth: { lfoSample = ~lfoAccu8; break; } case LFOWaveTypeSquare: { lfoSample = (lfoAccu8 & 0x80) ? 0x00 : 0xFF; break; } case LFOWaveTypeRandom: { if ((lfoAccu8 & 0x80) != (lfoAccu8Last & 0x80)) { uint16_t r = voiceIn->lfoRandom; uint16_t bit = ((r >> 0) ^ (r >> 2) ^ (r >> 3) ^ (r >> 5) ) & 1; voiceIn->lfoRandom = (r >> 1) | (bit << 15); } lfoSample = voiceIn->lfoRandom & 0xFF; break; } } int freqAmount = lfoAmounts[voice->lfoOscAmount]; int volAmount = voice->lfoVolAmount; int pwAmount = voice->lfoPWAmount; int freqMod = freq * lfoSample * freqAmount >> 16; if (voice->lfoAttr.invert) freq -= freqMod; else freq += freqMod; if (freq < 1) freq = 1; if (freq > 65535) freq = 65535; if (voice->lfoAttr.invert) { volume -= volume * lfoSample * volAmount >> 12; } else { volume -= volume * (~lfoSample & 0xFF) * volAmount >> 12; } if (volume < 0) volume = 0; if (volume > 255) volume = 255; int pwMod = lfoSample * pwAmount >> 4; if (voice->lfoAttr.invert) pulseWidth -= pwMod; else pulseWidth += pwMod; if (pulseWidth < 0) pulseWidth = 0; if (pulseWidth > 254) pulseWidth = 254; // if (i == 0 && v == 0) printf("pulseWidth %d\n", pulseWidth); // --- WAVEFORM GENERATOR --- uint16_t accu16Last = ((uint32_t)voiceIn->accumulator >> 4) & 0xFFFF; double accumulator = voiceIn->accumulator + (double)freq * 65536.0 / (double)outputFrequency; if (accumulator >= overflow) { // avoid overflow and loss of precision accumulator -= overflow; } voiceIn->accumulator = accumulator; uint16_t accu16 = ((uint32_t)voiceIn->accumulator >> 4) & 0xFFFF; uint16_t sample = 0x7FFF; // silence enum WaveType waveType = voice->attr.wave; switch (waveType) { case WaveTypeSawtooth: { sample = accu16; break; } case WaveTypePulse: { sample = ((accu16 >> 8) > pulseWidth) ? 0xFFFF : 0x0000; break; } case WaveTypeTriangle: { sample = ((accu16 & 0x8000) ? ~(accu16 << 1) : (accu16 << 1)); break; } case WaveTypeNoise: { if ((accu16 & 0x1000) != (accu16Last & 0x1000)) { uint16_t r = voiceIn->noiseRandom; uint16_t bit = ((r >> 0) ^ (r >> 2) ^ (r >> 3) ^ (r >> 5) ) & 1; voiceIn->noiseRandom = (r >> 1) | (bit << 15); } sample = voiceIn->noiseRandom & 0xFFFF; break; } } // --- TIMEOUT --- if (voice->attr.timeout) { voiceIn->timeoutCounter -= 60.0 / outputFrequency; if (voiceIn->timeoutCounter <= 0.0) { voiceIn->timeoutCounter = 0.0; voice->status.gate = 0; } } // --- ENVELOPE GENERATOR --- if (!voice->status.gate) { voiceIn->envState = EnvStateRelease; } switch (voiceIn->envState) { case EnvStateAttack: voiceIn->envCounter += envRates[voice->envA] / outputFrequency; if (voiceIn->envCounter >= 255.0) { voiceIn->envCounter = 255.0; voiceIn->envState = EnvStateDecay; } break; case EnvStateDecay: if (voiceIn->envCounter > voice->envS * 16.0) { voiceIn->envCounter -= envRates[voice->envD] / outputFrequency; } break; case EnvStateRelease: if (voiceIn->envCounter > 0.0) { voiceIn->envCounter -= envRates[voice->envR] / outputFrequency; if (voiceIn->envCounter < 0.0) { voiceIn->envCounter = 0.0; } } break; } // --- OUTPUT --- volume = volume * (int)voiceIn->envCounter >> 8; // output peak to system registers lifeRegisters->voices[v].peak = volume; int16_t voiceSample = (((int32_t)(sample - 0x7FFF)) * volume) >> 10; // 8 bit for volume, 2 bit for global if (voice->status.mix & 0x01) { leftOutput += voiceSample; } if (voice->status.mix & 0x02) { rightOutput += voiceSample; } } // filter int32_t *filterBufferL = internals->filterBuffer[0]; int32_t *filterBufferR = internals->filterBuffer[1]; for (int f = AUDIO_FILTER_BUFFER_SIZE - 1; f > 0; f--) { filterBufferL[f] = filterBufferL[f - 1]; filterBufferR[f] = filterBufferR[f - 1]; } filterBufferL[0] = leftOutput; filterBufferR[0] = rightOutput; leftOutput = ((filterBufferL[0] >> 4) + (filterBufferL[1] >> 1) + (filterBufferL[2] >> 4)); rightOutput = ((filterBufferR[0] >> 4) + (filterBufferR[1] >> 1) + (filterBufferR[2] >> 4)); } stereoOutput[i++] = leftOutput >> volume; stereoOutput[i++] = rightOutput >> volume; } } ================================================ FILE: core/machine/audio_chip.h ================================================ // // Copyright 2016-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef audio_chip_h #define audio_chip_h #include #include #include #define NUM_VOICES 4 #define NUM_AUDIO_BUFFERS 6 #define AUDIO_FILTER_BUFFER_SIZE 3 // audio output channels for stereo #define NUM_CHANNELS 2 struct Core; enum WaveType { WaveTypeSawtooth, WaveTypeTriangle, WaveTypePulse, WaveTypeNoise }; enum EnvState { EnvStateAttack, EnvStateDecay, EnvStateRelease }; union VoiceStatus { struct { uint8_t volume:4; uint8_t mix:2; uint8_t init:1; uint8_t gate:1; }; uint8_t value; }; union VoiceAttributes { struct { uint8_t pulseWidth:4; uint8_t wave:2; uint8_t timeout:1; }; uint8_t value; }; enum LFOWaveType { LFOWaveTypeTriangle, LFOWaveTypeSawtooth, LFOWaveTypeSquare, LFOWaveTypeRandom }; union LFOAttributes { struct { uint8_t wave:2; uint8_t invert:1; uint8_t envMode:1; uint8_t trigger:1; }; uint8_t value; }; struct Voice { uint8_t frequencyLow; uint8_t frequencyHigh; union VoiceStatus status; uint8_t peak; union VoiceAttributes attr; uint8_t length; struct { uint8_t envA:4; uint8_t envD:4; }; struct { uint8_t envS:4; uint8_t envR:4; }; union LFOAttributes lfoAttr; struct { uint8_t lfoFrequency:4; uint8_t lfoOscAmount:4; }; struct { uint8_t lfoVolAmount:4; uint8_t lfoPWAmount:4; }; uint8_t reserved2; }; struct AudioRegisters { struct Voice voices[NUM_VOICES]; }; struct VoiceInternals { double accumulator; uint16_t noiseRandom; double envCounter; enum EnvState envState; double lfoAccumulator; bool lfoHold; uint16_t lfoRandom; double timeoutCounter; }; struct AudioInternals { struct VoiceInternals voices[NUM_VOICES]; struct AudioRegisters buffers[NUM_AUDIO_BUFFERS]; int readBufferIndex; int writeBufferIndex; bool audioEnabled; int32_t filterBuffer[NUM_CHANNELS][AUDIO_FILTER_BUFFER_SIZE]; }; void audio_reset(struct Core *core); void audio_bufferRegisters(struct Core *core); void audio_renderAudio(struct Core *core, int16_t *output, int numSamples, int outputFrequency, int volume); #endif /* audio_chip_h */ ================================================ FILE: core/machine/io_chip.h ================================================ // // Copyright 2016 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef io_chip_h #define io_chip_h #include #define NUM_GAMEPADS 2 // ================ Gamepad ================ union Gamepad { struct { uint8_t up:1; uint8_t down:1; uint8_t left:1; uint8_t right:1; uint8_t buttonA:1; uint8_t buttonB:1; }; uint8_t value; }; // ================ Status ================ union IOStatus { struct { uint8_t pause:1; uint8_t touch:1; }; uint8_t value; }; // ================ Attributes ================ union IOAttributes { struct { uint8_t gamepadsEnabled:2; // 0: off, 1...2: number of players uint8_t keyboardEnabled:1; uint8_t touchEnabled:1; }; uint8_t value; }; // =============================================== // ================ I/O Registers ================ // =============================================== struct IORegisters { union Gamepad gamepads[NUM_GAMEPADS]; // 2 bytes uint8_t touchX; uint8_t touchY; char key; union IOStatus status; union IOAttributes attr; }; #endif /* io_chip_h */ ================================================ FILE: core/machine/machine.c ================================================ // // Copyright 2016-2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "machine.h" #include #include #include #include "core.h" void machine_init(struct Core *core) { assert(sizeof(struct Machine) == 0x10000); } void machine_reset(struct Core *core, bool resetPersistent) { // rom, video ram, working ram memset(core->machine, 0, 0xE000); if (resetPersistent) { // persistent ram memset(core->machine->persistentRam, 0, PERSISTENT_RAM_SIZE); } // registers and reserved spaces memset(core->machine->reservedMemory, 0, 0x1000); memset(core->machineInternals, 0, sizeof(struct MachineInternals)); audio_reset(core); } int machine_peek(struct Core *core, int address) { if (address < 0 || address > 0xFFFF) { return -1; } if (address >= 0xE000 && address < 0xF000) // persistent { if (!core->machineInternals->hasAccessedPersistent) { delegate_persistentRamWillAccess(core, core->machine->persistentRam, PERSISTENT_RAM_SIZE); core->machineInternals->hasAccessedPersistent = true; } } // read byte return *(uint8_t *)((uint8_t *)core->machine + address); } bool machine_poke(struct Core *core, int address, int value) { if (address < 0x8000 || address > 0xFFFF) { // cartridge ROM or outside RAM return false; } if (address >= 0xF000 && address < 0xFE00) { // reserved memory return false; } if (address >= 0xFF80) { // reserved registers return false; } if (address == 0xFF76) // IO attributes { // check for illegal input change (gamepad <-> touch) union IOAttributes currAttr = core->machine->ioRegisters.attr; union IOAttributes newAttr; newAttr.value = value; if (currAttr.gamepadsEnabled > 0 && (newAttr.gamepadsEnabled == 0 || newAttr.touchEnabled)) { return false; } if (currAttr.touchEnabled && (newAttr.touchEnabled == 0 || newAttr.gamepadsEnabled > 0)) { return false; } } else if (address >= 0xE000 && address < 0xF000) // persistent { if (!core->machineInternals->hasAccessedPersistent) { delegate_persistentRamWillAccess(core, core->machine->persistentRam, PERSISTENT_RAM_SIZE); core->machineInternals->hasAccessedPersistent = true; } core->machineInternals->hasChangedPersistent = true; } // write byte *(uint8_t *)((uint8_t *)core->machine + address) = value & 0xFF; if (address == 0xFF76) // IO attributes { delegate_controlsDidChange(core); } else if (address >= 0xFF40 && address < 0xFF70) // audio { machine_enableAudio(core); } return true; } void machine_enableAudio(struct Core *core) { if (!core->machineInternals->audioInternals.audioEnabled) { core->machineInternals->audioInternals.audioEnabled = true; delegate_controlsDidChange(core); } } void machine_suspendEnergySaving(struct Core *core, int numUpdates) { if (core->machineInternals->energySavingTimer < numUpdates) { core->machineInternals->energySavingTimer = numUpdates; } } ================================================ FILE: core/machine/machine.h ================================================ // // Copyright 2016 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef machine_h #define machine_h #include #include #include #include "io_chip.h" #include "video_chip.h" #include "audio_chip.h" #define PERSISTENT_RAM_SIZE 4096 struct Core; // 64 KB struct Machine { // 0x0000 uint8_t cartridgeRom[0x8000]; // 32 KB // 0x8000 struct VideoRam videoRam; // 8 KB // 0xA000 uint8_t workingRam[0x4000]; // 16 KB // 0xE000 uint8_t persistentRam[PERSISTENT_RAM_SIZE]; // 4 KB // 0xF000 uint8_t reservedMemory[0xFE00 - 0xF000]; // 0xFE00 struct SpriteRegisters spriteRegisters; // 256 B // 0xFF00 struct ColorRegisters colorRegisters; // 32 B // 0xFF20 struct VideoRegisters videoRegisters; uint8_t reservedVideo[0x20 - sizeof(struct VideoRegisters)]; // 0xFF40 struct AudioRegisters audioRegisters; // 0xFF70 struct IORegisters ioRegisters; uint8_t reservedIO[0x10 - sizeof(struct IORegisters)]; // 0xFF80 uint8_t reservedRegisters[0x10000 - 0xFF80]; }; struct MachineInternals { struct AudioInternals audioInternals; bool hasAccessedPersistent; bool hasChangedPersistent; bool isEnergySaving; int energySavingTimer; }; void machine_init(struct Core *core); void machine_reset(struct Core *core, bool resetPersistent); int machine_peek(struct Core *core, int address); bool machine_poke(struct Core *core, int address, int value); void machine_enableAudio(struct Core *core); void machine_suspendEnergySaving(struct Core *core, int numUpdates); #endif /* machine_h */ ================================================ FILE: core/machine/video_chip.c ================================================ // // Copyright 2016-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "video_chip.h" #include "core.h" #include #define OVERLAY_FLAG (1<<6) int video_getCharacterPixel(struct Character *character, int x, int y) { int b0 = (character->data[y] >> (7 - x)) & 0x01; int b1 = (character->data[y | 8] >> (7 - x)) & 0x01; return b0 | (b1 << 1); } void video_renderPlane(struct Character *characters, struct Plane *plane, int sizeMode, int y, int scrollX, int scrollY, int pixelFlag, uint8_t *scanlineBuffer) { int divShift = sizeMode ? 4 : 3; int planeY = y + scrollY; int row = (planeY >> divShift) & 31; int cellY = planeY & 7; int x = 0; int b = 0; uint8_t d0 = 0; uint8_t d1 = 0; uint8_t pal = 0; uint8_t pri = 0; int pre = scrollX & 7; while (x < SCREEN_WIDTH) { if (!b) { int planeX = x + scrollX; int column = (planeX >> divShift) & 31; struct Cell *cell = &plane->cells[row][column]; int index = cell->character; if (sizeMode) { index += (cell->attr.flipX ? (planeX >> 3) + 1 : planeX >> 3) & 1; index += ((cell->attr.flipY ? (planeY >> 3) + 1 : planeY >> 3) & 1) << 4; } struct Character *character = &characters[index]; pal = cell->attr.palette << 2; pri = cell->attr.priority << 7; int fcy = cell->attr.flipY ? (7 - cellY) : cellY; d0 = character->data[fcy]; d1 = character->data[fcy | 8]; if (cell->attr.flipX) { // reverse bits hack from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits d0 = ((d0 * 0x0802LU & 0x22110LU) | (d0 * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16; d1 = ((d1 * 0x0802LU & 0x22110LU) | (d1 * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16; } } if (pre) { --pre; } else { if (pri >= (*scanlineBuffer >> 7)) { int pixel = ((d0 >> 7) & 1) | ((d1 >> 6) & 2); if (pixel) { *scanlineBuffer = pixel | pal | pri | pixelFlag; } } ++scanlineBuffer; ++x; } d0 <<= 1; d1 <<= 1; b = (b + 1) & 7; } } void video_renderSprites(struct SpriteRegisters *reg, struct VideoRam *ram, int y, uint8_t *scanlineBuffer, uint8_t *scanlineSpriteBuffer) { for (int i = NUM_SPRITES - 1; i >= 0; i--) { struct Sprite *sprite = ®->sprites[i]; if (sprite->x != 0 || sprite->y != 0) { int spriteY = y - sprite->y + SPRITE_OFFSET_Y; int size = (sprite->attr.size + 1) << 3; if (spriteY >= 0 && spriteY < size) { if (sprite->attr.flipY) { spriteY = size - spriteY - 1; } int charIndex = sprite->character + ((spriteY >> 3) << 4); if (sprite->attr.flipX) { charIndex += sprite->attr.size; } int minX = sprite->x - SPRITE_OFFSET_X; int maxX = minX + size; if (minX < 0) { int skip = -minX >> 3; if (sprite->attr.flipX) { charIndex -= skip; } else { charIndex += skip; } } if (minX < 0) minX = 0; if (maxX > SCREEN_WIDTH) maxX = SCREEN_WIDTH; uint8_t *buffer = &scanlineSpriteBuffer[minX]; int spriteX = minX - sprite->x + SPRITE_OFFSET_X; if (sprite->attr.flipX) { spriteX = size - spriteX - 1; } struct Character *character = &ram->characters[charIndex]; for (int x = minX; x < maxX; x++) { int pixel = video_getCharacterPixel(character, spriteX & 0x07, spriteY & 0x07); if (pixel) { *buffer = pixel | (sprite->attr.palette << 2) | (sprite->attr.priority << 7); } buffer++; if (sprite->attr.flipX) { if (!(spriteX & 0x07)) { character--; } spriteX--; } else { spriteX++; if (!(spriteX & 0x07)) { character++; } } } } } } for (int x = 0; x < SCREEN_WIDTH; x++) { int pixel = *scanlineSpriteBuffer; if (pixel && (pixel >> 7) >= (*scanlineBuffer >> 7)) { *scanlineBuffer = pixel; } scanlineSpriteBuffer++; scanlineBuffer++; } } void video_renderScreen(struct Core *core, uint32_t *outputRGB) { uint8_t scanlineBuffer[SCREEN_WIDTH]; uint8_t scanlineSpriteBuffer[SCREEN_WIDTH]; uint32_t *outputPixel = outputRGB; struct VideoRam *ram = &core->machine->videoRam; struct VideoRegisters *reg = &core->machine->videoRegisters; struct SpriteRegisters *sreg = &core->machine->spriteRegisters; struct ColorRegisters *creg = &core->machine->colorRegisters; for (int y = 0; y < SCREEN_HEIGHT; y++) { reg->rasterLine = y; itp_runInterrupt(core, InterruptTypeRaster); memset(scanlineBuffer, 0, sizeof(scanlineBuffer)); bool skip = (core->interpreter->interruptOverCycles > 0); if (!skip) { if (reg->attr.planeBEnabled) { int scrollX = reg->scrollBX | (reg->scrollMSB.bX << 8); int scrollY = reg->scrollBY | (reg->scrollMSB.bY << 8); video_renderPlane(ram->characters, &ram->planeB, reg->attr.planeBCellSize, y, scrollX, scrollY, 0, scanlineBuffer); } if (reg->attr.planeAEnabled) { int scrollX = reg->scrollAX | (reg->scrollMSB.aX << 8); int scrollY = reg->scrollAY | (reg->scrollMSB.aY << 8); video_renderPlane(ram->characters, &ram->planeA, reg->attr.planeACellSize, y, scrollX, scrollY, 0, scanlineBuffer); } if (reg->attr.spritesEnabled) { memset(scanlineSpriteBuffer, 0, sizeof(scanlineSpriteBuffer)); video_renderSprites(sreg, ram, y, scanlineBuffer, scanlineSpriteBuffer); } } // overlay video_renderPlane((struct Character *)overlayCharacters, &core->overlay->plane, 0, y, 0, 0, OVERLAY_FLAG, scanlineBuffer); for (int x = 0; x < SCREEN_WIDTH; x++) { int colorIndex = scanlineBuffer[x] & 0x1F; int color = (scanlineBuffer[x] & OVERLAY_FLAG) ? overlayColors[colorIndex] : skip ? 0 : creg->colors[colorIndex]; int r = (color >> 4) & 0x03; int g = (color >> 2) & 0x03; int b = color & 0x03; // add some gray (0x000F0F0F) to simulate screen #if __LIBRETRO__ *outputPixel = (b * 0x55) | ((g * 0x55) << 8) | ((r * 0x55) << 16) | 0x000F0F0F; #else *outputPixel = (r * 0x55) | ((g * 0x55) << 8) | ((b * 0x55) << 16) | 0x000F0F0F; #endif ++outputPixel; } } } ================================================ FILE: core/machine/video_chip.h ================================================ // // Copyright 2016 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef video_chip_h #define video_chip_h #include #include #define SCREEN_WIDTH 160 #define SCREEN_HEIGHT 128 #define NUM_CHARACTERS 256 #define NUM_PALETTES 8 #define PLANE_COLUMNS 32 #define PLANE_ROWS 32 #define NUM_SPRITES 64 #define SPRITE_OFFSET_X 32 #define SPRITE_OFFSET_Y 32 struct Core; // ================ Character ================ // 16 bytes struct Character { uint8_t data[16]; }; // ================ Sprite ================ union CharacterAttributes { struct { uint8_t palette:3; uint8_t flipX:1; uint8_t flipY:1; uint8_t priority:1; uint8_t size:2; // 1x1 - 4x4 characters }; uint8_t value; }; // 4 bytes struct Sprite { uint8_t x; uint8_t y; uint8_t character; union CharacterAttributes attr; }; // ================ Cell ================ // 2 bytes struct Cell { uint8_t character; union CharacterAttributes attr; }; // ================ Plane ================ // 2048 bytes struct Plane { struct Cell cells[PLANE_ROWS][PLANE_COLUMNS]; }; // =========================================== // ================ Video RAM ================ // =========================================== // 8 KB struct VideoRam { struct Character characters[NUM_CHARACTERS]; // 4 KB struct Plane planeA; // 2 KB struct Plane planeB; // 2 KB }; // ================================================= // ================ Video Registers ================ // ================================================= struct SpriteRegisters { struct Sprite sprites[NUM_SPRITES]; // 256 bytes }; struct ColorRegisters { uint8_t colors[NUM_PALETTES * 4]; // 32 bytes }; union DisplayAttributes { struct { uint8_t spritesEnabled:1; uint8_t planeAEnabled:1; uint8_t planeBEnabled:1; uint8_t planeACellSize:1; uint8_t planeBCellSize:1; }; uint8_t value; }; union ScrollMSB { struct { uint8_t aX:1; uint8_t aY:1; uint8_t bX:1; uint8_t bY:1; }; uint8_t value; }; struct VideoRegisters { union DisplayAttributes attr; uint8_t scrollAX; uint8_t scrollAY; uint8_t scrollBX; uint8_t scrollBY; union ScrollMSB scrollMSB; uint8_t rasterLine; }; // =========================================== // ================ Functions ================ // =========================================== void video_renderScreen(struct Core *core, uint32_t *outputRGB); #endif /* video_chip_h */ ================================================ FILE: core/overlay/overlay.c ================================================ // // Copyright 2017-2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "overlay.h" #include "core.h" #include "io_chip.h" #include void overlay_clear(struct Core *core); void overlay_init(struct Core *core) { struct TextLib *lib = &core->overlay->textLib; lib->core = core; lib->bg = OVERLAY_BG; lib->windowBg = OVERLAY_BG; lib->charAttr.priority = 1; lib->charAttr.palette = 1; lib->fontCharOffset = 0; lib->windowX = 0; lib->windowY = 0; lib->windowWidth = 20; lib->windowHeight = 16; lib->cursorX = 0; lib->cursorY = 0; } void overlay_reset(struct Core *core) { overlay_clear(core); core->overlay->textLib.cursorX = 0; core->overlay->textLib.cursorY = 0; } void overlay_updateState(struct Core *core) { overlay_clear(core); if (core->interpreter->state == StatePaused) { core->overlay->timer = 0; } if (!core->interpreter->debug) { core->overlay->textLib.cursorX = 0; core->overlay->textLib.cursorY = 0; } } void overlay_message(struct Core *core, const char *message) { struct TextLib *lib = &core->overlay->textLib; txtlib_setCells(lib, 0, 15, 19, 15, 0); txtlib_writeText(lib, message, 0, 15); core->overlay->messageTimer = 120; machine_suspendEnergySaving(core, 120); } void overlay_draw(struct Core *core, bool ingame) { struct TextLib *lib = &core->overlay->textLib; if (core->overlay->messageTimer > 0) { core->overlay->messageTimer--; if (core->overlay->messageTimer < 20) { txtlib_scrollBackground(lib, 0, 15, 19, 15, -1, 0); txtlib_setCell(lib, 19, 15, 0); } } if (ingame) { if (core->interpreter->state == StatePaused) { if (core->overlay->timer % 60 < 40) { txtlib_writeText(lib, "PAUSED", 7, 7); } else { txtlib_setCells(lib, 7, 7, 12, 7, 0); } } if (core->interpreter->debug) { txtlib_writeText(lib, "CPU", 17, 0); int cpuLoad = core->interpreter->cpuLoadDisplay; if (cpuLoad < 100) { txtlib_writeNumber(lib, cpuLoad, 2, 17, 1); txtlib_writeText(lib, "%", 19, 1); } else { txtlib_writeText(lib, "MAX", 17, 1); } } } core->overlay->timer++; } void overlay_clear(struct Core *core) { struct Plane *plane = &core->overlay->plane; for (int y = 0; y < PLANE_ROWS; y++) { for (int x = 0; x < PLANE_COLUMNS; x++) { struct Cell *cell = &plane->cells[y][x]; cell->character = 0; cell->attr.palette = 0; cell->attr.priority = 1; } } core->overlay->messageTimer = 0; } ================================================ FILE: core/overlay/overlay.h ================================================ // // Copyright 2017-2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef overlay_h #define overlay_h #include #include #include "video_chip.h" #include "overlay_data.h" #include "text_lib.h" struct Core; struct Overlay { struct Plane plane; struct TextLib textLib; int timer; int messageTimer; }; void overlay_init(struct Core *core); void overlay_reset(struct Core *core); void overlay_updateState(struct Core *core); void overlay_message(struct Core *core, const char *message); void overlay_draw(struct Core *core, bool ingame); #endif /* overlay_h */ ================================================ FILE: core/overlay/overlay_data.c ================================================ // // Copyright 2017-2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "overlay_data.h" uint8_t overlayColors[] = { // gamepads 0, (3 << 4) | (3 << 2) | 3, (2 << 4) | (2 << 2) | 2, (1 << 4) | (1 << 2) | 1, // paused text 0, (3 << 4) | (3 << 2) | 3, 0, 0 }; uint8_t overlayCharacters[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x24, 0x24, 0x24, 0x24, 0x3C, 0x24, 0x3C, 0xFE, 0xFE, 0xFE, 0xFE, 0x7E, 0x00, 0x00, 0x00, 0xFE, 0x92, 0x92, 0xDA, 0x7E, 0x00, 0x00, 0x00, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xDB, 0x81, 0xDB, 0xDB, 0x81, 0xDB, 0x7E, 0x1C, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x1C, 0x1C, 0x77, 0x41, 0x47, 0x71, 0x41, 0x77, 0x1C, 0xF7, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xEF, 0xF7, 0x9D, 0x9B, 0xF6, 0x6F, 0xD9, 0xB9, 0xEF, 0x3E, 0x7E, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3E, 0x62, 0x4A, 0xC7, 0x91, 0x9B, 0xC5, 0x7F, 0x3C, 0x3C, 0x7C, 0x7C, 0x78, 0x00, 0x00, 0x00, 0x3C, 0x24, 0x64, 0x4C, 0x78, 0x00, 0x00, 0x00, 0x1E, 0x3E, 0x7E, 0x7C, 0x7C, 0x7E, 0x3E, 0x1E, 0x1E, 0x32, 0x66, 0x4C, 0x4C, 0x66, 0x32, 0x1E, 0x78, 0x7C, 0x7E, 0x3E, 0x3E, 0x7E, 0x7C, 0x78, 0x78, 0x4C, 0x66, 0x32, 0x32, 0x66, 0x4C, 0x78, 0x00, 0x7E, 0x7E, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0x00, 0x7E, 0x5A, 0xE7, 0x81, 0xE7, 0x5A, 0x7E, 0x00, 0x3C, 0x3C, 0xFF, 0xFF, 0xFF, 0x3C, 0x3C, 0x00, 0x3C, 0x24, 0xE7, 0x81, 0xE7, 0x24, 0x3C, 0x00, 0x00, 0x00, 0x3C, 0x3C, 0x7C, 0x7C, 0x78, 0x00, 0x00, 0x00, 0x3C, 0x24, 0x64, 0x4C, 0x78, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x81, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x24, 0x24, 0x3C, 0x0F, 0x1F, 0x3F, 0x7E, 0xFC, 0xF8, 0xF0, 0xE0, 0x0F, 0x19, 0x33, 0x66, 0xCC, 0x98, 0xB0, 0xE0, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC3, 0x99, 0x91, 0x89, 0x99, 0xC3, 0x7E, 0x3C, 0x7C, 0x7C, 0x7C, 0x3C, 0xFF, 0xFF, 0xFF, 0x3C, 0x64, 0x44, 0x64, 0x24, 0xE7, 0x81, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF, 0x7E, 0xC3, 0x99, 0xF3, 0x66, 0xCF, 0x81, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC3, 0x99, 0xF3, 0xF9, 0x99, 0xC3, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0xFF, 0x99, 0x99, 0x81, 0xF9, 0x09, 0x09, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x81, 0x9F, 0x83, 0xF9, 0xF9, 0x83, 0xFE, 0x3E, 0x7E, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3E, 0x62, 0xCE, 0x83, 0x99, 0x99, 0xC3, 0x7E, 0xFF, 0xFF, 0xFF, 0x3F, 0x7E, 0x7C, 0x78, 0x78, 0xFF, 0x81, 0xF9, 0x33, 0x66, 0x4C, 0x48, 0x78, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC3, 0x99, 0xC3, 0x99, 0x99, 0xC3, 0x7E, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC3, 0x99, 0xC1, 0xF9, 0x99, 0xC3, 0x7E, 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00, 0x00, 0x3C, 0x24, 0x3C, 0x24, 0x3C, 0x00, 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x7C, 0x7C, 0x78, 0x00, 0x00, 0x3C, 0x24, 0x3C, 0x64, 0x4C, 0x78, 0x00, 0x1E, 0x3E, 0x7E, 0x7C, 0x7E, 0x3E, 0x1E, 0x00, 0x1E, 0x32, 0x66, 0x4C, 0x66, 0x32, 0x1E, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x81, 0xFF, 0x81, 0xFF, 0x00, 0x00, 0x78, 0x7C, 0x7E, 0x3E, 0x7E, 0x7C, 0x78, 0x00, 0x78, 0x4C, 0x66, 0x32, 0x66, 0x4C, 0x78, 0x7E, 0xFF, 0xFF, 0xFF, 0x3E, 0x3C, 0x3C, 0x3C, 0x7E, 0xC3, 0x99, 0xF3, 0x26, 0x3C, 0x24, 0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7E, 0x7E, 0xC3, 0x99, 0x91, 0x91, 0x9F, 0xC2, 0x7E, 0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3C, 0x66, 0xC3, 0x99, 0x81, 0x99, 0x99, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0x83, 0x99, 0x83, 0x99, 0x99, 0x83, 0xFE, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC3, 0x99, 0x9F, 0x9F, 0x99, 0xC3, 0x7E, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xFC, 0x86, 0x93, 0x99, 0x99, 0x93, 0x86, 0xFC, 0xFF, 0xFF, 0xFF, 0xFC, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x9F, 0x84, 0x9C, 0x9F, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFC, 0xF0, 0xF0, 0xF0, 0xFF, 0x81, 0x9F, 0x84, 0x9C, 0x90, 0x90, 0xF0, 0x7E, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC2, 0x9F, 0x91, 0x99, 0x99, 0xC3, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0x99, 0x81, 0x99, 0x99, 0x99, 0xFF, 0x7E, 0x7E, 0x7E, 0x3C, 0x3C, 0x7E, 0x7E, 0x7E, 0x7E, 0x42, 0x66, 0x24, 0x24, 0x66, 0x42, 0x7E, 0x3F, 0x3F, 0x3F, 0x0F, 0xFF, 0xFF, 0xFF, 0x7E, 0x3F, 0x21, 0x39, 0x09, 0xF9, 0x99, 0xC3, 0x7E, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0x93, 0x86, 0x86, 0x93, 0x99, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0x90, 0x90, 0x90, 0x90, 0x9F, 0x81, 0xFF, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xBD, 0x99, 0x81, 0x81, 0x99, 0x99, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0x89, 0x81, 0x91, 0x99, 0x99, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC3, 0x99, 0x99, 0x99, 0x99, 0xC3, 0x7E, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xF0, 0xF0, 0xF0, 0xFE, 0x83, 0x99, 0x83, 0x9E, 0x90, 0x90, 0xF0, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7E, 0xC3, 0x99, 0x99, 0x95, 0x93, 0xC1, 0x7F, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0x83, 0x99, 0x83, 0x86, 0x93, 0x99, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFE, 0x7F, 0xC1, 0x9F, 0xC3, 0x79, 0xF9, 0x83, 0xFE, 0xFF, 0xFF, 0xFF, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFF, 0x81, 0xE7, 0x24, 0x24, 0x24, 0x24, 0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0xFF, 0x99, 0x99, 0x99, 0x99, 0x99, 0xC3, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0xFF, 0x99, 0x99, 0x99, 0x99, 0xC3, 0x66, 0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0x99, 0x99, 0x81, 0x81, 0x99, 0xBD, 0xE7, 0xFF, 0xFF, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0xC3, 0x66, 0xC3, 0x99, 0x99, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x3C, 0x3C, 0xFF, 0x99, 0x99, 0xC3, 0x66, 0x24, 0x24, 0x3C, 0xFF, 0xFF, 0xFF, 0x7E, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xF3, 0x66, 0xCC, 0x9F, 0x81, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC3, 0x99, 0xA5, 0xBD, 0xA5, 0xC3, 0x7E, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC3, 0xA1, 0xB9, 0xA5, 0xB9, 0xC3, 0x7E, 0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x3C, 0x66, 0xE7, 0x81, 0x81, 0xE7, 0x66, 0x3C, 0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x3C, 0x66, 0xC3, 0x99, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x81, 0xFF, }; ================================================ FILE: core/overlay/overlay_data.h ================================================ // // Copyright 2017-2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef overlay_data_h #define overlay_data_h #include #include #include "video_chip.h" extern uint8_t overlayColors[]; extern uint8_t overlayCharacters[]; #endif /* overlay_data_h */ ================================================ FILE: docs/license.txt ================================================ Copyright 2016-2021 Timo Kloss This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ================================================ FILE: docs/manual.html ================================================ LowRes NX Manual

Introduction

Program retro games in BASIC

Make your own retro games on a virtual game console. Program in the classic BASIC language and create sprites, tile maps, sound and music with the included tools. As a beginner you will quickly understand how to create simple text games or show your first sprite on a tile map. As an experienced programmer you can discover the full potential of retro hardware tricks!

Virtual Game Console

Imagine LowRes NX as a handheld game console with a d-pad, two action buttons and a little rubber keyboard below a slidable touchscreen. LowRes NX was inspired by real 8- and 16-bit systems and simulates chips for graphics, sound and I/O, which actually work like classic hardware. It supports hardware sprites as well as hardware parallax scrolling, and even offers vertical blank and raster interrupts to create authentic retro effects.

Old-School Programming

The programming language of LowRes NX is based on second-generation, structured BASIC. It offers all the classic commands, but with labels, loops and subprograms instead of line numbers. Graphics and sound are supported by additional commands and you can even access the virtual hardware directly using PEEK and POKE. You have complete control over the program flow, there is no standard update function to implement.

Creative Tools

LowRes NX includes all the tools you need: The Gfx Designer for editing sprites, tiles, fonts and maps, as well as the Sound Composer for music and sound effects. All of these are just normal BASIC programs. You can change and improve them or even create your own custom editors.

Share and Play

Send your games directly to other users or share them via the website. All programs are open source, so you can play them, learn from them and edit them. Do you prefer making just art or music? Share your creations as assets and let other programmers use them in their projects.

Specifications

  • Cartridge ROM: 32 KB for gfx, music, any binary data
  • Code: BASIC, max 16384 tokens
  • Screen: 160x128 pixels, 60 Hz
  • Backgrounds: Two layers, tile-based, scrollable
  • Sprites: 64, max 32x32 pixels
  • Colors: 8 dynamic 6-bit palettes with 4 colors each
  • Sound: 4 voices, saw/tri/pulse/noise, pulse width, volume, ADSR, LFO
  • Input: Two game controllers with d-pad and two buttons + pause
  • Optional input: Keyboard and touchscreen/mouse

Contents

Getting Started

Try some of the included programs to see how LowRes NX can look like. Have a look at the action game LowRes Galaxy 2, the text adventure LowRes Adventure, and the demo Star Scroller.

On the "My Programs" screen select a program to open the source code editor. Then tap on the Play button to run it.
Find the examples in the folder "programs". Open the LowRes NX application and drag and drop any .nx file into its window. You can also select LowRes NX as default application for .nx files, so programs can be started simply by double clicking them.

Once you have played enough, you can create your first own program.

On the "My Programs" screen tap on the Plus button to create a new program. Select it to open the source code editor.
Use any text editor to create a new file. On Windows make sure the text editor supports Mac/Linux line ends, otherwise you may see everything in one line.

Type these lines:

PRINT "WELCOME!"
PRINT "WHAT IS YOUR NAME?"
INPUT ">";N$
PRINT "HELLO ";N$;"!"
Save your program file with a useful name and the extension ".nx".

Now run your program. This is a little example using the keyboard. Let's try something with a gamepad. Create another new program and type this:

GAMEPAD 1
X=76
Y=60
DO
  IF UP(0) THEN Y=Y-1
  IF DOWN(0) THEN Y=Y+1
  IF LEFT(0) THEN X=X-1
  IF RIGHT(0) THEN X=X+1
  SPRITE 0,X,Y,225
  WAIT VBL
LOOP

Run this program and you will see an "A" on the screen which you can move around using the gamepad.

With the program still running, tap the menu button on the top right and select "Capture Program Icon". Now exit the program and return to the "My Programs" screen. There you will see your program with a new image. Long tap the icon and select "Rename..." to give it a better name.

Editing

Programs and Data

A program file contains a complete game or application, including all its data, stored as simple text. The first part is the BASIC source code. Please read the programming chapters for further explanation.

The second part are the cartridge ROM entries. These are up to 16 numbered data blocks, which can contain any kind of data, for example graphics, level maps, music, etc. When a program is running, all its ROM entries are accessible in the first 32 KB of the memory.

You can easily create and edit ROM entries by using tools. Tools are normal NX programs, but they are specifically made for editing data. They can access any NX file as a "virtual disk" and use its ROM entries like files.

There are two ways of using tools:

  • You can open tools directly like other NX programs. They will use the "Disk.nx" file in the tool's folder for loading and saving their data.
  • You can open any program you want to edit and select a tool from a menu. This way the tool will access directly the data of the current program.

Let's try it. Open your program with the moving "A" (from the "Getting Started" chapter) and select "Gfx Designer" from the Tools menu.

Open your program, tap the Tools button and select the tool.
Run your program and press the Escape key to enter the development menu. Then press the Edit (ED) button and select the tool. By default the tools menu is empty, so drag and drop the programs "Gfx Designer" and "Sound Designer" into the window.

Draw something as character #1 (keep #0 empty), then tap on "File" and save as file 2 ("Main Characters"). Now return to the source code editor and you will see some hexadecimal data below your program. This is your image! To see it, change the line

SPRITE 0,X,Y,225

to

SPRITE 0,X,Y,1

and run your program. There it is!

Press Ctrl+R in the LowRes NX application to reload and run your current program. The Run button in the developer menu does the same.

Keep in mind that tools don't save automatically, so never forget to save before you exit them.

Standard ROM Entries

For an easy start you should use the ROM entry numbers of the following table. Their data is made ready for use automatically.

#0Keep empty for default font
#1Color palettes
#2Characters (sprites, tiles)
#3Background (tile map)
#15Sounds and music

If cartridge ROM entry 0 is not used by a program, the compiler adds character data for the default font. It occupies the characters 192-255 and is automatically copied to video RAM on program start. If you want to use the default font, make sure to keep ROM entry 0 unused.

Gfx Designer

Use the Gfx Designer to draw characters (sprites, tiles, fonts), design backgrounds (tile maps, screen layouts) and edit color palettes.

On the bottom right there are three tabs for the different sections of the program: The character editor, the palette editor and the background editor. You can use the keys 1, 2 and 3 to switch between them when running LowRes NX on a computer.

The Gfx Designer loads the main palettes (file 1), characters (file 2), and background (file 3) on startup, but keep in mind that it doesn't save automatically.

Character Editor

At the bottom part you see the 256 characters split into four pages. There you can select the current one for editing. Keep the last page empty, if you want to use the default font. Also character #0 should be empty for a clean background.

The top left square is for drawing the current character using the selected color.

FILEGo to the characters file menu.
MOVMove the pixels of the current character.
16*16Go to the 16x16-pixel edit screen.
FLIPFlip the current character horizontally or vertically.
SPINRotate the current character.
CLRClear the current character with the selected color.
CUTCopy and clear the current character.
COPCopy the current character.
PASPaste the copied character.
SELChange the size of the selection.

The size of the selection affects all edit tools on this screen, as well as drawing in the background editor.

Palette Editor

The bottom part is identical to the character editor.

In the box at the left you select the current color palette.

On the right you can edit each color of the current palette using the RGB sliders. The first color is usually transparent and unused, except the first color of palette 0, which is used as the screen backdrop color.

FILEGo to the palettes file menu.

Background Editor

Here you draw your background using the character and color palette currently selected on the other screens.

FILEGo to the background file menu.
PanDrag the visible map area.
DrawDraw with the current mode.
EraseErase cells including their attributes.
FLIPToggles the X/Y flip attributes.
PRIToggles the priority attribute.

You can use the arrow or WASD keys to scroll the map when running LowRes NX on a computer. The keys Z, X, C, V and B select the tool.

The draw tool has different modes. By tapping the button again, when it's already selected, you can change the mode:

DrawEdit single cells.
FillEdit areas by dragging.
CharacterDraw with the selected character and color palette, using the selected flip and priority attributes.
PaletteChange the color palette only.
PriorityChange the priority only. Cells with priority 1 are shown in green, all others in red.
StampSelect an area by dragging and then draw with it (copy and paste).

File Menu

From here you load and save characters, color palettes and the background separately. There is no option to save everything at once.

The list shows all 16 files of the current virtual disk, or in other words the ROM entries of the program you are editing. Select one and tap on Load or Save.

Characters

NEWClear all characters.
FONCopy a standard font to the current page.
PAGLoad only one page from the selected file to the current page. Useful to include fonts from another file, for example.

Palettes

NEWReset to the default colors.

Backgrounds

NEWClear the complete background (keeping its size).
SIZEGo to the size menu, where you can choose the width and height of the background (in cells), as well as the cell size (8x8 or 16x16 pixels).

Sound Composer

The Sound Composer serves to create sound presets, sequences ("tracks") and complete songs.

It loads the main sound data (file 15) on startup, but keep in mind that it doesn't save automatically. A sound file includes all sound and music data.

Structure

16 Sounds

A sound is a preset with all available sound parameters (waveform, envelope, LFO, etc.). It can be used directly with the PLAY command in your program or as an instrument in a track/song.

64 Tracks

A track is a sequence of 32 steps for a single voice, where each step can play a note or modify the sound. It can be used in your program for complex sound effects and short melodies (e.g. "level up") with the TRACK command. Tracks are also used to create songs.

64 Patterns

A pattern is a block of music, which defines which track should be played on each of the four voices. Patterns will be played one after another, except for the following cases: If the next pattern is empty, the song is stopped. When the current pattern finishes and has a "loop end" flag, the player jumps back to the previous pattern with a "loop start" flag. When a pattern finishes and has a "stop" flag, the player stops.
If you want to create music for a game and you plan to use additional sound effects, you should leave at least one voice empty.

Songs

The 64 patterns can be used for one long song or several shorter ones. By using the "loop" and "stop" flags songs can be separated. To play one specific song, just start at its first pattern. To play a song in your program use the MUSIC command.

Editors

The Sound Composer has three tabs in the top right corner for the different sections: The pattern editor, the track editor and the sound editor. This is a "Tracker"-style program, which means the timeline goes from top to bottom, not from left to right. So the steps in a track are also called rows. Most values in the range from 0 to 15 are shown in hexadecimal format (0-F).

There is some keyboard support when running LowRes NX on a computer: The arrow keys move the cursor and the key rows simulate a musical keyboard with two octaves to enter notes. The return key enters a note stop, backspace deletes the current note and space toggles between play (pattern/track) and stop.

The Pattern Editor

Here you can select a pattern, choose its tracks for all voices and edit directly the notes of each selected track. There is also a toggle button for the "loop" and "stop" flags for the current pattern. If you set notes in a voice without track, it selects automatically a free track. Use the star symbol from the musical keyboard, if you want to stop/release a note in a track.

The Track Editor

Here you can select and edit a single track with additional parameters:

SSound
VVolume
CSound command
PParameter

The Sound Editor

Define and test your sounds here. The currently selected sound will be used in the other editors for new notes.

Sound Commands

These commands allow you to change parameters dynamically while playback. Use them in the track editor.

CPPurpose
00No command
0xMix (1=left, 2=right, 3=center/both)
1xAttack Time
2xDecay Time
3xSustain Level
4xRelease Time
5xLFO Rate
6xLFO Frequency Amount
7xLFO Volume Amount
8xLFO Pulse Width Amount
9xPulse Width
DxSlow Speed (like E, but +16)
ExSpeed (ticks per row, 8 by default)
F0Break Track/Pattern
F1Cut Note / Volume 0

Language Basics

The programming language of LowRes NX is based on second-generation, structured BASIC (1985 style).

This manual is meant to be a reference for people with prior programming experience. If you are new to programming, you should look for tutorials on the LowRes NX website and then check this manual for further details.

Types and Variables

Available value types are strings and numbers (floating point).

Variable names can contain letters (A-Z), digits (0-9) and underscores (_), but cannot begin with a digit. Reserved keywords (commands, functions) cannot be used as variable names, but they can be part of them. There is a list in the chapter "Reserved Keywords".

Examples of valid variable names:

X
A1
LONG_VARIABLE_NAME
PRINTER

Examples of invalid variable names:

2B
PRINT

String variable names end with a $ symbol, for example:

A$
NAME$

Variables are not explicitly declared, but they need to be initialized with a value before you can read from them. Values are assigned to variables using the equal symbol:

NAME$="LOWRES NX"
LIVES=3

Hexadecimal and binary notation can be used for number values:

$FF02
%11001011

Arrays

DIM

DIM [GLOBAL] var-list

Defines arrays with the highest index for each dimension:

DIM A(100)
DIM MAP(31,23,1),NAMES$(9),SCORES(9)

Access elements from arrays, indices start from 0:

DIM SCORES(9)
SCORES(0)=100
SCORES(9)=5
PRINT SCORES(0),SCORES(9)

All elements of arrays are automatically initialized, using zeros (0) or empty strings ("").

With the optional GLOBAL keyword the arrays will be accessable from all subprograms.

=UBOUND

UBOUND(var[,dimension])

Returns the upper bound (the highest index) of a dimension of the array var.

The dimension parameter is an optional number used when the array is multi-dimensional, and specifies the dimension of the array. The default value is 1.

DIM SCORES(9)
FOR I=0 TO UBOUND(SCORES)
  PRINT SCORES(I)
NEXT I

Labels

A label marks a position in a program and is used for commands like GOTO. It consists of a name, using the same rules as for variables, followed by a colon.

TESTLABEL:

Operators

Arithmetic

Symbol Example Purpose
- -B Negation
^ X^3 Exponentiation
* 2*Y Multiplication
/ X/2 Division
\ X\2 Integer Division
+ C+2 Addition
- 100-D Subtraction
MOD X MOD 2 Modulo

Operations are performed in mathematical order, for example multiplications and divisions are performed before additions and subtractions.

PRINT 10+2*5-8/4+5^2

The order can be specified explicitly through the use of parentheses, for example:

PRINT (3+4)*5

Relational

Symbol Example Purpose
= A=10 Equal
<> A<>100 Not equal
> B>C Greater than
< 5<X Less than
>= X>=20 Greater than or equal
<= X<=30 Less than or equal

A relational operator expression returns the value true (-1) or false (0).

Logical/Bitwise

Symbol Example Purpose
NOT NOT (X=15)
NOT 0
"Not"
AND A=1 AND B=12
170 AND 15
"And"
OR X=10 OR Y=0
128 OR 2
"Or"
XOR A XOR B "Exclusive Or"

How to Use Operators

All operators are available for numbers. Relational and addition operators are usable with strings, too:

SUM=1+3
IF SUM<5 THEN PRINT "LESS THAN 5"
NAME$="LOWRES NX"
GREET$="HELLO "+NAME$+"!"
IF NAME$>"LOWRES" THEN PRINT GREET$
IF SUM=4 AND NAME$<>"" THEN PRINT "OK"

Program Flow Control

Basics

REM

REM remark
' remark

Allows you to put comments into your program. REM lines are not executed. You can use an apostrophe (') in place of the word REM.

REM THIS IS A TEST PROGRAM
REM WRITTEN BY TIMO KLOSS
PRINT "HELLO"
'SHOW MORE TEXT...
PRINT "BYE"

IF...THEN...ELSE

IF expr THEN command [ELSE command]

Checks if the given expression is true or false. If it's true, the command after THEN is executed, otherwise the one after ELSE. The ELSE part is optional.

INPUT "NUMBER A:";A
INPUT "NUMBER B:";B
PRINT "SAME? ";
IF A=B THEN PRINT "YES" ELSE PRINT "NO"
IF A<B THEN PRINT "A LESS THAN B"
IF A>B THEN PRINT "A GREATER THAN B"

If you want to execute more than one command (or avoid long lines), you can use the block version of the IF command. It must be closed with the line END IF.

IF expression THEN
  commands
[ELSE IF expression THEN]
  commands
[ELSE]
  commands
END IF

Blocks can be nested like in this example:

PRINT "NUMBER OF PLAYERS"
INPUT "1-4: ";N

IF N<1 OR N>4 THEN
  PRINT "WRONG INPUT"
  PRINT "RESTART!"
ELSE IF N=1 THEN
  PRINT "SINGLE-PLAYER"
ELSE
  PRINT "MULTI-PLAYER"
  IF N=4 THEN
    PRINT "MAXIMUM"
  END IF
END IF

GOTO

GOTO label

Jumps to the given label and continues the program execution there.

PRINT "START"
GOTO GAMELEVEL

GAMEOVER:
PRINT "GAME OVER"
END

GAMELEVEL:
PRINT "PLAYING"
GOTO GAMEOVER

GOSUB

GOSUB label

Adds the current program position to a stack and jumps to the given label. The program after the label is called a subroutine and must be finished using RETURN.

FOR I=1 TO 5
  GOSUB SHOWNUMBER
NEXT I
END

SHOWNUMBER:
PRINT "NUMBER",I
RETURN

NOTE: Subroutines exist mostly for historical reasons. You should prefer the more powerful and safer subprograms.

RETURN

RETURN

Jumps back to the position of the last call of GOSUB and removes it from the stack.

RETURN label

Works like GOTO, but clears the whole stack. Use this to exit from a subroutine, if you want to continue your program somewhere else.

FOR I=1 TO 5
  GOSUB SHOWNUMBER
NEXT I
END

SHOWNUMBER:
IF I=4 THEN RETURN GAMEOVER
PRINT "NUMBER",I
RETURN

GAMEOVER:
PRINT "GAME OVER"

END

END

Stops the program from any position. The program is also stopped automatically after the last line of code.

PRINT "THIS HAPPENS"
END
PRINT "THIS DOESN'T"

WAIT VBL

WAIT VBL

Waits for the next frame. This (or WAIT n) should be the last command in all loops which do animations and/or handle input, like the main game loop.

DO
  PAL RND(3)
  TEXT RND(19),RND(15),"*"
  WAIT VBL
LOOP

WAIT

WAIT n

Waits n frames (n/60 seconds), where the minimum for n is 1. Subprograms running from interrupts (ON VBL/RASTER, MUSIC) will continue to work as normal during this period.

WAIT 1 is the same as WAIT VBL, so why is there WAIT VBL? Because it looks cooler and nerdier! A little guideline: Use WAIT VBL in loops for smooth animations and input handling, and WAIT n if you actually want to wait some time.

PRINT "INUTILIS"
PRINT "PRESENTS..."
WAIT 60
PRINT "THE WAIT PROGRAM!"

Loops

FOR...NEXT

FOR var=a TO b [STEP s]
  commands
NEXT var

Performs a series of commands in a loop a given number of times. The FOR command uses the variable var as a counter, starting with the value a. All commands until NEXT are executed, then the counter is increased by s (or 1 if STEP is omitted). A check is performed to see if the counter is now greater than b. If not, the process is repeated. If it is greater, the program continues with the lines after NEXT.

FOR I=1 TO 8
  PRINT I
NEXT I
FOR I=2 TO 16 STEP 2
  PRINT I
NEXT I

If STEP s is negative, the loop is executed until the counter is less than value b.

FOR I=8 TO 1 STEP -1
  PRINT I
NEXT I

Loops can be placed inside of others:

FOR P=0 TO 3
  PAL P
  FOR N=1 TO 4
    PRINT N
  NEXT N
NEXT P

DO...LOOP

DO
  commands
LOOP

Performs commands in an endless loop. You can use GOTO or EXIT to leave it.

DO
  PAL RND(3)
  PRINT "FOREVER"
LOOP

REPEAT...UNTIL

REPEAT
  commands
UNTIL expression

Executes the commands in a loop until the given expression is true. The loop is executed at least once.

REPEAT
  A=RND(9)
  PRINT A
UNTIL A=0

WHILE...WEND

WHILE expression
  commands
WEND

Executes the commands in a loop as long as the given expression is true.

A=4
WHILE A<10
  PRINT A
  A=A+1
WEND

EXIT

EXIT

Exits immediately from a loop created with the FOR...NEXT, REPEAT...UNTIL, WHILE...WEND, or DO...LOOP commands.

DO
  INPUT "ENTER E TO EXIT:";A$
  IF A$="E" THEN EXIT
  PRINT "GOING ON..."
LOOP
PRINT "END"

Subprograms

SUB...END SUB

SUB name [(parameter-list)]
  commands
END SUB

Defines a subprogram with the given name. The optional parameter list can contain two types of entries: simple variables and array variables (followed by an empty parentheses pair). Entries are separated by commas. By default all variables inside the subprogram are local.

NOTE: Don't use GOTO or GOSUB to jump out of a subprogram!

CALL

CALL name [(argument-list)]

Executes the subprogram with the given name and returns to the current position after finishing it. The argument list must match the parameters of the SUB definition. Simple variables, single array elements and entire arrays (followed by an empty parentheses pair) are passed by reference to the subprogram. Other expressions are passed by value.

CALL GREET("LOWRES NX",3)
CALL GREET("YOU",1)
CALL BYE

SUB BYE
  PAL 1
  PRINT "BYE"
END SUB

SUB GREET(N$,A)
  FOR I=1 TO A
    PAL RND(3)
    PRINT "HELLO",N$
  NEXT I
END SUB

EXIT SUB

EXIT SUB

Exits a subprogram before END SUB is reached.

GLOBAL

GLOBAL variable-list

Makes variables from the main program available to all subprograms. The list can contain simple variables only. For arrays you should use DIM GLOBAL. This command cannot be used within a subprogram.

GLOBAL SCORE,TRIES

TRIES=3

CALL WIN(10)
CALL WIN(20)

SUB WIN(N)
  SCORE=SCORE+N
  PRINT "SCORE:",SCORE,"TRIES:",TRIES
END SUB

Text

PRINT

PRINT expression-list

Outputs text to the current window. Expressions can be strings or numbers, separated by commas or semicolons. A comma separates the output with a space, a semicolon outputs without space. End the list with a comma or semicolon to keep the cursor at the end of the output, otherwise a new line is started.

PRINT "HELLO WORLD"
PRINT 42
PRINT 1+3
PRINT "SCORE:",100
PRINT "ONE ";
PRINT "LINE"

INPUT

INPUT ["prompt";]var

Lets the user enter a text or number on the keyboard and stores it in the variable var. Optionally it can show a prompt text before (cannot be a variable).

INPUT automatically enables the keyboard.

INPUT "NAME:";N$
INPUT "AGE:";AGE
PRINT "HELLO ";N$;"!"
PRINT "SOON YOU ARE ";AGE+1

LOCATE

LOCATE cx,cy

Moves the text cursor to column cx and row cy relative to the current window.

LOCATE 2,4
PRINT "HELLO"

=CURSOR.X/Y

CURSOR.X
CURSOR.Y

Returns the current column (X) or row (Y) of the text cursor relative to the current window.

WINDOW

WINDOW cx,cy,w,h,b

Sets the text output window to cell position cx,cy and sets the size to w columns and h rows. Text will be written to background b (0 or 1).

WINDOW 6,4,8,4,0
PRINT "LONG TEXT IN A SMALL WINDOW"

CLW

CLW

Clears the window with spaces and resets the text cursor position.

PRINT "HELLO"
CLW
PRINT "BYE"

User Input

Gamepads

GAMEPAD

GAMEPAD n

Enables gamepads for n (1 or 2) players. Once the gamepad is enabled, the program cannot change to touchscreen/mouse input anymore.

=UP/DOWN/LEFT/RIGHT

UP(p)
DOWN(p)
LEFT(p)
RIGHT(p)

Returns true if the given direction is currently pressed on the direction pad of player p (0/1).

UP TAP(p)
DOWN TAP(p)
LEFT TAP(p)
RIGHT TAP(p)

With the optional TAP keyword, this function returns true only for the first frame the button is pressed.

GAMEPAD 1
DO
  IF UP(0) THEN PRINT "UP"
  IF DOWN(0) THEN PRINT "DOWN"
  IF LEFT TAP(0) THEN PRINT "TAP <"
  IF RIGHT TAP(0) THEN PRINT "TAP >"
  WAIT VBL
LOOP

=BUTTON

BUTTON(p[,n])

Returns true if button A (n=0) or B (n=1) is currently pressed by player p (0/1). If the parameter n is omitted, both buttons (A and B) are checked.

BUTTON TAP(p[,n])

With the optional TAP keyword, this function returns true only for the first frame the button is pressed.

GAMEPAD 1
DO
  IF BUTTON(0,0) THEN PRINT "A"
  IF BUTTON TAP(0,1) THEN PRINT "TAP B"
  WAIT VBL
LOOP

PAUSE ON/OFF

PAUSE ON
PAUSE OFF

Enables or disables the automatic pause handling. By default it's enabled, so if you press the pause button, the program stops and shows "PAUSE" on the screen, until the button is pressed again.

PAUSE

PAUSE

Pauses the program and shows the default "PAUSE" screen, even if automatic pause handling is disabled.

=PAUSE

PAUSE

Returns true if the pause button was pressed, otherwise false. After calling this function its value is cleared, so it returns each button tap only once. The automatic pause handling needs to be disabled for this function.

GAMEPAD 1
PAUSE OFF
DO
  IF PAUSE THEN PRINT "PAUSE PRESSED"
  WAIT VBL
LOOP

Touchscreen/Mouse

Use touchscreen support only if you think it will work well with a computer mouse, too. If you want to create your own game buttons, keep in mind that your game might be unplayable on a computer, because it won't support the keyboard or a real gamepad. Always consider using the standard gamepad functions.

TOUCHSCREEN

TOUCHSCREEN

Enables the touchscreen/mouse support. Once it's enabled, the program cannot change to gamepad input anymore.

=TOUCH.X/Y

TOUCH.X
TOUCH.Y

Returns the current X or Y pixel position where the user touches the screen, or where it was touched the last time.

=TOUCH

TOUCH

Returns true if the screen is currently touched.

TOUCHSCREEN
DO
  IF TOUCH THEN PRINT TOUCH.X,TOUCH.Y
  WAIT VBL
LOOP

=TAP

TAP

Returns true if the screen is currently touched and was not touched the last frame.

TOUCHSCREEN
DO
  IF TAP THEN PRINT TOUCH.X,TOUCH.Y
  WAIT VBL
LOOP

Keyboard

KEYBOARD ON/OFF/OPTIONAL

KEYBOARD ON
KEYBOARD OFF

Enables or disables the keyboard. While the keyboard is enabled, gamepads don't work.

KEYBOARD OPTIONAL

Enables the keyboard, but won't show an on-screen keyboard on touchscreen devices. Programs using this mode should be completely usable with gamepad or touch control and use the keyboard for alternative input only.

=INKEY$

INKEY$

Returns the last pressed key as a string. If no key was pressed, it returns an empty string (""). After calling this function its value is cleared, so it returns each pressed key only once. The keyboard needs to be enabled for this function.

KEYBOARD ON
DO
  I$=INKEY$
  IF I$<>"" THEN PRINT I$
  WAIT VBL
LOOP

Graphics

All graphics in LowRes NX are based on characters. A character is an 8x8-pixel image with 3 colors plus transparent. It's displayed with one of the 8 programmable color palettes.

At program start all characters from ROM entry 2 are copied to video RAM to make them immediately usable.

The display is composed of 3 layers, which are from back to front:

  • Background 1 (BG 1)
  • Background 0 (BG 0)
  • Sprites

Each sprite and background cell has an attribute called "priority". By setting it, the cell or sprite will appear on a higher display layer. Actually there are 6 layers, from back to front:

  • Background 1 (BG 1) - prio 0
  • Background 0 (BG 0) - prio 0
  • Sprites - prio 0
  • Background 1 (BG 1) - prio 1
  • Background 0 (BG 0) - prio 1
  • Sprites - prio 1

Sprites

Sprites are independent objects, which can be freely moved on the screen. They can have a size of 8x8 pixels (one character) or up to 32x32 pixels by grouping several characters. Each sprite has the standard character attributes (color palette, flip X/Y, priority) and additionally its size.

SPRITE

SPRITE n,[x],[y],[c]

Sets the position (x,y) and character (c) of sprite n (0-63). All parameters can be omitted to keep their current settings.

SPRITE n [PAL pal] [FLIP fx,fy]
... [PRIO pri] [SIZE s]

Sets one or more attributes of sprite n (0 - 63):

pal palette number (0-7)
fx flip horizontally (0/1)
fy flip vertically (0/1)
pri priority (0/1)
s size (0-3):
0: 1 character (8x8 px)
1: 2x2 characters (16x16 px)
2: 3x3 characters (24x24 px)
3: 4x4 characters (32x32 px)
SPRITE 0,32,64,193
SPRITE 1,64,64,193
SPRITE 1 PAL 1 SIZE 3
FOR X=64 TO 128
  SPRITE 1,X,,
  IF X=96 THEN SPRITE 1 FLIP 1,0
  WAIT VBL
NEXT X

SPRITE.A

SPRITE.A n,a

Sets all attributes of sprite n (0-63) as a single 8-bit value. See "Sprite Registers" for more information.

NOTE: It's easier to set these values with the attributes syntax of the SPRITE command.

SPRITE OFF

SPRITE OFF [n]
SPRITE OFF a TO b

Hides one or more sprites. If all parameters are omitted, all sprites (0 - 63) are hidden. With one parameter only the given sprite is hidden. The last option is to hide sprites in the range from a to b.

=SPRITE.X/Y

SPRITE.X(n)
SPRITE.Y(n)

Return the position of sprite n.

=SPRITE.C

SPRITE.C(n)

Returns the character of sprite n.

=SPRITE.A

SPRITE.A(n)

Returns the attributes of sprite n as an 8-bit value. See "Sprite Registers" for more information.

=SPRITE HIT

SPRITE HIT(n[,a [TO b]])

Returns true if sprite n collides with another sprite (which means that pixels overlap). If no more parameters are given, it will check with all other visible sprites. If the a parameter is added, it will check only with that sprite a. If all parameters are given, it will check with all sprites from number a to number b.

SPRITE 0,32,64,225
SPRITE 1,96,68,226
FOR X=32 TO 128
  SPRITE 0,X,,
  IF SPRITE HIT(0,1) THEN
    SPRITE 0 PAL 1
  ELSE
    SPRITE 0 PAL 0
  END IF
  WAIT VBL
NEXT X

=HIT

HIT

Returns the number of the sprite which collided with the sprite of the last call of SPRITE HIT.

Backgrounds

A background is a map of 32x32 character cells, which is used for text and tile based maps or images. Each cell has the information of which character it contains and additional attributes (color palette, flip X/Y, priority).

As a character has the size of 8x8 pixels, the resulting background size is 256x256 pixels, which is larger than the actual screen (160x128). By modifying the scroll offset of a background, the visible area can be moved.

If the visible area moves out of the borders of the background, the display wraps around the edges. This can be used to achieve endless scrolling.

There is a mode for 16x16-pixel cells. When active, each cell will show 2x2 characters, similar to big sprites. This mode also increases the background size to 512x512 pixels. Use the CELL SIZE command to enable it.

For most of the commands and functions that access backgrounds their cell co-ordinates can be outside of the background size (32x32). They will be wrapped around the edges, so for example a character drawn at position 34,-2 will actually appear at position 2,30.

CLS

CLS

Clears both backgrounds with character 0 and resets the current window to the default one.

CLS b

Clears background b with character 0.

PAL

PAL p

Sets the palette number (0-7) attribute for cell and text commands.

PRINT "HELLO"
PAL 1
PRINT "LOWRES NX!"

FLIP

FLIP fx,fy

Sets the values (0 or 1) for horizontal (fx) and vertical (fy) flip attributes for cell and text commands.

PRINT "LOWRES NX"
FLIP 0,1
PRINT "LOWRES NX"
FLIP 1,0
PRINT "XN SERWOL"

PRIO

PRIO p

Sets the priority (0 or 1) attribute for cell and text commands.

ATTR

ATTR a

Sets all attributes for cell and text commands as a single 8-bit value. See "BG Data" for more information.

NOTE: It's easier to use the commands PAL, FLIP and PRIO.

BG

BG n

Sets the current background (0 or 1) for cell and text commands.

CELL

CELL cx,cy,c

Sets the cell at position cx,cy of the current background to character c using the current attributes.

PAL 2
CELL 1,1,225
CELL 2,1,226
PAL 1
CELL 3,1,227

=CELL.C

CELL.C(cx,cy)

Returns the character of the cell at position cx,cy of the current background.

=CELL.A

CELL.A(cx,cy)

Returns the attributes of the cell at position cx,cy of the current background as an 8-bit value. See "BG Data" for more information.

BG FILL

BG FILL cx1,cy1 TO cx2,cy2 CHAR c

Sets all cells in the area from cx1,cy1 to cx2,cy2 of the current background to character c using the current attributes.

PAL 2
BG FILL 2,2 TO 17,13 CHAR 225
PAL 1
BG FILL 10,8 TO 17,13 CHAR 226

TINT

TINT cx,cy [PAL pal]
... [FLIP fx,fy] [PRIO pri]

Changes the palette and/or other attributes of the cell at position cx,cy of the current background. The cell's character and omitted attributes stay unchanged.

BG FILL 2,2 TO 17,13 CHAR 225
TINT 10,8 PAL 1 FLIP 0,1

BG TINT

BG TINT cx1,cy1 TO cx2,cy2
... [PAL pal] [FLIP fx,fy] [PRIO pri]

Changes the palette and/or other attributes of all cells in the area from cx1,cy1 to cx2,cy2 of the current background. The cells' characters and omitted attributes stay unchanged.

BG FILL 2,2 TO 17,13 CHAR 225
BG TINT 10,8 TO 15,11 PAL 1

BG SOURCE

BG SOURCE a[,w,h]

Sets the current source for the BG COPY command. The two-dimensional map starts at memory address a, has a width of w and a height of h cells.

Without the size parameters, Gfx Designer's background data format is assumed: The width is read from address a+2, the height from a+3 and the actual map data starts at a+4.

By default ROM entry 3 is used as source.

'USE A MAP FROM ROM ENTRY 4
BG SOURCE ROM(4)
'USE A MAP FROM WORKING RAM
BG SOURCE $A000,32,32

BG COPY

BG COPY cx1,cy1,w,h TO cx2,cy2

Copies a two-dimensional part of the current source to the current background.

'SHOW SCREEN FROM GFX DESIGNER
BG COPY 0,0,20,16 TO 0,0

BG SCROLL

BG SCROLL cx1,cy1 TO cx2,cy2 STEP dx,dy

Moves the content of all cells in the area from cx1,cy1 to cx2,cy2 horizontally by dx and vertically by dy cells.

DO
  BG SCROLL 0,0 TO 19,15 STEP -1,0
  CELL 18,RND(15),193+RND(62)
  WAIT 5
LOOP

=MCELL.C/A

MCELL.C(cx,cy)
MCELL.A(cx,cy)

Work like the CELL.C and CELL.A functions, but get a cell from the source map (BG SOURCE) instead of the current background. If the co-ordinates are outside of the map bounds, the functions return -1.

MCELL

MCELL cx,cy,c

Works like the CELL command, but sets a cell in the source map (BG SOURCE) instead of the current background. The source must be in working RAM, otherwise you will get an "Illegal Memory Access" error.

TEXT

TEXT cx,cy,s$

Outputs the string s$ to the current background at cell position cx,cy using the current attributes.

PAL 3
TEXT 15,15,"HELLO"

NUMBER

NUMBER cx,cy,n,d

Outputs the number n to the current background at cell position cx,cy using the current attributes. The number is formatted to show always d digits. This command is preferred over TEXT to show numbers, as it doesn't need to convert numbers to strings.

NUMBER 15,0,321,5

FONT

FONT c

Sets the current character range used for text output. c is the character where the font starts (space).

The default value is 192, which points to the standard font, if available.

SCROLL

SCROLL b,x,y

Sets the scroll offset of background b (0/1) to pixel co-ordinates x,y.

TEXT 0,7,"SCROLLING BACKGROUND"
FOR I=0 TO 256
  SCROLL 0,I,0
  WAIT VBL
NEXT I

=SCROLL.X/Y

SCROLL.X(b)
SCROLL.Y(b)

Return the scroll offset of background b.

Display Settings

SPRITE VIEW ON/OFF

SPRITE VIEW ON
SPRITE VIEW OFF

Shows or hides the complete sprite layer without changing any of the sprites' settings.

BG VIEW ON/OFF

BG VIEW ON n
BG VIEW OFF n

Shows or hides background n (0 or 1).

CELL SIZE

CELL SIZE b,s

Sets the cell size for background b (0 or 1), where s can be:

0 1 character (8x8 pixels)
1 2x2 characters (16x16 pixels)

PALETTE

PALETTE n,[c0],[c1],[c2],[c3]

Sets all four colors of palette n (0-7). Color 0 is only used for palette 0 and shown as the screen's backdrop color. The color parameters can be omitted to keep their current settings. Valid color values are 0-63 and can be calculated like this:

VALUE = RED * 16 + GREEN * 4 + BLUE

RED, GREEN and BLUE are values from 0 to 3.

By default all palettes are read from ROM entry 1.

PRINT "COLORS!"
WAIT 30
PALETTE 0,48,63,63,0
WAIT 30
PALETTE 0,3,,,
WAIT 30
PALETTE 0,12,,,8

=COLOR

COLOR(p,n)

Returns the value of color n (0-3) from palette p (0-7). You can get the RED, GREEN and BLUE values like this:

RED = INT(VALUE / 16)
GREEN = INT(VALUE / 4) MOD 4
BLUE = VALUE MOD 4

ON RASTER CALL/OFF

ON RASTER CALL name

Sets a subprogram which is executed for every screen line before it's drawn. Usually used to change color palettes or scroll offsets to achieve graphical effects. Raster subprograms should be short (see "CPU Cycles").

ON RASTER OFF

Removes the current subprogram.

ON RASTER CALL RAINBOW
PRINT "RAINBOW WRITER"
INPUT A$

SUB RAINBOW
  PALETTE 0,(RASTER+TIMER) MOD 64,,,
END SUB

=RASTER

RASTER

Returns the current screen line (y position). Use this in a raster subprogram.

ON VBL CALL/OFF

ON VBL CALL name

Sets a subprogram which is executed each frame. Can be used to update animations or sounds, even if the main program is blocked by WAIT or INPUT. VBL subprograms should not be very long (see "CPU Cycles").

ON VBL OFF

Removes the current subprogram.

ON VBL CALL BLINK
PRINT "BLINK WRITER"
INPUT A$

SUB BLINK
  PALETTE 0,TIMER MOD 64,,,
END SUB

=TIMER

TIMER

Returns the number of frames shown since LowRes NX was started. The value wraps to 0 when 5184000 is reached, which is about 24 hours.

Sound

LowRes NX has four independent sound generators (voices). Each one can play sawtooth, triangle, pulse and noise waveforms, and has frequency, volume and pulse width settings. An additional envelope generator and LFO per voice makes complex sounds and instruments possible.

You can use the tool "Sound Composer" (or compatible programs) to create music, tracks and sound presets.

MUSIC

MUSIC [p]

Starts playback of a song at pattern p. If the parameter p is omitted, it starts at pattern 0.

TRACK

TRACK n,v

Plays track n once on voice v. Each voice can play a track independently, so this can be used for sound effects, even while music is playing.

PLAY

PLAY v,p[,len] [SOUND s]

Plays a sound on voice v (0-3). p is the pitch:

NotePitch (with different octaves)
C113253749617385
C#214263850627486
D315273951637587
D#416284052647688
E517294153657789
F618304254667890
F#719314355677991
G820324456688092
G#921334557698193
A1022344658708294
A#1123354759718395
B1224364860728496

The optional parameter len is the length in 1/60 seconds, the maximum is 255. 0 means, that the sound won't stop automatically. If the parameter is omitted, the current value of the voice is kept.

By default the current sound settings of the voice are used. Add the SOUND parameter to use the sound number s from the Sound Composer tool.

PLAY 0,49,20
WAIT 30
PLAY 0,53,20
WAIT 30
PLAY 0,56,40
WAIT 60

STOP

STOP [v]

Stops the current sound and track on voice v. If the parameter is omitted, all voices, tracks and music are stopped. If a voice's envelope has a release time, the sound won't stop immediately, but fade out.

VOLUME

VOLUME v,[vol],[mix]

Sets the volume of voice n (0-3) to vol (0-15) and its outputs to mix (0-3):

0Muted
1Left
2Right
3Left and right (center)

All parameters can be omitted to keep their current settings.

SOUND

SOUND v,[w],[pw],[len]

Sets the basic sound parameters of voice v (0-3).

w is the waveform:

0Sawtooth
1Triangle
2Pulse
3Noise

pw is the pulse width (0-15), a value of 8 results in a square wave. This parameter only has an effect for the pulse waveform.

len is the sound length in 1/60 seconds, the maximum is 255. 0 means, that the sound won't stop automatically. If the length is set using this command, the length parameter of PLAY can be omitted.

All parameters can be omitted to keep their current settings.

ENVELOPE

ENVELOPE v,[a],[d],[s],[r]

Sets the volume envelope generator of voice v (0-3).

a is the attack time, d is the decay time, and r is the release time. All times are non-linear and range from 0 (2 ms) to 15 (12 s)

s is the sustain level (0-15), which is the volume after the decay time and before the sound gets released.

All parameters can be omitted to keep their current settings.

ENVELOPE 0,1,6,8,8
PLAY 0,49,20
WAIT 30
PLAY 0,53,20
WAIT 30
PLAY 0,56,40
WAIT 120

LFO

LFO v,[r],[fr],[vol],[pw]

Sets the LFO (low frequency oscillator) of voice v (0-3).

r is the LFO rate and ranges from 0 (0.12 Hz) to 15 (18 Hz) in a non-linear manner.

The other paramters set the amount of the effect on different sound parameters: fr for frequency/pitch, vol for volume and pw for pulse width. These values range from 0 to 15.

All parameters can be omitted to keep their current settings.

LFO 0,12,4,0,0
PLAY 0,49,20
WAIT 30
PLAY 0,53,20
WAIT 30
PLAY 0,56,40
WAIT 120

LFO WAVE

LFO WAVE v,[w],[i],[e],[t]

Sets options for the LFO wave of voice v (0-3). All parameters can be omitted to keep their current settings.

w wave (0-3):
0: triangle
1: sawtooth
2: square
3: random
i invert (0/1)
e env mode enabled (0/1)
t trigger enabled (0/1)

By default the LFO adds its output to the normal sound parameters. If invert is enabled, it subtracts. By enabling the env mode, the LFO stops after one cycle, so it can be used as an additional envelope generator. If the trigger is enabled, the LFO restarts for each played sound, otherwise it runs continuously. Trigger is enabled implicitly with the env mode.

SOUND SOURCE

SOUND SOURCE a

Sets the current data source for the PLAY, MUSIC and TRACK commands to the memory address a. This only affects the following calls to these commands, already started playback keeps its own data source. The data is assumed to be in the format of the Sound Composer tool.

By default ROM entry 15 is used as source.

=MUSIC

MUSIC(n)

Returns the status of playing music. Specify n for the information you want to get:

0The current pattern
1The current row
2The current tick
3The current speed (0 = stopped)

Data

DATA

DATA constant-list

Stores comma separated numeric and string constants (values, but no variables or expressions) that are accessed by the READ command. DATA commands are not executed and may be placed anywhere in the program.

READ commands access DATA in order, from the top of a program until the bottom. All constants of all DATA commands are read as one continuous list of items.

READ

READ var-list

Reads values from DATA commands and assigns them to the comma separated variables in var-list. The program has an internal pointer to the current DATA value. With each value read, the pointer will move to the next DATA value.

FOR I=0 TO 3
  READ TYPE$,POWER
  PRINT TYPE$;":",POWER
NEXT I
DATA "LASER",10,"BLASTER",15
DATA "PLASMA",20,"FUSION",30

RESTORE

RESTORE [label]

Changes the internal read pointer to another position. This allows to reread data or to select specific data. If the label parameter is omitted, READ will start again from the top of the program. Otherwise the pointer will be set to the jump label.

RESTORE SHIELDS
FOR I=0 TO 1
  READ TYPE$,POWER
  PRINT TYPE$;":",POWER
NEXT I
DATA "LASER",10,"BLASTER",15
SHIELDS:
DATA "SIMPLE",30,"ADVANCED",60

Memory Access

LowRes NX simulates chips for graphics, sound and I/O, the cartridge ROM, working RAM and persistent RAM. Everything is accessible in a 64 KB memory map, which is described in the chapter "Hardware Reference".

=PEEK

PEEK(a)

Returns the byte value (0-255) at memory address a.

TOUCHSCREEN
PRINT "TOUCH!"
DO
  IF PEEK($FF75) AND %10 THEN
    PRINT PEEK($FF72),PEEK($FF73)
  END IF
  WAIT VBL
LOOP

POKE

POKE a,v

Sets the memory at address a to value v. v is a numeric expression from 0 to 255; numeric expressions outside this range are truncated to 8 bits.

POKE $9000,232
POKE $9002,233

=PEEKW

PEEKW(a)

Returns the two-byte value (-32768 to 32767) at memory address a.

POKEW

POKEW a,v

Writes a two-byte value at memory address a. v is a numeric expression from -32768 to 32767; numeric expressions outside this range are truncated to 16 bits.

=PEEKL

PEEKL(a)

Returns the four-byte value (-2147483648 to 2147483647) at memory address a.

POKEL

POKEL a,v

Writes a four-byte value at memory address a. v is a numeric expression from -2147483648 to 2147483647; numeric expressions outside this range are truncated to 32 bits.

'EXAMPLE USES PERSISTENT RAM
HI=PEEKL($E000)
PRINT "HIGHSCORE:"
PRINT HI
INPUT "SCORE:";SC
IF SC>HI THEN POKEL $E000,SC
PRINT "RESTART!"

COPY

COPY a,n TO d

Copies n bytes starting from memory address a to address d. The source and the destination areas may overlap.

'COPY CHARACTERS FROM ROM
'ENTRY 4 TO VIDEO RAM
COPY ROM(4),SIZE(4) TO $8000

FILL

FILL a,n[,v]

Sets n bytes starting from memory address a to value v, or 0 if the parameter is omitted.

ROL

ROL a,n

Takes the byte at address a and rotates its bits left by n places.

ROR

ROR a,n

Takes the byte at address a and rotates its bits right by n places.

=ROM

ROM(n)

Returns the memory address of ROM entry n.

=SIZE

SIZE(n)

Returns the number of bytes of ROM entry n.

Files

The file commands can be used to store data on a virtual disk, which can contain up to 16 files. Its format is the same as the ROM entries part in a program file. This makes it possible to use any NX program directly as a virtual disk to edit its data.

Virtual disks are meant to be used for development tools only, for example image and map editors or music programs. Games should use persistent memory instead. Imagine that the standard LowRes NX console wouldn't have a disk drive.

LOAD

LOAD f,a[,n[,o]]

Loads the file number f from the current virtual disk to memory starting at address a.

Optionally the parameter n can be used for the maximum number of bytes that should be loaded. 0 means no limit. With the optional parameter o an offset in the file can be set.

LOAD is meant to be used for tools only. Use ROM entries for game data or persistent memory for game states.

SAVE

SAVE f,c$,a,n

Saves n bytes starting at memory address a to the current virtual disk as a file number f (0-15) with comment c$ (up to 31 characters).

If this file was loaded before, consider keeping its original comment or allow the user to edit it before saving. If the file is new, the comment should contain at least the type of data, e.g. "CHARACTERS" or "MUSIC".

SAVE is meant to be used for tools only. Use persistent memory to store game states.

FILES

FILES

Loads the current file directory for use with FILE$.

=FILE$

FILE$(f)

Returns the comment string of file number f. Call FILES before accessing the file directory to update its content, or use FILE$ directly after LOAD or SAVE.

FILES
FOR I=0 TO 15
  PRINT I,FILE$(I)
NEXT I

=FSIZE

FSIZE(n)

Returns the number of bytes of file number n. Call FILES before accessing the file directory to update its content, or use FSIZE directly after LOAD or SAVE.

Math Functions

Trigonometric

=PI

PI

PI is the ratio of the circumference of a circle to its diameter: 3.1415926535...

=SIN

SIN(x)

The sine of x, where x is in radians.

=COS

COS(x)

The cosine of x, where x is in radians.

=TAN

TAN(x)

The tangent of x, where x is in radians.

=ASIN

ASIN(x)

The arc sine of x, where x must be in the range of -1 to +1. The range of the function is -(PI/2) <= ASIN(x) <= (PI/2).

=ACOS

ACOS(x)

The arc cosine of x, where x must be in the range of -1 to +1. The range of the function is 0 <= ACOS(x) <= PI.

=ATAN

ATAN(x)

The arctangent of x in radians, i.e. the angle whose tangent is x. The range of the function is -(PI/2) <= ATAN(x) <= (PI/2).

=HSIN

HSIN(x)

The hyperbolic sine of x.

=HCOS

HCOS(x)

The hyperbolic cosine of x.

=HTAN

HTAN(x)

The hyperbolic tangent of x.

Standard Math

=ABS

ABS(x)

The absolute value of x.

=SGN

SGN(x)

The sign of x: -1 if x < 0, 0 if x = 0 and +1 if x > 0.

=INT

INT(x)

The largest integer not greater than x; e.g. INT(1.3) = 1 and INT(-1.3) = -2.

=EXP

EXP(x)

The exponential of x, i.e. the value of the base of natural logarithms (e = 2,71828...) raised to the power x.

=LOG

LOG(x)

The natural logarithm of x; x must be greater than zero.

=SQR

SQR(x)

The non-negative square root of x; x must be non-negative.

Random Sequences

=RND

RND

The next number in a sequence of random numbers uniformly distributed in the range 0 <= RND < 1.

RND(n)

The second syntax generates a random integer between 0 and n inclusive.

RANDOMIZE

RANDOMIZE x

Sets the seed for random numbers to x, which should be an integer value. By default a program starts with seed 0, so the sequence of random numbers is always the same.

RANDOMIZE TIMER

If you want different random numbers each time you run your program, you should insert this line at the beginning.

RANDOMIZE TIMER
FOR I=1 TO 16
  PRINT RND(1000)
NEXT I

Manipulating Numbers

=MIN

MIN(x,y)

The MIN function returns the smallest value of two expressions.

=MAX

MAX(x,y)

The MAX function returns the largest value of two expressions.

SWAP

SWAP var1,var2

Swaps the data between any two variables of the same type.

A=10
B=40
SWAP A,B
PRINT A
PRINT B

INC

INC var

Increases the value of the variable by one. INC A does the same as A=A+1, but costs less CPU cycles.

DEC

DEC var

Decreases the value of the variable by one. DEC A does the same as A=A-1, but costs less CPU cycles.

ADD

ADD var,x

Adds the value x to the variable, where x can the positive or negative. ADD A,X does the same as A=A+X, but costs less CPU cycles.

ADD var,x,base TO top

The second syntax of ADD helps with repeating counters.

A=0
DO
  ADD A,2,0 TO 10
  PRINT A
LOOP

It's the same as:

A=0
DO
  A=A+2
  IF A>10 THEN A=0
  IF A<0 THEN A=10
  PRINT A
LOOP

But again the ADD command costs less CPU cycles.

String Functions

=LEFT$=

LEFT$(s$,n)

Returns a new string with the first n characters of s$.

PRINT LEFT$("LOWRES NX",3)
LEFT$(s$,n)=a$

Overwrites the first characters in the variable s$ with the first n characters of a$.

A$="FOORES NX"
LEFT$(A$,3)="LOWER"
PRINT A$

=RIGHT$=

RIGHT$(s$,n)

Returns a new string with the last n characters of s$.

RIGHT$(s$,n)=a$

Overwrites the last characters in the variable s$ with the last n characters of a$.

=MID$=

MID$(s$,p,n)

Returns a new string with n characters of s$, starting at character p. The first character has the position 1.

PRINT MID$("LOWRES NX",4,3)
MID$(s$,p,n)=a$

Overwrites the given text range in the variable s$ with the first n characters of a$.

A$="LOWFOO NX"
MID$(A$,4,3)="RESTAURANT"
PRINT A$

=INSTR

INSTR(d$,s$[,p])

Searches the first occurrence of s$ inside of d$ and returns its start position. If it's not found, the function returns 0. Usually the function starts searching at the beginning of the string. Optionally it can start searching at position p.

PRINT INSTR("LOWRES NX","RES")

=CHR$

CHR$(n)

Returns a string containing one character with ASCII code n.

FOR I=32 TO 90
  PRINT CHR$(I)
NEXT I

=ASC

ASC(a$)

Supplies you with the ASCII code of the first character of a$.

PRINT ASC("L")

=LEN

LEN(a$)

Returns the number of characters in a$.

=VAL

VAL(a$)

Converts a number written in a$ into a numeric value.

=STR$

STR$(n)

Converts the number n into a string.

=BIN$

BIN$(n[,len])

Converts the number n into a binary string with at least len digits.

=HEX$

HEX$(n[,len])

Converts the number n into a hexadecimal string with at least len digits.

System

System commands are a link between the virtual console and the LowRes NX application.

TRACE

TRACE expression-list

Outputs text to the debugging window. Expressions can be strings or numbers, separated by commas. This command is ignored if the debug mode is not enabled.

'RUN IN DEBUG MODE
A=13
B$="GO"
TRACE "TEST",A,B$

SYSTEM

SYSTEM n,v

Sets the system status n to value v. Currently only one status is available:

0 Energy Saving Mode:
Enable with v different from 0. In this mode the screen refreshes with a very low rate whenever there is no user input. It does not affect the virtual CPU, the code still runs at full speed.

Advanced Topics

CPU Cycles

LowRes NX has a simplified simulation of CPU cycles. There is a fixed limit of cycles per frame. This assures the same program execution speed on all devices, so if you optimize your program on your device to run smoothly, it will run the same on all other devices.

Each execution of a command, function or operator, as well as access to a variable or a constant count 1 cycle. Some operations have additional costs:

  • String creation and modification count 1 cycle per letter.
  • Array initialization counts 1 cycle per element.
  • Memory area modification counts 1 cycle per byte (not single byte modifications like POKE).
  • BG area modification and text output count 2 cycles per cell (not single cell modifications like CELL).
Total cycles per frame17556
Cycles per VBL interrupt1140
Cycles per raster interrupt51

The main program may spend any number of cycles, but when the limit is reached before a WAIT VBL or WAIT command, the execution continues in the next frame. If interrupts exceed their limit, you will see black scanlines on the screen.

Hardware Reference

Memory Map

$0000 - Cartridge ROM (32 KB)

$8000 - Character Data (4 KB)
$9000 - BG0 Data (2 KB)
$9800 - BG1 Data (2 KB)

$A000 - Working RAM (16 KB)

$E000 - Persistent RAM (4 KB)

$FE00 - Sprite Registers (256 B)
$FF00 - Color Registers (32 B)
$FF20 - Video Registers
$FF40 - Audio Registers
$FF70 - I/O Registers

Character Data

A character is an 8x8-pixel image with 2 bits per pixel, with a resulting size of 16 bytes. The video RAM has space for 256 characters.

The first 8 bytes of a character contain the low bits of all its pixels, followed by 8 more bytes containing the high bits of all pixels.

BG Data

A background is a map of 32x32 character cells. Each cell occupies two bytes:

- Character number
- Attributes:
    Bit  Purpose
    0-2  Palette number
    3    Flip X
    4    Flip Y
    5    Priority
    6-7  Unused

Persistent RAM

Imagine it as a battery buffered RAM on the game cartridge. Use it for data like game positions or high score tables. The content of the persistent RAM will be saved automatically when you exit the program and loaded when you run it. Each program saves its persistent RAM separately.

Sprite Registers

There are 64 sprites available, each occupies 4 bytes:

- X position
- Y position
- Character number
- Attributes:
    Bit  Purpose
    0-2  Palette number
    3    Flip X
    4    Flip Y
    5    Priority
    6-7  Size:
         0: 1 character (8x8 px)
         1: 2x2 characters (16x16 px)
         2: 3x3 characters (24x24 px)
         3: 4x4 characters (32x32 px)

Note: X and Y sprite position registers have an offset of 32, so they can move out of the top/left screen borders without using negative numbers. Using the BASIC commands, this offset is removed for convenience.

Color Registers

There are 8 palettes of each 4 colors. One color is one byte:

Bits  Component
0-1   Blue
2-3   Green
4-5   Red

Video Registers

$FF20 - Attributes:
    Bit  Purpose
    0    Sprites enabled
    1    BG0 enabled
    2    BG1 enabled
    3    BG0 cell size,
    4    BG1 cell size:
         0: 1 character (8x8 px)
            (BG 256x256 px)
         1: 2x2 characters (16x16 px)
            (BG 512x512 px)

$FF21 - BG0 scroll offset X
$FF22 - BG0 scroll offset Y
$FF23 - BG1 scroll offset X
$FF24 - BG1 scroll offset Y
$FF25 - Scroll offset MSB
    (most significant bits)
    used for big cell size only:
    Bit  Purpose
    0    BG0 X+256
    1    BG0 Y+256
    2    BG1 X+256
    3    BG1 Y+256

$FF26 - Raster line

Audio Registers

There are registers for 4 voices:

$FF40 - Voice 0
$FF4C - Voice 1
$FF58 - Voice 2
$FF64 - Voice 3

Each voice occupies 12 bytes:

- Frequency low-byte
- Frequency high-byte
- Status:
    Bit  Purpose
    0-3  Volume
    4    Mix to left
    5    Mix to right
    6    Init
    7    Gate
- Peak meter (read only)
- Attributes:
    Bit  Purpose
    0-3  Pulse width
    4-5  Wave:
         0: Sawtooth
         1: Triangle
         2: Pulse
         3: Noise
    6    Timeout enabled
- Length (timeout)
- Envelope byte 1:
    Bit  Purpose
    0-3  Attack
    4-7  Decay
- Envelope byte 2:
    Bit  Purpose
    0-3  Sustain
    4-7  Release
- LFO attributes:
    Bit  Purpose
    0-1  Wave:
         0: Triangle
         1: Sawtooth
         2: Square
         3: Random
    2    Invert
    3    Env mode enabled
    4    Trigger enabled
- LFO settings byte 1:
    Bit  Purpose
    0-3  LFO Rate
    4-7  Frequency amount
- LFO settings byte 2:
    Bit  Purpose
    0-3  Volume amount
    4-7  Pulse width amount
- Reserved

Note: The frequency is a 16-bit value: f = hertz * 16

I/O Registers

$FF70 - Gamepad 0 status
$FF71 - Gamepad 1 status

Gamepad status:
    Bit  Purpose
    0    Up
    1    Down
    2    Left
    3    Right
    4    Button A
    5    Button B

$FF72 - Last touch X position
$FF73 - Last touch Y position
$FF74 - Last pressed key (ASCII code)
$FF75 - Status:
    Bit  Purpose
    0    Pause button
    1    Touch

$FF76 - Attributes:
    Bit  Purpose
    0-1  Gamepads enabled:
         0: off
         1: 1 player
         2: 2 players
    2    Keyboard enabled
    3    Touchscreen enabled

Sound Data Format

This is the format used for the PLAY, MUSIC and TRACK commands. It's valid to store only the sound presets, if no MUSIC or TRACK commands are used with this data. If any tracks are available, all patterns must be stored. Empty tracks after the last used one don't need to be stored.

Offset - Content
0      - 16 sound presets
128    - 64 patterns
384    - 64 tracks

Each sound preset occupies 8 bytes and matches the format of the audio registers of one voice, but without the first 4 bytes.

Each pattern occupies 4 bytes:

- Voice 0:
    Bit  Purpose
    0-6  Track index
         (64 = voice unused)
    7    Flag loop start
- Voice 1:
    Bit  Purpose
    0-6  Track index
         (64 = voice unused)
    7    Flag loop end
- Voice 2:
    Bit  Purpose
    0-6  Track index
         (64 = voice unused)
    7    Flag song stop
- Voice 3:
    Bit  Purpose
    0-6  Track index
         (64 = voice unused)

Each track occupies 96 bytes and consists of 32 entries with each 3 bytes:

- Note pitch (0 = empty)
- Data (ignored if note is 0):
    Bit  Purpose
    0-3  Volume
    4-7  Sound
- Control:
    Bit  Purpose
    0-3  Parameter
    4-7  Command

Obsolete Syntaxes

These commands, functions and syntaxes should not be used anymore and may be removed in future versions.

SPRITE.A n,(pal,fx,fy,pri,s)

Use SPRITE (new syntax) or SPRITE.A (single value only) instead.

ATTR (pal,fx,fy,pri,s)[,m]
ATTR a,m

Use PAL, FLIP, PRIO or ATTR (single value only) instead. Use TINT and BG TINT for replacing attribute masks.

DISPLAY (s,b0,b1,c0,c1)
DISPLAY a

Use BG VIEW ON/OFF, SPRITE VIEW ON/OFF and CELL SIZE instead.

DISPLAY

Use PEEK($FF20) instead.

LFO.A v,(w,r,e,t)

Use LFO WAVE instead.

Reserved Keywords

The following is a list of reserved words used in LowRes NX BASIC. If you use these words as variable names, a syntax error will be generated.

Keywords marked with an asterisk have no function yet, but are reserved for future versions.

ABS, ACOS, ADD, AND, ANIM*, ASC, ASIN, ATAN, ATTR, BG, BIN$, BUTTON, CALL, CELL, CELL.A, CELL.C, CHAR, CHR$, CLOSE*, CLS, CLW, COLOR, COPY, COS, DATA, DEC, DECLARE*, DEF*, DIM, DISPLAY, DO, DOWN, ELSE, END, ENVELOPE, EXIT, EXP, FILE$, FILES, FILL, FLASH*, FLIP, FN*, FONT, FOR, FSIZE, FUNCTION*, GAMEPAD, GLOBAL, GOSUB, GOTO, HCOS, HEX$, HIT, HSIN, HTAN, IF, INC, INKEY$, INPUT, INSTR, INT, KEYBOARD, LBOUND*, LEFT$, LEFT, LEN, LET, LFO, LFO.A, LOAD, LOCATE, LOG, LOOP, MAX, MCELL, MCELL.A, MCELL.C, MID$, MIN, MOD, MUSIC, NEXT, NOT, NUMBER, OFF, ON, OPEN*, OPTIONAL, OR, OUTPUT*, PAL, PALETTE, PAUSE, PEEK, PEEKL, PEEKW, PI, PLAY, POKE, POKEL, POKEW, PRINT, PRIO, RANDOMIZE, RASTER, READ, REM, REPEAT, RESTORE, RETURN, RIGHT$, RIGHT, RND, ROL, ROM, ROR, SAVE, SCROLL, SCROLL.X, SCROLL.Y, SGN, SHARED*, SIN, SIZE, SOUND, SOURCE, SPRITE, SPRITE.A, SPRITE.C, SPRITE.X, SPRITE.Y, SQR, STATIC*, STEP, STOP, STR$, SUB, SWAP, SYSTEM, TAN, TAP, TEMPO*, TEXT, THEN, TIMER, TINT, TO, TOUCH, TOUCH.X, TOUCH.Y, TOUCHSCREEN, TRACE, TRACK, UBOUND, UNTIL, UP, VAL, VBL, VIEW, VOICE*, VOLUME, WAIT, WAVE, WEND, WHILE, WINDOW, WRITE*, XOR

================================================ FILE: docs/readme.txt ================================================ *************** * First Steps * *************** Please read the introduction (at least "Getting Started") of the manual. ************ * Controls * ************ - Use any real gamepad or the keyboard: Button Player 1 Player 2 -------+-----------+-------- UP Arrow Up E DOWN Arrow Down D LEFT Arrow Left S RIGHT Arrow Right F A Z/N Q/Tab B X/M A/Shift - More keys: Dev Menu Esc Pause Return/P Fullscreen Ctrl+f Zoom Mode Ctrl+z Screenshot large Ctrl+s original Ctrl+Shift+s Debug Ctrl+d Reload/Run Ctrl+r Eject Ctrl+e Volume up Ctrl+Plus Volume down Ctrl+Minus Quit Esc (if disabledev) ************ * Settings * ************ - Settings file: A default settings file is created on application start, if none exists yet. Available options are the same as for command line arguments, but each one is written in its own line and without the leading "-" character. macOS: /Users/YourName/Library/Application Support/Inutilis Software/LowRes NX/settings.txt Windows: C:\Users\YourName\AppData\Roaming\Inutilis Software\LowRes NX\settings.txt - Command line arguments: These override the options from the settings file. "LowRes NX" [-option value] [program.nx] -fullscreen yes/no Start the application in fullscreen mode -zoom 0-3 Start the application in zoom mode: 0 = pixel perfect, 1 = large, 2 = overscan, 3 = squeeze. -disabledev yes/no Disable the Development Menu, Esc key quits LowRes NX -mapping 0-1 Set the key mapping. 0 is standard, 1 is GameShell. -disabledelay yes/no Disable the delay for too short frames. program.nx Name of the program to run ********* * Notes * ********* - Share programs and discuss on: https://lowresnx.inutilis.com - Development news on Twitter: @timo_inutilis - Tweet with #LowResNX. - To-do and bug list on: https://github.com/timoinutilis/lowres-nx/issues - Write to timo@inutilis.com. ================================================ FILE: extras/LowRes NX.sublime-syntax ================================================ %YAML 1.2 --- # See http://www.sublimetext.com/docs/3/syntax.html file_extensions: - nx name: LowRes NX scope: source.lowres-nx contexts: main: # Strings - match: '"' scope: punctuation.definition.string.begin.lowres-nx push: double_quoted_string # Comments begin with 'REM', # or ' and finish at the end of the line - match: '^\s*(REM|#|'')' scope: punctuation.definition.comment.lowres-nx push: line_comment # Keywords - match: '\b(ABS|ACOS|ADD|ASC|ASIN|ATAN|ATTR|BG|BIN|BUTTON|CALL|CELL\.A|CELL\.C|CELL|CHAR|CHR|CLS|CLW|COLOR|COPY|COS|DATA|DEC|DIM|DISPLAY|DOWN|DO|ELSE|END|ENVELOPE|EXIT|EXP|FILE|FILES|FILL|FONT|FOR|FSIZE|GAMEPAD|GLOBAL|GOSUB|GOTO|HEX|HCOS|HIT|HSIN|HTAN|IF|INC|INKEY|INPUT|INSTR|INT|KEYBOARD|LEFT|LEN|LET|LFO\.A|LFO|LOAD|LOCATE|LOG|LOOP|MAX|MID|MIN|NEXT|NUMBER|OFF|ON|PALETTE|PAUSE|PEEK|PEEKL|PEEKW|PI|PLAY|POKE|POKEL|POKEW|PRINT|RANDOMIZE|RASTER|READ|REPEAT|RESTORE|RETURN|RIGHT|RND|ROL|ROM|ROR|SAVE|SCROLL\.X|SCROLL\.Y|SCROLL|SGN|SIN|SIZE|SOUND|SOURCE|SPRITE\.A|SPRITE\.C|SPRITE\.X|SPRITE\.Y|SPRITE|SQR|STEP|STOP|STR|SUB|SWAP|SYSTEM|TAN|TAP|TEXT|THEN|TIMER|TO|TOUCH\.X|TOUCH\.Y|TOUCH|TRACE|UNTIL|UP|VAL|VBL|VOLUME|WAIT|WEND|WHILE|WINDOW|PAL|FLIP|PRIO|VIEW|WAVE)\b' scope: keyword.control.lowres-nx # Numbers - match: '\b(-)?[0-9.]+\b' scope: constant.numeric.lowres-nx # Operators - match: '\b(AND|OR|XOR|NOT|MOD)\b' scope: keyword.operator.word.lowres-nx - match: <\=|>\=|\=|<|>|<> scope: keyword.operator.comparison.lowres-nx - match: \+|\-|\*|/|\\ scope: keyword.operator.arithmetic.lowres-nx # Punctuation - match: ',' scope: punctuation.separator.lowres-nx double_quoted_string: - meta_scope: string.quoted.double.lowres-nx - match: '\\.' scope: constant.character.escape.lowres-nx - match: '"' scope: punctuation.definition.string.end.lowres-nx pop: true line_comment: - meta_scope: comment.line.lowres-nx - match: $ pop: true ================================================ FILE: libretro/libretro.h ================================================ /* Copyright (C) 2010-2016 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this libretro API header (libretro.h). * --------------------------------------------------------------------------------------- * * Permission is hereby granted, free of charge, * to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef LIBRETRO_H__ #define LIBRETRO_H__ #include #include #include #ifdef __cplusplus extern "C" { #endif #ifndef __cplusplus #if defined(_MSC_VER) && !defined(SN_TARGET_PS3) /* Hack applied for MSVC when compiling in C89 mode * as it isn't C99-compliant. */ #define bool unsigned char #define true 1 #define false 0 #else #include #endif #endif #ifndef RETRO_CALLCONV # if defined(__GNUC__) && defined(__i386__) && !defined(__x86_64__) # define RETRO_CALLCONV __attribute__((cdecl)) # elif defined(_MSC_VER) && defined(_M_X86) && !defined(_M_X64) # define RETRO_CALLCONV __cdecl # else # define RETRO_CALLCONV /* all other platforms only have one calling convention each */ # endif #endif #ifndef RETRO_API # if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) # ifdef RETRO_IMPORT_SYMBOLS # ifdef __GNUC__ # define RETRO_API RETRO_CALLCONV __attribute__((__dllimport__)) # else # define RETRO_API RETRO_CALLCONV __declspec(dllimport) # endif # else # ifdef __GNUC__ # define RETRO_API RETRO_CALLCONV __attribute__((__dllexport__)) # else # define RETRO_API RETRO_CALLCONV __declspec(dllexport) # endif # endif # else # if defined(__GNUC__) && __GNUC__ >= 4 && !defined(__CELLOS_LV2__) # define RETRO_API RETRO_CALLCONV __attribute__((__visibility__("default"))) # else # define RETRO_API RETRO_CALLCONV # endif # endif #endif /* Used for checking API/ABI mismatches that can break libretro * implementations. * It is not incremented for compatible changes to the API. */ #define RETRO_API_VERSION 1 /* * Libretro's fundamental device abstractions. * * Libretro's input system consists of some standardized device types, * such as a joypad (with/without analog), mouse, keyboard, lightgun * and a pointer. * * The functionality of these devices are fixed, and individual cores * map their own concept of a controller to libretro's abstractions. * This makes it possible for frontends to map the abstract types to a * real input device, and not having to worry about binding input * correctly to arbitrary controller layouts. */ #define RETRO_DEVICE_TYPE_SHIFT 8 #define RETRO_DEVICE_MASK ((1 << RETRO_DEVICE_TYPE_SHIFT) - 1) #define RETRO_DEVICE_SUBCLASS(base, id) (((id + 1) << RETRO_DEVICE_TYPE_SHIFT) | base) /* Input disabled. */ #define RETRO_DEVICE_NONE 0 /* The JOYPAD is called RetroPad. It is essentially a Super Nintendo * controller, but with additional L2/R2/L3/R3 buttons, similar to a * PS1 DualShock. */ #define RETRO_DEVICE_JOYPAD 1 /* The mouse is a simple mouse, similar to Super Nintendo's mouse. * X and Y coordinates are reported relatively to last poll (poll callback). * It is up to the libretro implementation to keep track of where the mouse * pointer is supposed to be on the screen. * The frontend must make sure not to interfere with its own hardware * mouse pointer. */ #define RETRO_DEVICE_MOUSE 2 /* KEYBOARD device lets one poll for raw key pressed. * It is poll based, so input callback will return with the current * pressed state. * For event/text based keyboard input, see * RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK. */ #define RETRO_DEVICE_KEYBOARD 3 /* Lightgun X/Y coordinates are reported relatively to last poll, * similar to mouse. */ #define RETRO_DEVICE_LIGHTGUN 4 /* The ANALOG device is an extension to JOYPAD (RetroPad). * Similar to DualShock it adds two analog sticks. * This is treated as a separate device type as it returns values in the * full analog range of [-0x8000, 0x7fff]. Positive X axis is right. * Positive Y axis is down. * Only use ANALOG type when polling for analog values of the axes. */ #define RETRO_DEVICE_ANALOG 5 /* Abstracts the concept of a pointing mechanism, e.g. touch. * This allows libretro to query in absolute coordinates where on the * screen a mouse (or something similar) is being placed. * For a touch centric device, coordinates reported are the coordinates * of the press. * * Coordinates in X and Y are reported as: * [-0x7fff, 0x7fff]: -0x7fff corresponds to the far left/top of the screen, * and 0x7fff corresponds to the far right/bottom of the screen. * The "screen" is here defined as area that is passed to the frontend and * later displayed on the monitor. * * The frontend is free to scale/resize this screen as it sees fit, however, * (X, Y) = (-0x7fff, -0x7fff) will correspond to the top-left pixel of the * game image, etc. * * To check if the pointer coordinates are valid (e.g. a touch display * actually being touched), PRESSED returns 1 or 0. * * If using a mouse on a desktop, PRESSED will usually correspond to the * left mouse button, but this is a frontend decision. * PRESSED will only return 1 if the pointer is inside the game screen. * * For multi-touch, the index variable can be used to successively query * more presses. * If index = 0 returns true for _PRESSED, coordinates can be extracted * with _X, _Y for index = 0. One can then query _PRESSED, _X, _Y with * index = 1, and so on. * Eventually _PRESSED will return false for an index. No further presses * are registered at this point. */ #define RETRO_DEVICE_POINTER 6 /* Buttons for the RetroPad (JOYPAD). * The placement of these is equivalent to placements on the * Super Nintendo controller. * L2/R2/L3/R3 buttons correspond to the PS1 DualShock. */ #define RETRO_DEVICE_ID_JOYPAD_B 0 #define RETRO_DEVICE_ID_JOYPAD_Y 1 #define RETRO_DEVICE_ID_JOYPAD_SELECT 2 #define RETRO_DEVICE_ID_JOYPAD_START 3 #define RETRO_DEVICE_ID_JOYPAD_UP 4 #define RETRO_DEVICE_ID_JOYPAD_DOWN 5 #define RETRO_DEVICE_ID_JOYPAD_LEFT 6 #define RETRO_DEVICE_ID_JOYPAD_RIGHT 7 #define RETRO_DEVICE_ID_JOYPAD_A 8 #define RETRO_DEVICE_ID_JOYPAD_X 9 #define RETRO_DEVICE_ID_JOYPAD_L 10 #define RETRO_DEVICE_ID_JOYPAD_R 11 #define RETRO_DEVICE_ID_JOYPAD_L2 12 #define RETRO_DEVICE_ID_JOYPAD_R2 13 #define RETRO_DEVICE_ID_JOYPAD_L3 14 #define RETRO_DEVICE_ID_JOYPAD_R3 15 /* Index / Id values for ANALOG device. */ #define RETRO_DEVICE_INDEX_ANALOG_LEFT 0 #define RETRO_DEVICE_INDEX_ANALOG_RIGHT 1 #define RETRO_DEVICE_ID_ANALOG_X 0 #define RETRO_DEVICE_ID_ANALOG_Y 1 /* Id values for MOUSE. */ #define RETRO_DEVICE_ID_MOUSE_X 0 #define RETRO_DEVICE_ID_MOUSE_Y 1 #define RETRO_DEVICE_ID_MOUSE_LEFT 2 #define RETRO_DEVICE_ID_MOUSE_RIGHT 3 #define RETRO_DEVICE_ID_MOUSE_WHEELUP 4 #define RETRO_DEVICE_ID_MOUSE_WHEELDOWN 5 #define RETRO_DEVICE_ID_MOUSE_MIDDLE 6 #define RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP 7 #define RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN 8 /* Id values for LIGHTGUN types. */ #define RETRO_DEVICE_ID_LIGHTGUN_X 0 #define RETRO_DEVICE_ID_LIGHTGUN_Y 1 #define RETRO_DEVICE_ID_LIGHTGUN_TRIGGER 2 #define RETRO_DEVICE_ID_LIGHTGUN_CURSOR 3 #define RETRO_DEVICE_ID_LIGHTGUN_TURBO 4 #define RETRO_DEVICE_ID_LIGHTGUN_PAUSE 5 #define RETRO_DEVICE_ID_LIGHTGUN_START 6 /* Id values for POINTER. */ #define RETRO_DEVICE_ID_POINTER_X 0 #define RETRO_DEVICE_ID_POINTER_Y 1 #define RETRO_DEVICE_ID_POINTER_PRESSED 2 /* Returned from retro_get_region(). */ #define RETRO_REGION_NTSC 0 #define RETRO_REGION_PAL 1 /* Id values for LANGUAGE */ enum retro_language { RETRO_LANGUAGE_ENGLISH = 0, RETRO_LANGUAGE_JAPANESE = 1, RETRO_LANGUAGE_FRENCH = 2, RETRO_LANGUAGE_SPANISH = 3, RETRO_LANGUAGE_GERMAN = 4, RETRO_LANGUAGE_ITALIAN = 5, RETRO_LANGUAGE_DUTCH = 6, RETRO_LANGUAGE_PORTUGUESE = 7, RETRO_LANGUAGE_RUSSIAN = 8, RETRO_LANGUAGE_KOREAN = 9, RETRO_LANGUAGE_CHINESE_TRADITIONAL = 10, RETRO_LANGUAGE_CHINESE_SIMPLIFIED = 11, RETRO_LANGUAGE_ESPERANTO = 12, RETRO_LANGUAGE_POLISH = 13, RETRO_LANGUAGE_LAST, /* Ensure sizeof(enum) == sizeof(int) */ RETRO_LANGUAGE_DUMMY = INT_MAX }; /* Passed to retro_get_memory_data/size(). * If the memory type doesn't apply to the * implementation NULL/0 can be returned. */ #define RETRO_MEMORY_MASK 0xff /* Regular save RAM. This RAM is usually found on a game cartridge, * backed up by a battery. * If save game data is too complex for a single memory buffer, * the SAVE_DIRECTORY (preferably) or SYSTEM_DIRECTORY environment * callback can be used. */ #define RETRO_MEMORY_SAVE_RAM 0 /* Some games have a built-in clock to keep track of time. * This memory is usually just a couple of bytes to keep track of time. */ #define RETRO_MEMORY_RTC 1 /* System ram lets a frontend peek into a game systems main RAM. */ #define RETRO_MEMORY_SYSTEM_RAM 2 /* Video ram lets a frontend peek into a game systems video RAM (VRAM). */ #define RETRO_MEMORY_VIDEO_RAM 3 /* Keysyms used for ID in input state callback when polling RETRO_KEYBOARD. */ enum retro_key { RETROK_UNKNOWN = 0, RETROK_FIRST = 0, RETROK_BACKSPACE = 8, RETROK_TAB = 9, RETROK_CLEAR = 12, RETROK_RETURN = 13, RETROK_PAUSE = 19, RETROK_ESCAPE = 27, RETROK_SPACE = 32, RETROK_EXCLAIM = 33, RETROK_QUOTEDBL = 34, RETROK_HASH = 35, RETROK_DOLLAR = 36, RETROK_AMPERSAND = 38, RETROK_QUOTE = 39, RETROK_LEFTPAREN = 40, RETROK_RIGHTPAREN = 41, RETROK_ASTERISK = 42, RETROK_PLUS = 43, RETROK_COMMA = 44, RETROK_MINUS = 45, RETROK_PERIOD = 46, RETROK_SLASH = 47, RETROK_0 = 48, RETROK_1 = 49, RETROK_2 = 50, RETROK_3 = 51, RETROK_4 = 52, RETROK_5 = 53, RETROK_6 = 54, RETROK_7 = 55, RETROK_8 = 56, RETROK_9 = 57, RETROK_COLON = 58, RETROK_SEMICOLON = 59, RETROK_LESS = 60, RETROK_EQUALS = 61, RETROK_GREATER = 62, RETROK_QUESTION = 63, RETROK_AT = 64, RETROK_LEFTBRACKET = 91, RETROK_BACKSLASH = 92, RETROK_RIGHTBRACKET = 93, RETROK_CARET = 94, RETROK_UNDERSCORE = 95, RETROK_BACKQUOTE = 96, RETROK_a = 97, RETROK_b = 98, RETROK_c = 99, RETROK_d = 100, RETROK_e = 101, RETROK_f = 102, RETROK_g = 103, RETROK_h = 104, RETROK_i = 105, RETROK_j = 106, RETROK_k = 107, RETROK_l = 108, RETROK_m = 109, RETROK_n = 110, RETROK_o = 111, RETROK_p = 112, RETROK_q = 113, RETROK_r = 114, RETROK_s = 115, RETROK_t = 116, RETROK_u = 117, RETROK_v = 118, RETROK_w = 119, RETROK_x = 120, RETROK_y = 121, RETROK_z = 122, RETROK_DELETE = 127, RETROK_KP0 = 256, RETROK_KP1 = 257, RETROK_KP2 = 258, RETROK_KP3 = 259, RETROK_KP4 = 260, RETROK_KP5 = 261, RETROK_KP6 = 262, RETROK_KP7 = 263, RETROK_KP8 = 264, RETROK_KP9 = 265, RETROK_KP_PERIOD = 266, RETROK_KP_DIVIDE = 267, RETROK_KP_MULTIPLY = 268, RETROK_KP_MINUS = 269, RETROK_KP_PLUS = 270, RETROK_KP_ENTER = 271, RETROK_KP_EQUALS = 272, RETROK_UP = 273, RETROK_DOWN = 274, RETROK_RIGHT = 275, RETROK_LEFT = 276, RETROK_INSERT = 277, RETROK_HOME = 278, RETROK_END = 279, RETROK_PAGEUP = 280, RETROK_PAGEDOWN = 281, RETROK_F1 = 282, RETROK_F2 = 283, RETROK_F3 = 284, RETROK_F4 = 285, RETROK_F5 = 286, RETROK_F6 = 287, RETROK_F7 = 288, RETROK_F8 = 289, RETROK_F9 = 290, RETROK_F10 = 291, RETROK_F11 = 292, RETROK_F12 = 293, RETROK_F13 = 294, RETROK_F14 = 295, RETROK_F15 = 296, RETROK_NUMLOCK = 300, RETROK_CAPSLOCK = 301, RETROK_SCROLLOCK = 302, RETROK_RSHIFT = 303, RETROK_LSHIFT = 304, RETROK_RCTRL = 305, RETROK_LCTRL = 306, RETROK_RALT = 307, RETROK_LALT = 308, RETROK_RMETA = 309, RETROK_LMETA = 310, RETROK_LSUPER = 311, RETROK_RSUPER = 312, RETROK_MODE = 313, RETROK_COMPOSE = 314, RETROK_HELP = 315, RETROK_PRINT = 316, RETROK_SYSREQ = 317, RETROK_BREAK = 318, RETROK_MENU = 319, RETROK_POWER = 320, RETROK_EURO = 321, RETROK_UNDO = 322, RETROK_LAST, RETROK_DUMMY = INT_MAX /* Ensure sizeof(enum) == sizeof(int) */ }; enum retro_mod { RETROKMOD_NONE = 0x0000, RETROKMOD_SHIFT = 0x01, RETROKMOD_CTRL = 0x02, RETROKMOD_ALT = 0x04, RETROKMOD_META = 0x08, RETROKMOD_NUMLOCK = 0x10, RETROKMOD_CAPSLOCK = 0x20, RETROKMOD_SCROLLOCK = 0x40, RETROKMOD_DUMMY = INT_MAX /* Ensure sizeof(enum) == sizeof(int) */ }; /* If set, this call is not part of the public libretro API yet. It can * change or be removed at any time. */ #define RETRO_ENVIRONMENT_EXPERIMENTAL 0x10000 /* Environment callback to be used internally in frontend. */ #define RETRO_ENVIRONMENT_PRIVATE 0x20000 /* Environment commands. */ #define RETRO_ENVIRONMENT_SET_ROTATION 1 /* const unsigned * -- * Sets screen rotation of graphics. * Is only implemented if rotation can be accelerated by hardware. * Valid values are 0, 1, 2, 3, which rotates screen by 0, 90, 180, * 270 degrees counter-clockwise respectively. */ #define RETRO_ENVIRONMENT_GET_OVERSCAN 2 /* bool * -- * Boolean value whether or not the implementation should use overscan, * or crop away overscan. */ #define RETRO_ENVIRONMENT_GET_CAN_DUPE 3 /* bool * -- * Boolean value whether or not frontend supports frame duping, * passing NULL to video frame callback. */ /* Environ 4, 5 are no longer supported (GET_VARIABLE / SET_VARIABLES), * and reserved to avoid possible ABI clash. */ #define RETRO_ENVIRONMENT_SET_MESSAGE 6 /* const struct retro_message * -- * Sets a message to be displayed in implementation-specific manner * for a certain amount of 'frames'. * Should not be used for trivial messages, which should simply be * logged via RETRO_ENVIRONMENT_GET_LOG_INTERFACE (or as a * fallback, stderr). */ #define RETRO_ENVIRONMENT_SHUTDOWN 7 /* N/A (NULL) -- * Requests the frontend to shutdown. * Should only be used if game has a specific * way to shutdown the game from a menu item or similar. */ #define RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL 8 /* const unsigned * -- * Gives a hint to the frontend how demanding this implementation * is on a system. E.g. reporting a level of 2 means * this implementation should run decently on all frontends * of level 2 and up. * * It can be used by the frontend to potentially warn * about too demanding implementations. * * The levels are "floating". * * This function can be called on a per-game basis, * as certain games an implementation can play might be * particularly demanding. * If called, it should be called in retro_load_game(). */ #define RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY 9 /* const char ** -- * Returns the "system" directory of the frontend. * This directory can be used to store system specific * content such as BIOSes, configuration data, etc. * The returned value can be NULL. * If so, no such directory is defined, * and it's up to the implementation to find a suitable directory. * * NOTE: Some cores used this folder also for "save" data such as * memory cards, etc, for lack of a better place to put it. * This is now discouraged, and if possible, cores should try to * use the new GET_SAVE_DIRECTORY. */ #define RETRO_ENVIRONMENT_SET_PIXEL_FORMAT 10 /* const enum retro_pixel_format * -- * Sets the internal pixel format used by the implementation. * The default pixel format is RETRO_PIXEL_FORMAT_0RGB1555. * This pixel format however, is deprecated (see enum retro_pixel_format). * If the call returns false, the frontend does not support this pixel * format. * * This function should be called inside retro_load_game() or * retro_get_system_av_info(). */ #define RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS 11 /* const struct retro_input_descriptor * -- * Sets an array of retro_input_descriptors. * It is up to the frontend to present this in a usable way. * The array is terminated by retro_input_descriptor::description * being set to NULL. * This function can be called at any time, but it is recommended * to call it as early as possible. */ #define RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK 12 /* const struct retro_keyboard_callback * -- * Sets a callback function used to notify core about keyboard events. */ #define RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE 13 /* const struct retro_disk_control_callback * -- * Sets an interface which frontend can use to eject and insert * disk images. * This is used for games which consist of multiple images and * must be manually swapped out by the user (e.g. PSX). */ #define RETRO_ENVIRONMENT_SET_HW_RENDER 14 /* struct retro_hw_render_callback * -- * Sets an interface to let a libretro core render with * hardware acceleration. * Should be called in retro_load_game(). * If successful, libretro cores will be able to render to a * frontend-provided framebuffer. * The size of this framebuffer will be at least as large as * max_width/max_height provided in get_av_info(). * If HW rendering is used, pass only RETRO_HW_FRAME_BUFFER_VALID or * NULL to retro_video_refresh_t. */ #define RETRO_ENVIRONMENT_GET_VARIABLE 15 /* struct retro_variable * -- * Interface to acquire user-defined information from environment * that cannot feasibly be supported in a multi-system way. * 'key' should be set to a key which has already been set by * SET_VARIABLES. * 'data' will be set to a value or NULL. */ #define RETRO_ENVIRONMENT_SET_VARIABLES 16 /* const struct retro_variable * -- * Allows an implementation to signal the environment * which variables it might want to check for later using * GET_VARIABLE. * This allows the frontend to present these variables to * a user dynamically. * This should be called as early as possible (ideally in * retro_set_environment). * * 'data' points to an array of retro_variable structs * terminated by a { NULL, NULL } element. * retro_variable::key should be namespaced to not collide * with other implementations' keys. E.g. A core called * 'foo' should use keys named as 'foo_option'. * retro_variable::value should contain a human readable * description of the key as well as a '|' delimited list * of expected values. * * The number of possible options should be very limited, * i.e. it should be feasible to cycle through options * without a keyboard. * * First entry should be treated as a default. * * Example entry: * { "foo_option", "Speed hack coprocessor X; false|true" } * * Text before first ';' is description. This ';' must be * followed by a space, and followed by a list of possible * values split up with '|'. * * Only strings are operated on. The possible values will * generally be displayed and stored as-is by the frontend. */ #define RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE 17 /* bool * -- * Result is set to true if some variables are updated by * frontend since last call to RETRO_ENVIRONMENT_GET_VARIABLE. * Variables should be queried with GET_VARIABLE. */ #define RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME 18 /* const bool * -- * If true, the libretro implementation supports calls to * retro_load_game() with NULL as argument. * Used by cores which can run without particular game data. * This should be called within retro_set_environment() only. */ #define RETRO_ENVIRONMENT_GET_LIBRETRO_PATH 19 /* const char ** -- * Retrieves the absolute path from where this libretro * implementation was loaded. * NULL is returned if the libretro was loaded statically * (i.e. linked statically to frontend), or if the path cannot be * determined. * Mostly useful in cooperation with SET_SUPPORT_NO_GAME as assets can * be loaded without ugly hacks. */ /* Environment 20 was an obsolete version of SET_AUDIO_CALLBACK. * It was not used by any known core at the time, * and was removed from the API. */ #define RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK 22 /* const struct retro_audio_callback * -- * Sets an interface which is used to notify a libretro core about audio * being available for writing. * The callback can be called from any thread, so a core using this must * have a thread safe audio implementation. * It is intended for games where audio and video are completely * asynchronous and audio can be generated on the fly. * This interface is not recommended for use with emulators which have * highly synchronous audio. * * The callback only notifies about writability; the libretro core still * has to call the normal audio callbacks * to write audio. The audio callbacks must be called from within the * notification callback. * The amount of audio data to write is up to the implementation. * Generally, the audio callback will be called continously in a loop. * * Due to thread safety guarantees and lack of sync between audio and * video, a frontend can selectively disallow this interface based on * internal configuration. A core using this interface must also * implement the "normal" audio interface. * * A libretro core using SET_AUDIO_CALLBACK should also make use of * SET_FRAME_TIME_CALLBACK. */ #define RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK 21 /* const struct retro_frame_time_callback * -- * Lets the core know how much time has passed since last * invocation of retro_run(). * The frontend can tamper with the timing to fake fast-forward, * slow-motion, frame stepping, etc. * In this case the delta time will use the reference value * in frame_time_callback.. */ #define RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE 23 /* struct retro_rumble_interface * -- * Gets an interface which is used by a libretro core to set * state of rumble motors in controllers. * A strong and weak motor is supported, and they can be * controlled indepedently. */ #define RETRO_ENVIRONMENT_GET_INPUT_DEVICE_CAPABILITIES 24 /* uint64_t * -- * Gets a bitmask telling which device type are expected to be * handled properly in a call to retro_input_state_t. * Devices which are not handled or recognized always return * 0 in retro_input_state_t. * Example bitmask: caps = (1 << RETRO_DEVICE_JOYPAD) | (1 << RETRO_DEVICE_ANALOG). * Should only be called in retro_run(). */ #define RETRO_ENVIRONMENT_GET_SENSOR_INTERFACE (25 | RETRO_ENVIRONMENT_EXPERIMENTAL) /* struct retro_sensor_interface * -- * Gets access to the sensor interface. * The purpose of this interface is to allow * setting state related to sensors such as polling rate, * enabling/disable it entirely, etc. * Reading sensor state is done via the normal * input_state_callback API. */ #define RETRO_ENVIRONMENT_GET_CAMERA_INTERFACE (26 | RETRO_ENVIRONMENT_EXPERIMENTAL) /* struct retro_camera_callback * -- * Gets an interface to a video camera driver. * A libretro core can use this interface to get access to a * video camera. * New video frames are delivered in a callback in same * thread as retro_run(). * * GET_CAMERA_INTERFACE should be called in retro_load_game(). * * Depending on the camera implementation used, camera frames * will be delivered as a raw framebuffer, * or as an OpenGL texture directly. * * The core has to tell the frontend here which types of * buffers can be handled properly. * An OpenGL texture can only be handled when using a * libretro GL core (SET_HW_RENDER). * It is recommended to use a libretro GL core when * using camera interface. * * The camera is not started automatically. The retrieved start/stop * functions must be used to explicitly * start and stop the camera driver. */ #define RETRO_ENVIRONMENT_GET_LOG_INTERFACE 27 /* struct retro_log_callback * -- * Gets an interface for logging. This is useful for * logging in a cross-platform way * as certain platforms cannot use use stderr for logging. * It also allows the frontend to * show logging information in a more suitable way. * If this interface is not used, libretro cores should * log to stderr as desired. */ #define RETRO_ENVIRONMENT_GET_PERF_INTERFACE 28 /* struct retro_perf_callback * -- * Gets an interface for performance counters. This is useful * for performance logging in a cross-platform way and for detecting * architecture-specific features, such as SIMD support. */ #define RETRO_ENVIRONMENT_GET_LOCATION_INTERFACE 29 /* struct retro_location_callback * -- * Gets access to the location interface. * The purpose of this interface is to be able to retrieve * location-based information from the host device, * such as current latitude / longitude. */ #define RETRO_ENVIRONMENT_GET_CONTENT_DIRECTORY 30 /* Old name, kept for compatibility. */ #define RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY 30 /* const char ** -- * Returns the "core assets" directory of the frontend. * This directory can be used to store specific assets that the * core relies upon, such as art assets, * input data, etc etc. * The returned value can be NULL. * If so, no such directory is defined, * and it's up to the implementation to find a suitable directory. */ #define RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY 31 /* const char ** -- * Returns the "save" directory of the frontend. * This directory can be used to store SRAM, memory cards, * high scores, etc, if the libretro core * cannot use the regular memory interface (retro_get_memory_data()). * * NOTE: libretro cores used to check GET_SYSTEM_DIRECTORY for * similar things before. * They should still check GET_SYSTEM_DIRECTORY if they want to * be backwards compatible. * The path here can be NULL. It should only be non-NULL if the * frontend user has set a specific save path. */ #define RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO 32 /* const struct retro_system_av_info * -- * Sets a new av_info structure. This can only be called from * within retro_run(). * This should *only* be used if the core is completely altering the * internal resolutions, aspect ratios, timings, sampling rate, etc. * Calling this can require a full reinitialization of video/audio * drivers in the frontend, * * so it is important to call it very sparingly, and usually only with * the users explicit consent. * An eventual driver reinitialize will happen so that video and * audio callbacks * happening after this call within the same retro_run() call will * target the newly initialized driver. * * This callback makes it possible to support configurable resolutions * in games, which can be useful to * avoid setting the "worst case" in max_width/max_height. * * ***HIGHLY RECOMMENDED*** Do not call this callback every time * resolution changes in an emulator core if it's * expected to be a temporary change, for the reasons of possible * driver reinitialization. * This call is not a free pass for not trying to provide * correct values in retro_get_system_av_info(). If you need to change * things like aspect ratio or nominal width/height, * use RETRO_ENVIRONMENT_SET_GEOMETRY, which is a softer variant * of SET_SYSTEM_AV_INFO. * * If this returns false, the frontend does not acknowledge a * changed av_info struct. */ #define RETRO_ENVIRONMENT_SET_PROC_ADDRESS_CALLBACK 33 /* const struct retro_get_proc_address_interface * -- * Allows a libretro core to announce support for the * get_proc_address() interface. * This interface allows for a standard way to extend libretro where * use of environment calls are too indirect, * e.g. for cases where the frontend wants to call directly into the core. * * If a core wants to expose this interface, SET_PROC_ADDRESS_CALLBACK * **MUST** be called from within retro_set_environment(). */ #define RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO 34 /* const struct retro_subsystem_info * -- * This environment call introduces the concept of libretro "subsystems". * A subsystem is a variant of a libretro core which supports * different kinds of games. * The purpose of this is to support e.g. emulators which might * have special needs, e.g. Super Nintendo's Super GameBoy, Sufami Turbo. * It can also be used to pick among subsystems in an explicit way * if the libretro implementation is a multi-system emulator itself. * * Loading a game via a subsystem is done with retro_load_game_special(), * and this environment call allows a libretro core to expose which * subsystems are supported for use with retro_load_game_special(). * A core passes an array of retro_game_special_info which is terminated * with a zeroed out retro_game_special_info struct. * * If a core wants to use this functionality, SET_SUBSYSTEM_INFO * **MUST** be called from within retro_set_environment(). */ #define RETRO_ENVIRONMENT_SET_CONTROLLER_INFO 35 /* const struct retro_controller_info * -- * This environment call lets a libretro core tell the frontend * which controller types are recognized in calls to * retro_set_controller_port_device(). * * Some emulators such as Super Nintendo * support multiple lightgun types which must be specifically * selected from. * It is therefore sometimes necessary for a frontend to be able * to tell the core about a special kind of input device which is * not covered by the libretro input API. * * In order for a frontend to understand the workings of an input device, * it must be a specialized type * of the generic device types already defined in the libretro API. * * Which devices are supported can vary per input port. * The core must pass an array of const struct retro_controller_info which * is terminated with a blanked out struct. Each element of the struct * corresponds to an ascending port index to * retro_set_controller_port_device(). * Even if special device types are set in the libretro core, * libretro should only poll input based on the base input device types. */ #define RETRO_ENVIRONMENT_SET_MEMORY_MAPS (36 | RETRO_ENVIRONMENT_EXPERIMENTAL) /* const struct retro_memory_map * -- * This environment call lets a libretro core tell the frontend * about the memory maps this core emulates. * This can be used to implement, for example, cheats in a core-agnostic way. * * Should only be used by emulators; it doesn't make much sense for * anything else. * It is recommended to expose all relevant pointers through * retro_get_memory_* as well. * * Can be called from retro_init and retro_load_game. */ #define RETRO_ENVIRONMENT_SET_GEOMETRY 37 /* const struct retro_game_geometry * -- * This environment call is similar to SET_SYSTEM_AV_INFO for changing * video parameters, but provides a guarantee that drivers will not be * reinitialized. * This can only be called from within retro_run(). * * The purpose of this call is to allow a core to alter nominal * width/heights as well as aspect ratios on-the-fly, which can be * useful for some emulators to change in run-time. * * max_width/max_height arguments are ignored and cannot be changed * with this call as this could potentially require a reinitialization or a * non-constant time operation. * If max_width/max_height are to be changed, SET_SYSTEM_AV_INFO is required. * * A frontend must guarantee that this environment call completes in * constant time. */ #define RETRO_ENVIRONMENT_GET_USERNAME 38 /* const char ** * Returns the specified username of the frontend, if specified by the user. * This username can be used as a nickname for a core that has online facilities * or any other mode where personalization of the user is desirable. * The returned value can be NULL. * If this environ callback is used by a core that requires a valid username, * a default username should be specified by the core. */ #define RETRO_ENVIRONMENT_GET_LANGUAGE 39 /* unsigned * -- * Returns the specified language of the frontend, if specified by the user. * It can be used by the core for localization purposes. */ #define RETRO_ENVIRONMENT_GET_CURRENT_SOFTWARE_FRAMEBUFFER (40 | RETRO_ENVIRONMENT_EXPERIMENTAL) /* struct retro_framebuffer * -- * Returns a preallocated framebuffer which the core can use for rendering * the frame into when not using SET_HW_RENDER. * The framebuffer returned from this call must not be used * after the current call to retro_run() returns. * * The goal of this call is to allow zero-copy behavior where a core * can render directly into video memory, avoiding extra bandwidth cost by copying * memory from core to video memory. * * If this call succeeds and the core renders into it, * the framebuffer pointer and pitch can be passed to retro_video_refresh_t. * If the buffer from GET_CURRENT_SOFTWARE_FRAMEBUFFER is to be used, * the core must pass the exact * same pointer as returned by GET_CURRENT_SOFTWARE_FRAMEBUFFER; * i.e. passing a pointer which is offset from the * buffer is undefined. The width, height and pitch parameters * must also match exactly to the values obtained from GET_CURRENT_SOFTWARE_FRAMEBUFFER. * * It is possible for a frontend to return a different pixel format * than the one used in SET_PIXEL_FORMAT. This can happen if the frontend * needs to perform conversion. * * It is still valid for a core to render to a different buffer * even if GET_CURRENT_SOFTWARE_FRAMEBUFFER succeeds. * * A frontend must make sure that the pointer obtained from this function is * writeable (and readable). */ enum retro_hw_render_interface_type { RETRO_HW_RENDER_INTERFACE_VULKAN = 0, RETRO_HW_RENDER_INTERFACE_DUMMY = INT_MAX }; /* Base struct. All retro_hw_render_interface_* types * contain at least these fields. */ struct retro_hw_render_interface { enum retro_hw_render_interface_type interface_type; unsigned interface_version; }; #define RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE (41 | RETRO_ENVIRONMENT_EXPERIMENTAL) /* const struct retro_hw_render_interface ** -- * Returns an API specific rendering interface for accessing API specific data. * Not all HW rendering APIs support or need this. * The contents of the returned pointer is specific to the rendering API * being used. See the various headers like libretro_vulkan.h, etc. * * GET_HW_RENDER_INTERFACE cannot be called before context_reset has been called. * Similarly, after context_destroyed callback returns, * the contents of the HW_RENDER_INTERFACE are invalidated. */ #define RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS (42 | RETRO_ENVIRONMENT_EXPERIMENTAL) /* const bool * -- * If true, the libretro implementation supports achievements * either via memory descriptors set with RETRO_ENVIRONMENT_SET_MEMORY_MAPS * or via retro_get_memory_data/retro_get_memory_size. * * This must be called before the first call to retro_run. */ enum retro_hw_render_context_negotiation_interface_type { RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN = 0, RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_DUMMY = INT_MAX }; /* Base struct. All retro_hw_render_context_negotiation_interface_* types * contain at least these fields. */ struct retro_hw_render_context_negotiation_interface { enum retro_hw_render_context_negotiation_interface_type interface_type; unsigned interface_version; }; #define RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE (43 | RETRO_ENVIRONMENT_EXPERIMENTAL) /* const struct retro_hw_render_context_negotiation_interface * -- * Sets an interface which lets the libretro core negotiate with frontend how a context is created. * The semantics of this interface depends on which API is used in SET_HW_RENDER earlier. * This interface will be used when the frontend is trying to create a HW rendering context, * so it will be used after SET_HW_RENDER, but before the context_reset callback. */ #define RETRO_MEMDESC_CONST (1 << 0) /* The frontend will never change this memory area once retro_load_game has returned. */ #define RETRO_MEMDESC_BIGENDIAN (1 << 1) /* The memory area contains big endian data. Default is little endian. */ #define RETRO_MEMDESC_ALIGN_2 (1 << 16) /* All memory access in this area is aligned to their own size, or 2, whichever is smaller. */ #define RETRO_MEMDESC_ALIGN_4 (2 << 16) #define RETRO_MEMDESC_ALIGN_8 (3 << 16) #define RETRO_MEMDESC_MINSIZE_2 (1 << 24) /* All memory in this region is accessed at least 2 bytes at the time. */ #define RETRO_MEMDESC_MINSIZE_4 (2 << 24) #define RETRO_MEMDESC_MINSIZE_8 (3 << 24) struct retro_memory_descriptor { uint64_t flags; /* Pointer to the start of the relevant ROM or RAM chip. * It's strongly recommended to use 'offset' if possible, rather than * doing math on the pointer. * * If the same byte is mapped my multiple descriptors, their descriptors * must have the same pointer. * If 'start' does not point to the first byte in the pointer, put the * difference in 'offset' instead. * * May be NULL if there's nothing usable here (e.g. hardware registers and * open bus). No flags should be set if the pointer is NULL. * It's recommended to minimize the number of descriptors if possible, * but not mandatory. */ void *ptr; size_t offset; /* This is the location in the emulated address space * where the mapping starts. */ size_t start; /* Which bits must be same as in 'start' for this mapping to apply. * The first memory descriptor to claim a certain byte is the one * that applies. * A bit which is set in 'start' must also be set in this. * Can be zero, in which case each byte is assumed mapped exactly once. * In this case, 'len' must be a power of two. */ size_t select; /* If this is nonzero, the set bits are assumed not connected to the * memory chip's address pins. */ size_t disconnect; /* This one tells the size of the current memory area. * If, after start+disconnect are applied, the address is higher than * this, the highest bit of the address is cleared. * * If the address is still too high, the next highest bit is cleared. * Can be zero, in which case it's assumed to be infinite (as limited * by 'select' and 'disconnect'). */ size_t len; /* To go from emulated address to physical address, the following * order applies: * Subtract 'start', pick off 'disconnect', apply 'len', add 'offset'. */ /* The address space name must consist of only a-zA-Z0-9_-, * should be as short as feasible (maximum length is 8 plus the NUL), * and may not be any other address space plus one or more 0-9A-F * at the end. * However, multiple memory descriptors for the same address space is * allowed, and the address space name can be empty. NULL is treated * as empty. * * Address space names are case sensitive, but avoid lowercase if possible. * The same pointer may exist in multiple address spaces. * * Examples: * blank+blank - valid (multiple things may be mapped in the same namespace) * 'Sp'+'Sp' - valid (multiple things may be mapped in the same namespace) * 'A'+'B' - valid (neither is a prefix of each other) * 'S'+blank - valid ('S' is not in 0-9A-F) * 'a'+blank - valid ('a' is not in 0-9A-F) * 'a'+'A' - valid (neither is a prefix of each other) * 'AR'+blank - valid ('R' is not in 0-9A-F) * 'ARB'+blank - valid (the B can't be part of the address either, because * there is no namespace 'AR') * blank+'B' - not valid, because it's ambigous which address space B1234 * would refer to. * The length can't be used for that purpose; the frontend may want * to append arbitrary data to an address, without a separator. */ const char *addrspace; /* TODO: When finalizing this one, add a description field, which should be * "WRAM" or something roughly equally long. */ /* TODO: When finalizing this one, replace 'select' with 'limit', which tells * which bits can vary and still refer to the same address (limit = ~select). * TODO: limit? range? vary? something else? */ /* TODO: When finalizing this one, if 'len' is above what 'select' (or * 'limit') allows, it's bankswitched. Bankswitched data must have both 'len' * and 'select' != 0, and the mappings don't tell how the system switches the * banks. */ /* TODO: When finalizing this one, fix the 'len' bit removal order. * For len=0x1800, pointer 0x1C00 should go to 0x1400, not 0x0C00. * Algorithm: Take bits highest to lowest, but if it goes above len, clear * the most recent addition and continue on the next bit. * TODO: Can the above be optimized? Is "remove the lowest bit set in both * pointer and 'len'" equivalent? */ /* TODO: Some emulators (MAME?) emulate big endian systems by only accessing * the emulated memory in 32-bit chunks, native endian. But that's nothing * compared to Darek Mihocka * (section Emulation 103 - Nearly Free Byte Reversal) - he flips the ENTIRE * RAM backwards! I'll want to represent both of those, via some flags. * * I suspect MAME either didn't think of that idea, or don't want the #ifdef. * Not sure which, nor do I really care. */ /* TODO: Some of those flags are unused and/or don't really make sense. Clean * them up. */ }; /* The frontend may use the largest value of 'start'+'select' in a * certain namespace to infer the size of the address space. * * If the address space is larger than that, a mapping with .ptr=NULL * should be at the end of the array, with .select set to all ones for * as long as the address space is big. * * Sample descriptors (minus .ptr, and RETRO_MEMFLAG_ on the flags): * SNES WRAM: * .start=0x7E0000, .len=0x20000 * (Note that this must be mapped before the ROM in most cases; some of the * ROM mappers * try to claim $7E0000, or at least $7E8000.) * SNES SPC700 RAM: * .addrspace="S", .len=0x10000 * SNES WRAM mirrors: * .flags=MIRROR, .start=0x000000, .select=0xC0E000, .len=0x2000 * .flags=MIRROR, .start=0x800000, .select=0xC0E000, .len=0x2000 * SNES WRAM mirrors, alternate equivalent descriptor: * .flags=MIRROR, .select=0x40E000, .disconnect=~0x1FFF * (Various similar constructions can be created by combining parts of * the above two.) * SNES LoROM (512KB, mirrored a couple of times): * .flags=CONST, .start=0x008000, .select=0x408000, .disconnect=0x8000, .len=512*1024 * .flags=CONST, .start=0x400000, .select=0x400000, .disconnect=0x8000, .len=512*1024 * SNES HiROM (4MB): * .flags=CONST, .start=0x400000, .select=0x400000, .len=4*1024*1024 * .flags=CONST, .offset=0x8000, .start=0x008000, .select=0x408000, .len=4*1024*1024 * SNES ExHiROM (8MB): * .flags=CONST, .offset=0, .start=0xC00000, .select=0xC00000, .len=4*1024*1024 * .flags=CONST, .offset=4*1024*1024, .start=0x400000, .select=0xC00000, .len=4*1024*1024 * .flags=CONST, .offset=0x8000, .start=0x808000, .select=0xC08000, .len=4*1024*1024 * .flags=CONST, .offset=4*1024*1024+0x8000, .start=0x008000, .select=0xC08000, .len=4*1024*1024 * Clarify the size of the address space: * .ptr=NULL, .select=0xFFFFFF * .len can be implied by .select in many of them, but was included for clarity. */ struct retro_memory_map { const struct retro_memory_descriptor *descriptors; unsigned num_descriptors; }; struct retro_controller_description { /* Human-readable description of the controller. Even if using a generic * input device type, this can be set to the particular device type the * core uses. */ const char *desc; /* Device type passed to retro_set_controller_port_device(). If the device * type is a sub-class of a generic input device type, use the * RETRO_DEVICE_SUBCLASS macro to create an ID. * * E.g. RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 1). */ unsigned id; }; struct retro_controller_info { const struct retro_controller_description *types; unsigned num_types; }; struct retro_subsystem_memory_info { /* The extension associated with a memory type, e.g. "psram". */ const char *extension; /* The memory type for retro_get_memory(). This should be at * least 0x100 to avoid conflict with standardized * libretro memory types. */ unsigned type; }; struct retro_subsystem_rom_info { /* Describes what the content is (SGB BIOS, GB ROM, etc). */ const char *desc; /* Same definition as retro_get_system_info(). */ const char *valid_extensions; /* Same definition as retro_get_system_info(). */ bool need_fullpath; /* Same definition as retro_get_system_info(). */ bool block_extract; /* This is set if the content is required to load a game. * If this is set to false, a zeroed-out retro_game_info can be passed. */ bool required; /* Content can have multiple associated persistent * memory types (retro_get_memory()). */ const struct retro_subsystem_memory_info *memory; unsigned num_memory; }; struct retro_subsystem_info { /* Human-readable string of the subsystem type, e.g. "Super GameBoy" */ const char *desc; /* A computer friendly short string identifier for the subsystem type. * This name must be [a-z]. * E.g. if desc is "Super GameBoy", this can be "sgb". * This identifier can be used for command-line interfaces, etc. */ const char *ident; /* Infos for each content file. The first entry is assumed to be the * "most significant" content for frontend purposes. * E.g. with Super GameBoy, the first content should be the GameBoy ROM, * as it is the most "significant" content to a user. * If a frontend creates new file paths based on the content used * (e.g. savestates), it should use the path for the first ROM to do so. */ const struct retro_subsystem_rom_info *roms; /* Number of content files associated with a subsystem. */ unsigned num_roms; /* The type passed to retro_load_game_special(). */ unsigned id; }; typedef void (RETRO_CALLCONV *retro_proc_address_t)(void); /* libretro API extension functions: * (None here so far). * * Get a symbol from a libretro core. * Cores should only return symbols which are actual * extensions to the libretro API. * * Frontends should not use this to obtain symbols to standard * libretro entry points (static linking or dlsym). * * The symbol name must be equal to the function name, * e.g. if void retro_foo(void); exists, the symbol must be called "retro_foo". * The returned function pointer must be cast to the corresponding type. */ typedef retro_proc_address_t (RETRO_CALLCONV *retro_get_proc_address_t)(const char *sym); struct retro_get_proc_address_interface { retro_get_proc_address_t get_proc_address; }; enum retro_log_level { RETRO_LOG_DEBUG = 0, RETRO_LOG_INFO, RETRO_LOG_WARN, RETRO_LOG_ERROR, RETRO_LOG_DUMMY = INT_MAX }; /* Logging function. Takes log level argument as well. */ typedef void (RETRO_CALLCONV *retro_log_printf_t)(enum retro_log_level level, const char *fmt, ...); struct retro_log_callback { retro_log_printf_t log; }; /* Performance related functions */ /* ID values for SIMD CPU features */ #define RETRO_SIMD_SSE (1 << 0) #define RETRO_SIMD_SSE2 (1 << 1) #define RETRO_SIMD_VMX (1 << 2) #define RETRO_SIMD_VMX128 (1 << 3) #define RETRO_SIMD_AVX (1 << 4) #define RETRO_SIMD_NEON (1 << 5) #define RETRO_SIMD_SSE3 (1 << 6) #define RETRO_SIMD_SSSE3 (1 << 7) #define RETRO_SIMD_MMX (1 << 8) #define RETRO_SIMD_MMXEXT (1 << 9) #define RETRO_SIMD_SSE4 (1 << 10) #define RETRO_SIMD_SSE42 (1 << 11) #define RETRO_SIMD_AVX2 (1 << 12) #define RETRO_SIMD_VFPU (1 << 13) #define RETRO_SIMD_PS (1 << 14) #define RETRO_SIMD_AES (1 << 15) #define RETRO_SIMD_VFPV3 (1 << 16) #define RETRO_SIMD_VFPV4 (1 << 17) #define RETRO_SIMD_POPCNT (1 << 18) #define RETRO_SIMD_MOVBE (1 << 19) #define RETRO_SIMD_CMOV (1 << 20) typedef uint64_t retro_perf_tick_t; typedef int64_t retro_time_t; struct retro_perf_counter { const char *ident; retro_perf_tick_t start; retro_perf_tick_t total; retro_perf_tick_t call_cnt; bool registered; }; /* Returns current time in microseconds. * Tries to use the most accurate timer available. */ typedef retro_time_t (RETRO_CALLCONV *retro_perf_get_time_usec_t)(void); /* A simple counter. Usually nanoseconds, but can also be CPU cycles. * Can be used directly if desired (when creating a more sophisticated * performance counter system). * */ typedef retro_perf_tick_t (RETRO_CALLCONV *retro_perf_get_counter_t)(void); /* Returns a bit-mask of detected CPU features (RETRO_SIMD_*). */ typedef uint64_t (RETRO_CALLCONV *retro_get_cpu_features_t)(void); /* Asks frontend to log and/or display the state of performance counters. * Performance counters can always be poked into manually as well. */ typedef void (RETRO_CALLCONV *retro_perf_log_t)(void); /* Register a performance counter. * ident field must be set with a discrete value and other values in * retro_perf_counter must be 0. * Registering can be called multiple times. To avoid calling to * frontend redundantly, you can check registered field first. */ typedef void (RETRO_CALLCONV *retro_perf_register_t)(struct retro_perf_counter *counter); /* Starts a registered counter. */ typedef void (RETRO_CALLCONV *retro_perf_start_t)(struct retro_perf_counter *counter); /* Stops a registered counter. */ typedef void (RETRO_CALLCONV *retro_perf_stop_t)(struct retro_perf_counter *counter); /* For convenience it can be useful to wrap register, start and stop in macros. * E.g.: * #ifdef LOG_PERFORMANCE * #define RETRO_PERFORMANCE_INIT(perf_cb, name) static struct retro_perf_counter name = {#name}; if (!name.registered) perf_cb.perf_register(&(name)) * #define RETRO_PERFORMANCE_START(perf_cb, name) perf_cb.perf_start(&(name)) * #define RETRO_PERFORMANCE_STOP(perf_cb, name) perf_cb.perf_stop(&(name)) * #else * ... Blank macros ... * #endif * * These can then be used mid-functions around code snippets. * * extern struct retro_perf_callback perf_cb; * Somewhere in the core. * * void do_some_heavy_work(void) * { * RETRO_PERFORMANCE_INIT(cb, work_1; * RETRO_PERFORMANCE_START(cb, work_1); * heavy_work_1(); * RETRO_PERFORMANCE_STOP(cb, work_1); * * RETRO_PERFORMANCE_INIT(cb, work_2); * RETRO_PERFORMANCE_START(cb, work_2); * heavy_work_2(); * RETRO_PERFORMANCE_STOP(cb, work_2); * } * * void retro_deinit(void) * { * perf_cb.perf_log(); * Log all perf counters here for example. * } */ struct retro_perf_callback { retro_perf_get_time_usec_t get_time_usec; retro_get_cpu_features_t get_cpu_features; retro_perf_get_counter_t get_perf_counter; retro_perf_register_t perf_register; retro_perf_start_t perf_start; retro_perf_stop_t perf_stop; retro_perf_log_t perf_log; }; /* FIXME: Document the sensor API and work out behavior. * It will be marked as experimental until then. */ enum retro_sensor_action { RETRO_SENSOR_ACCELEROMETER_ENABLE = 0, RETRO_SENSOR_ACCELEROMETER_DISABLE, RETRO_SENSOR_DUMMY = INT_MAX }; /* Id values for SENSOR types. */ #define RETRO_SENSOR_ACCELEROMETER_X 0 #define RETRO_SENSOR_ACCELEROMETER_Y 1 #define RETRO_SENSOR_ACCELEROMETER_Z 2 typedef bool (RETRO_CALLCONV *retro_set_sensor_state_t)(unsigned port, enum retro_sensor_action action, unsigned rate); typedef float (RETRO_CALLCONV *retro_sensor_get_input_t)(unsigned port, unsigned id); struct retro_sensor_interface { retro_set_sensor_state_t set_sensor_state; retro_sensor_get_input_t get_sensor_input; }; enum retro_camera_buffer { RETRO_CAMERA_BUFFER_OPENGL_TEXTURE = 0, RETRO_CAMERA_BUFFER_RAW_FRAMEBUFFER, RETRO_CAMERA_BUFFER_DUMMY = INT_MAX }; /* Starts the camera driver. Can only be called in retro_run(). */ typedef bool (RETRO_CALLCONV *retro_camera_start_t)(void); /* Stops the camera driver. Can only be called in retro_run(). */ typedef void (RETRO_CALLCONV *retro_camera_stop_t)(void); /* Callback which signals when the camera driver is initialized * and/or deinitialized. * retro_camera_start_t can be called in initialized callback. */ typedef void (RETRO_CALLCONV *retro_camera_lifetime_status_t)(void); /* A callback for raw framebuffer data. buffer points to an XRGB8888 buffer. * Width, height and pitch are similar to retro_video_refresh_t. * First pixel is top-left origin. */ typedef void (RETRO_CALLCONV *retro_camera_frame_raw_framebuffer_t)(const uint32_t *buffer, unsigned width, unsigned height, size_t pitch); /* A callback for when OpenGL textures are used. * * texture_id is a texture owned by camera driver. * Its state or content should be considered immutable, except for things like * texture filtering and clamping. * * texture_target is the texture target for the GL texture. * These can include e.g. GL_TEXTURE_2D, GL_TEXTURE_RECTANGLE, and possibly * more depending on extensions. * * affine points to a packed 3x3 column-major matrix used to apply an affine * transform to texture coordinates. (affine_matrix * vec3(coord_x, coord_y, 1.0)) * After transform, normalized texture coord (0, 0) should be bottom-left * and (1, 1) should be top-right (or (width, height) for RECTANGLE). * * GL-specific typedefs are avoided here to avoid relying on gl.h in * the API definition. */ typedef void (RETRO_CALLCONV *retro_camera_frame_opengl_texture_t)(unsigned texture_id, unsigned texture_target, const float *affine); struct retro_camera_callback { /* Set by libretro core. * Example bitmask: caps = (1 << RETRO_CAMERA_BUFFER_OPENGL_TEXTURE) | (1 << RETRO_CAMERA_BUFFER_RAW_FRAMEBUFFER). */ uint64_t caps; /* Desired resolution for camera. Is only used as a hint. */ unsigned width; unsigned height; /* Set by frontend. */ retro_camera_start_t start; retro_camera_stop_t stop; /* Set by libretro core if raw framebuffer callbacks will be used. */ retro_camera_frame_raw_framebuffer_t frame_raw_framebuffer; /* Set by libretro core if OpenGL texture callbacks will be used. */ retro_camera_frame_opengl_texture_t frame_opengl_texture; /* Set by libretro core. Called after camera driver is initialized and * ready to be started. * Can be NULL, in which this callback is not called. */ retro_camera_lifetime_status_t initialized; /* Set by libretro core. Called right before camera driver is * deinitialized. * Can be NULL, in which this callback is not called. */ retro_camera_lifetime_status_t deinitialized; }; /* Sets the interval of time and/or distance at which to update/poll * location-based data. * * To ensure compatibility with all location-based implementations, * values for both interval_ms and interval_distance should be provided. * * interval_ms is the interval expressed in milliseconds. * interval_distance is the distance interval expressed in meters. */ typedef void (RETRO_CALLCONV *retro_location_set_interval_t)(unsigned interval_ms, unsigned interval_distance); /* Start location services. The device will start listening for changes to the * current location at regular intervals (which are defined with * retro_location_set_interval_t). */ typedef bool (RETRO_CALLCONV *retro_location_start_t)(void); /* Stop location services. The device will stop listening for changes * to the current location. */ typedef void (RETRO_CALLCONV *retro_location_stop_t)(void); /* Get the position of the current location. Will set parameters to * 0 if no new location update has happened since the last time. */ typedef bool (RETRO_CALLCONV *retro_location_get_position_t)(double *lat, double *lon, double *horiz_accuracy, double *vert_accuracy); /* Callback which signals when the location driver is initialized * and/or deinitialized. * retro_location_start_t can be called in initialized callback. */ typedef void (RETRO_CALLCONV *retro_location_lifetime_status_t)(void); struct retro_location_callback { retro_location_start_t start; retro_location_stop_t stop; retro_location_get_position_t get_position; retro_location_set_interval_t set_interval; retro_location_lifetime_status_t initialized; retro_location_lifetime_status_t deinitialized; }; enum retro_rumble_effect { RETRO_RUMBLE_STRONG = 0, RETRO_RUMBLE_WEAK = 1, RETRO_RUMBLE_DUMMY = INT_MAX }; /* Sets rumble state for joypad plugged in port 'port'. * Rumble effects are controlled independently, * and setting e.g. strong rumble does not override weak rumble. * Strength has a range of [0, 0xffff]. * * Returns true if rumble state request was honored. * Calling this before first retro_run() is likely to return false. */ typedef bool (RETRO_CALLCONV *retro_set_rumble_state_t)(unsigned port, enum retro_rumble_effect effect, uint16_t strength); struct retro_rumble_interface { retro_set_rumble_state_t set_rumble_state; }; /* Notifies libretro that audio data should be written. */ typedef void (RETRO_CALLCONV *retro_audio_callback_t)(void); /* True: Audio driver in frontend is active, and callback is * expected to be called regularily. * False: Audio driver in frontend is paused or inactive. * Audio callback will not be called until set_state has been * called with true. * Initial state is false (inactive). */ typedef void (RETRO_CALLCONV *retro_audio_set_state_callback_t)(bool enabled); struct retro_audio_callback { retro_audio_callback_t callback; retro_audio_set_state_callback_t set_state; }; /* Notifies a libretro core of time spent since last invocation * of retro_run() in microseconds. * * It will be called right before retro_run() every frame. * The frontend can tamper with timing to support cases like * fast-forward, slow-motion and framestepping. * * In those scenarios the reference frame time value will be used. */ typedef int64_t retro_usec_t; typedef void (RETRO_CALLCONV *retro_frame_time_callback_t)(retro_usec_t usec); struct retro_frame_time_callback { retro_frame_time_callback_t callback; /* Represents the time of one frame. It is computed as * 1000000 / fps, but the implementation will resolve the * rounding to ensure that framestepping, etc is exact. */ retro_usec_t reference; }; /* Pass this to retro_video_refresh_t if rendering to hardware. * Passing NULL to retro_video_refresh_t is still a frame dupe as normal. * */ #define RETRO_HW_FRAME_BUFFER_VALID ((void*)-1) /* Invalidates the current HW context. * Any GL state is lost, and must not be deinitialized explicitly. * If explicit deinitialization is desired by the libretro core, * it should implement context_destroy callback. * If called, all GPU resources must be reinitialized. * Usually called when frontend reinits video driver. * Also called first time video driver is initialized, * allowing libretro core to initialize resources. */ typedef void (RETRO_CALLCONV *retro_hw_context_reset_t)(void); /* Gets current framebuffer which is to be rendered to. * Could change every frame potentially. */ typedef uintptr_t (RETRO_CALLCONV *retro_hw_get_current_framebuffer_t)(void); /* Get a symbol from HW context. */ typedef retro_proc_address_t (RETRO_CALLCONV *retro_hw_get_proc_address_t)(const char *sym); enum retro_hw_context_type { RETRO_HW_CONTEXT_NONE = 0, /* OpenGL 2.x. Driver can choose to use latest compatibility context. */ RETRO_HW_CONTEXT_OPENGL = 1, /* OpenGL ES 2.0. */ RETRO_HW_CONTEXT_OPENGLES2 = 2, /* Modern desktop core GL context. Use version_major/ * version_minor fields to set GL version. */ RETRO_HW_CONTEXT_OPENGL_CORE = 3, /* OpenGL ES 3.0 */ RETRO_HW_CONTEXT_OPENGLES3 = 4, /* OpenGL ES 3.1+. Set version_major/version_minor. For GLES2 and GLES3, * use the corresponding enums directly. */ RETRO_HW_CONTEXT_OPENGLES_VERSION = 5, /* Vulkan, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE. */ RETRO_HW_CONTEXT_VULKAN = 6, RETRO_HW_CONTEXT_DUMMY = INT_MAX }; struct retro_hw_render_callback { /* Which API to use. Set by libretro core. */ enum retro_hw_context_type context_type; /* Called when a context has been created or when it has been reset. * An OpenGL context is only valid after context_reset() has been called. * * When context_reset is called, OpenGL resources in the libretro * implementation are guaranteed to be invalid. * * It is possible that context_reset is called multiple times during an * application lifecycle. * If context_reset is called without any notification (context_destroy), * the OpenGL context was lost and resources should just be recreated * without any attempt to "free" old resources. */ retro_hw_context_reset_t context_reset; /* Set by frontend. * TODO: This is rather obsolete. The frontend should not * be providing preallocated framebuffers. */ retro_hw_get_current_framebuffer_t get_current_framebuffer; /* Set by frontend. */ retro_hw_get_proc_address_t get_proc_address; /* Set if render buffers should have depth component attached. * TODO: Obsolete. */ bool depth; /* Set if stencil buffers should be attached. * TODO: Obsolete. */ bool stencil; /* If depth and stencil are true, a packed 24/8 buffer will be added. * Only attaching stencil is invalid and will be ignored. */ /* Use conventional bottom-left origin convention. If false, * standard libretro top-left origin semantics are used. * TODO: Move to GL specific interface. */ bool bottom_left_origin; /* Major version number for core GL context or GLES 3.1+. */ unsigned version_major; /* Minor version number for core GL context or GLES 3.1+. */ unsigned version_minor; /* If this is true, the frontend will go very far to avoid * resetting context in scenarios like toggling fullscreen, etc. * TODO: Obsolete? Maybe frontend should just always assume this ... */ bool cache_context; /* The reset callback might still be called in extreme situations * such as if the context is lost beyond recovery. * * For optimal stability, set this to false, and allow context to be * reset at any time. */ /* A callback to be called before the context is destroyed in a * controlled way by the frontend. */ retro_hw_context_reset_t context_destroy; /* OpenGL resources can be deinitialized cleanly at this step. * context_destroy can be set to NULL, in which resources will * just be destroyed without any notification. * * Even when context_destroy is non-NULL, it is possible that * context_reset is called without any destroy notification. * This happens if context is lost by external factors (such as * notified by GL_ARB_robustness). * * In this case, the context is assumed to be already dead, * and the libretro implementation must not try to free any OpenGL * resources in the subsequent context_reset. */ /* Creates a debug context. */ bool debug_context; }; /* Callback type passed in RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK. * Called by the frontend in response to keyboard events. * down is set if the key is being pressed, or false if it is being released. * keycode is the RETROK value of the char. * character is the text character of the pressed key. (UTF-32). * key_modifiers is a set of RETROKMOD values or'ed together. * * The pressed/keycode state can be indepedent of the character. * It is also possible that multiple characters are generated from a * single keypress. * Keycode events should be treated separately from character events. * However, when possible, the frontend should try to synchronize these. * If only a character is posted, keycode should be RETROK_UNKNOWN. * * Similarily if only a keycode event is generated with no corresponding * character, character should be 0. */ typedef void (RETRO_CALLCONV *retro_keyboard_event_t)(bool down, unsigned keycode, uint32_t character, uint16_t key_modifiers); struct retro_keyboard_callback { retro_keyboard_event_t callback; }; /* Callbacks for RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE. * Should be set for implementations which can swap out multiple disk * images in runtime. * * If the implementation can do this automatically, it should strive to do so. * However, there are cases where the user must manually do so. * * Overview: To swap a disk image, eject the disk image with * set_eject_state(true). * Set the disk index with set_image_index(index). Insert the disk again * with set_eject_state(false). */ /* If ejected is true, "ejects" the virtual disk tray. * When ejected, the disk image index can be set. */ typedef bool (RETRO_CALLCONV *retro_set_eject_state_t)(bool ejected); /* Gets current eject state. The initial state is 'not ejected'. */ typedef bool (RETRO_CALLCONV *retro_get_eject_state_t)(void); /* Gets current disk index. First disk is index 0. * If return value is >= get_num_images(), no disk is currently inserted. */ typedef unsigned (RETRO_CALLCONV *retro_get_image_index_t)(void); /* Sets image index. Can only be called when disk is ejected. * The implementation supports setting "no disk" by using an * index >= get_num_images(). */ typedef bool (RETRO_CALLCONV *retro_set_image_index_t)(unsigned index); /* Gets total number of images which are available to use. */ typedef unsigned (RETRO_CALLCONV *retro_get_num_images_t)(void); struct retro_game_info; /* Replaces the disk image associated with index. * Arguments to pass in info have same requirements as retro_load_game(). * Virtual disk tray must be ejected when calling this. * * Replacing a disk image with info = NULL will remove the disk image * from the internal list. * As a result, calls to get_image_index() can change. * * E.g. replace_image_index(1, NULL), and previous get_image_index() * returned 4 before. * Index 1 will be removed, and the new index is 3. */ typedef bool (RETRO_CALLCONV *retro_replace_image_index_t)(unsigned index, const struct retro_game_info *info); /* Adds a new valid index (get_num_images()) to the internal disk list. * This will increment subsequent return values from get_num_images() by 1. * This image index cannot be used until a disk image has been set * with replace_image_index. */ typedef bool (RETRO_CALLCONV *retro_add_image_index_t)(void); struct retro_disk_control_callback { retro_set_eject_state_t set_eject_state; retro_get_eject_state_t get_eject_state; retro_get_image_index_t get_image_index; retro_set_image_index_t set_image_index; retro_get_num_images_t get_num_images; retro_replace_image_index_t replace_image_index; retro_add_image_index_t add_image_index; }; enum retro_pixel_format { /* 0RGB1555, native endian. * 0 bit must be set to 0. * This pixel format is default for compatibility concerns only. * If a 15/16-bit pixel format is desired, consider using RGB565. */ RETRO_PIXEL_FORMAT_0RGB1555 = 0, /* XRGB8888, native endian. * X bits are ignored. */ RETRO_PIXEL_FORMAT_XRGB8888 = 1, /* RGB565, native endian. * This pixel format is the recommended format to use if a 15/16-bit * format is desired as it is the pixel format that is typically * available on a wide range of low-power devices. * * It is also natively supported in APIs like OpenGL ES. */ RETRO_PIXEL_FORMAT_RGB565 = 2, /* Ensure sizeof() == sizeof(int). */ RETRO_PIXEL_FORMAT_UNKNOWN = INT_MAX }; struct retro_message { const char *msg; /* Message to be displayed. */ unsigned frames; /* Duration in frames of message. */ }; /* Describes how the libretro implementation maps a libretro input bind * to its internal input system through a human readable string. * This string can be used to better let a user configure input. */ struct retro_input_descriptor { /* Associates given parameters with a description. */ unsigned port; unsigned device; unsigned index; unsigned id; /* Human readable description for parameters. * The pointer must remain valid until * retro_unload_game() is called. */ const char *description; }; struct retro_system_info { /* All pointers are owned by libretro implementation, and pointers must * remain valid until retro_deinit() is called. */ const char *library_name; /* Descriptive name of library. Should not * contain any version numbers, etc. */ const char *library_version; /* Descriptive version of core. */ const char *valid_extensions; /* A string listing probably content * extensions the core will be able to * load, separated with pipe. * I.e. "bin|rom|iso". * Typically used for a GUI to filter * out extensions. */ /* If true, retro_load_game() is guaranteed to provide a valid pathname * in retro_game_info::path. * ::data and ::size are both invalid. * * If false, ::data and ::size are guaranteed to be valid, but ::path * might not be valid. * * This is typically set to true for libretro implementations that must * load from file. * Implementations should strive for setting this to false, as it allows * the frontend to perform patching, etc. */ bool need_fullpath; /* If true, the frontend is not allowed to extract any archives before * loading the real content. * Necessary for certain libretro implementations that load games * from zipped archives. */ bool block_extract; }; struct retro_game_geometry { unsigned base_width; /* Nominal video width of game. */ unsigned base_height; /* Nominal video height of game. */ unsigned max_width; /* Maximum possible width of game. */ unsigned max_height; /* Maximum possible height of game. */ float aspect_ratio; /* Nominal aspect ratio of game. If * aspect_ratio is <= 0.0, an aspect ratio * of base_width / base_height is assumed. * A frontend could override this setting, * if desired. */ }; struct retro_system_timing { double fps; /* FPS of video content. */ double sample_rate; /* Sampling rate of audio. */ }; struct retro_system_av_info { struct retro_game_geometry geometry; struct retro_system_timing timing; }; struct retro_variable { /* Variable to query in RETRO_ENVIRONMENT_GET_VARIABLE. * If NULL, obtains the complete environment string if more * complex parsing is necessary. * The environment string is formatted as key-value pairs * delimited by semicolons as so: * "key1=value1;key2=value2;..." */ const char *key; /* Value to be obtained. If key does not exist, it is set to NULL. */ const char *value; }; struct retro_game_info { const char *path; /* Path to game, UTF-8 encoded. * Usually used as a reference. * May be NULL if rom was loaded from stdin * or similar. * retro_system_info::need_fullpath guaranteed * that this path is valid. */ const void *data; /* Memory buffer of loaded game. Will be NULL * if need_fullpath was set. */ size_t size; /* Size of memory buffer. */ const char *meta; /* String of implementation specific meta-data. */ }; #define RETRO_MEMORY_ACCESS_WRITE (1 << 0) /* The core will write to the buffer provided by retro_framebuffer::data. */ #define RETRO_MEMORY_ACCESS_READ (1 << 1) /* The core will read from retro_framebuffer::data. */ #define RETRO_MEMORY_TYPE_CACHED (1 << 0) /* The memory in data is cached. * If not cached, random writes and/or reading from the buffer is expected to be very slow. */ struct retro_framebuffer { void *data; /* The framebuffer which the core can render into. Set by frontend in GET_CURRENT_SOFTWARE_FRAMEBUFFER. The initial contents of data are unspecified. */ unsigned width; /* The framebuffer width used by the core. Set by core. */ unsigned height; /* The framebuffer height used by the core. Set by core. */ size_t pitch; /* The number of bytes between the beginning of a scanline, and beginning of the next scanline. Set by frontend in GET_CURRENT_SOFTWARE_FRAMEBUFFER. */ enum retro_pixel_format format; /* The pixel format the core must use to render into data. This format could differ from the format used in SET_PIXEL_FORMAT. Set by frontend in GET_CURRENT_SOFTWARE_FRAMEBUFFER. */ unsigned access_flags; /* How the core will access the memory in the framebuffer. RETRO_MEMORY_ACCESS_* flags. Set by core. */ unsigned memory_flags; /* Flags telling core how the memory has been mapped. RETRO_MEMORY_TYPE_* flags. Set by frontend in GET_CURRENT_SOFTWARE_FRAMEBUFFER. */ }; /* Callbacks */ /* Environment callback. Gives implementations a way of performing * uncommon tasks. Extensible. */ typedef bool (RETRO_CALLCONV *retro_environment_t)(unsigned cmd, void *data); /* Render a frame. Pixel format is 15-bit 0RGB1555 native endian * unless changed (see RETRO_ENVIRONMENT_SET_PIXEL_FORMAT). * * Width and height specify dimensions of buffer. * Pitch specifices length in bytes between two lines in buffer. * * For performance reasons, it is highly recommended to have a frame * that is packed in memory, i.e. pitch == width * byte_per_pixel. * Certain graphic APIs, such as OpenGL ES, do not like textures * that are not packed in memory. */ typedef void (RETRO_CALLCONV *retro_video_refresh_t)(const void *data, unsigned width, unsigned height, size_t pitch); /* Renders a single audio frame. Should only be used if implementation * generates a single sample at a time. * Format is signed 16-bit native endian. */ typedef void (RETRO_CALLCONV *retro_audio_sample_t)(int16_t left, int16_t right); /* Renders multiple audio frames in one go. * * One frame is defined as a sample of left and right channels, interleaved. * I.e. int16_t buf[4] = { l, r, l, r }; would be 2 frames. * Only one of the audio callbacks must ever be used. */ typedef size_t (RETRO_CALLCONV *retro_audio_sample_batch_t)(const int16_t *data, size_t frames); /* Polls input. */ typedef void (RETRO_CALLCONV *retro_input_poll_t)(void); /* Queries for input for player 'port'. device will be masked with * RETRO_DEVICE_MASK. * * Specialization of devices such as RETRO_DEVICE_JOYPAD_MULTITAP that * have been set with retro_set_controller_port_device() * will still use the higher level RETRO_DEVICE_JOYPAD to request input. */ typedef int16_t (RETRO_CALLCONV *retro_input_state_t)(unsigned port, unsigned device, unsigned index, unsigned id); /* Sets callbacks. retro_set_environment() is guaranteed to be called * before retro_init(). * * The rest of the set_* functions are guaranteed to have been called * before the first call to retro_run() is made. */ RETRO_API void retro_set_environment(retro_environment_t); RETRO_API void retro_set_video_refresh(retro_video_refresh_t); RETRO_API void retro_set_audio_sample(retro_audio_sample_t); RETRO_API void retro_set_audio_sample_batch(retro_audio_sample_batch_t); RETRO_API void retro_set_input_poll(retro_input_poll_t); RETRO_API void retro_set_input_state(retro_input_state_t); /* Library global initialization/deinitialization. */ RETRO_API void retro_init(void); RETRO_API void retro_deinit(void); /* Must return RETRO_API_VERSION. Used to validate ABI compatibility * when the API is revised. */ RETRO_API unsigned retro_api_version(void); /* Gets statically known system info. Pointers provided in *info * must be statically allocated. * Can be called at any time, even before retro_init(). */ RETRO_API void retro_get_system_info(struct retro_system_info *info); /* Gets information about system audio/video timings and geometry. * Can be called only after retro_load_game() has successfully completed. * NOTE: The implementation of this function might not initialize every * variable if needed. * E.g. geom.aspect_ratio might not be initialized if core doesn't * desire a particular aspect ratio. */ RETRO_API void retro_get_system_av_info(struct retro_system_av_info *info); /* Sets device to be used for player 'port'. * By default, RETRO_DEVICE_JOYPAD is assumed to be plugged into all * available ports. * Setting a particular device type is not a guarantee that libretro cores * will only poll input based on that particular device type. It is only a * hint to the libretro core when a core cannot automatically detect the * appropriate input device type on its own. It is also relevant when a * core can change its behavior depending on device type. */ RETRO_API void retro_set_controller_port_device(unsigned port, unsigned device); /* Resets the current game. */ RETRO_API void retro_reset(void); /* Runs the game for one video frame. * During retro_run(), input_poll callback must be called at least once. * * If a frame is not rendered for reasons where a game "dropped" a frame, * this still counts as a frame, and retro_run() should explicitly dupe * a frame if GET_CAN_DUPE returns true. * In this case, the video callback can take a NULL argument for data. */ RETRO_API void retro_run(void); /* Returns the amount of data the implementation requires to serialize * internal state (save states). * Between calls to retro_load_game() and retro_unload_game(), the * returned size is never allowed to be larger than a previous returned * value, to ensure that the frontend can allocate a save state buffer once. */ RETRO_API size_t retro_serialize_size(void); /* Serializes internal state. If failed, or size is lower than * retro_serialize_size(), it should return false, true otherwise. */ RETRO_API bool retro_serialize(void *data, size_t size); RETRO_API bool retro_unserialize(const void *data, size_t size); RETRO_API void retro_cheat_reset(void); RETRO_API void retro_cheat_set(unsigned index, bool enabled, const char *code); /* Loads a game. */ RETRO_API bool retro_load_game(const struct retro_game_info *game); /* Loads a "special" kind of game. Should not be used, * except in extreme cases. */ RETRO_API bool retro_load_game_special( unsigned game_type, const struct retro_game_info *info, size_t num_info ); /* Unloads a currently loaded game. */ RETRO_API void retro_unload_game(void); /* Gets region of game. */ RETRO_API unsigned retro_get_region(void); /* Gets region of memory. */ RETRO_API void *retro_get_memory_data(unsigned id); RETRO_API size_t retro_get_memory_size(unsigned id); #ifdef __cplusplus } #endif #endif ================================================ FILE: libretro/libretro_main.c ================================================ // // Copyright 2021 Timo Kloss, Antoine Fauroux // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "libretro_main.h" #include #include #include #include "libretro.h" #include "core.h" #include "boot_intro.h" #define SAMPLING_RATE 44100.0f #define VIDEO_PIXELS SCREEN_WIDTH * SCREEN_HEIGHT #define AUDIO_SAMPLES 1470 static struct retro_log_callback logging; static retro_log_printf_t log; static retro_environment_t environment_callback; static retro_video_refresh_t video_refresh_callback; static retro_audio_sample_t audio_sample_callback; static retro_audio_sample_batch_t audio_sample_batch_callback; static retro_input_poll_t input_poll_callback; static retro_input_state_t input_state_callback; static uint32_t *pixels; static int16_t *audio_buf; static struct Core *core = NULL; static struct CoreDelegate coreDelegate; static struct CoreInput coreInput; static long ticks = 0; static bool hasInput = false; static bool hasUsedInputLastUpdate = false; static bool hasPressesPause = false; static bool hasPressesPauseLastUpdate = false; static bool messageShownUsingDisk = false; static enum MainState mainState = MainStateUndefined; static char *sourceCode = NULL; void bootNX(void); void runMainProgram(void); void interpreterDidFail(void *context, struct CoreError coreError); bool diskDriveWillAccess(void *context, struct DataManager *diskDataManager); void diskDriveDidSave(void *context, struct DataManager *diskDataManager); void diskDriveIsFull(void *context, struct DataManager *diskDataManager); void controlsDidChange(void *context, struct ControlsInfo controlsInfo); void persistentRamWillAccess(void *context, uint8_t *destination, int size); void persistentRamDidChange(void *context, uint8_t *data, int size); /* ======== LibRetro ======== */ static void fallback_log(enum retro_log_level level, const char *fmt, ...) { va_list va; va_start(va, fmt); vfprintf(stderr, fmt, va); va_end(va); } void show_message(const char *txt) { struct retro_message msg = { txt, 120 }; environment_callback(RETRO_ENVIRONMENT_SET_MESSAGE, &msg); } void init_joysticks() { struct retro_input_descriptor desc[] = { // Player 1 {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left"}, {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up"}, {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down"}, {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right"}, {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A"}, {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B"}, // Player 2 {1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left"}, {1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up"}, {1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down"}, {1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right"}, {1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A"}, {1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B"}, {0} }; environment_callback(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc); } /** * Retrieve gamepad information from libretro. */ bool update_gamepad(int player) { // D-Pad coreInput.gamepads[player].up = input_state_callback(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP); coreInput.gamepads[player].down = input_state_callback(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN); coreInput.gamepads[player].left = input_state_callback(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT); coreInput.gamepads[player].right = input_state_callback(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT); // A/B coreInput.gamepads[player].buttonA = input_state_callback(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A); coreInput.gamepads[player].buttonB = input_state_callback(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B); // Pause (Start button) if (input_state_callback(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) { hasPressesPause = true; } struct CoreInputGamepad gamepad = coreInput.gamepads[player]; return gamepad.buttonA || gamepad.buttonB || gamepad.up || gamepad.down || gamepad.left || gamepad.right; } int mouse_pointer_convert(float coord, float full) { float max = 0x7fff; return (int)((coord + max) / (max * 2.0f) * full); } bool update_mouse() { // Get the Pointer X and Y, and convert it to screen position. coreInput.touchX = mouse_pointer_convert(input_state_callback(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X), SCREEN_WIDTH); coreInput.touchY = mouse_pointer_convert(input_state_callback(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y), SCREEN_HEIGHT); coreInput.touch = input_state_callback(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED); return coreInput.touch; } void keyboard_pressed(bool down, unsigned keycode, uint32_t character, uint16_t key_modifiers) { if (down) { hasInput = true; if (character != 0) { // Text input if (character >= RETROK_SPACE && character <= RETROK_UNDERSCORE) { coreInput.key = character; } if (character >= RETROK_a && character <= RETROK_z) { coreInput.key = character - 32; } } switch (keycode) { case RETROK_RETURN: coreInput.key = CoreInputKeyReturn; break; case RETROK_BACKSPACE: coreInput.key = CoreInputKeyBackspace; break; case RETROK_UP: coreInput.key = CoreInputKeyUp; break; case RETROK_DOWN: coreInput.key = CoreInputKeyDown; break; case RETROK_LEFT: coreInput.key = CoreInputKeyLeft; break; case RETROK_RIGHT: coreInput.key = CoreInputKeyRight; break; } } } /* Sets callbacks. retro_set_environment() is guaranteed to be called * before retro_init(). * * The rest of the set_* functions are guaranteed to have been called * before the first call to retro_run() is made. */ RETRO_API void retro_set_environment(retro_environment_t callback) { environment_callback = callback; bool no_content = false; callback(RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME, &no_content); if (callback(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &logging)) log = logging.log; else log = fallback_log; enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888; callback(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt); struct retro_keyboard_callback kcb = { keyboard_pressed }; callback(RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK, &kcb); } RETRO_API void retro_set_video_refresh(retro_video_refresh_t callback) { video_refresh_callback = callback; } RETRO_API void retro_set_audio_sample(retro_audio_sample_t callback) { audio_sample_callback = callback; } RETRO_API void retro_set_audio_sample_batch(retro_audio_sample_batch_t callback) { audio_sample_batch_callback = callback; } RETRO_API void retro_set_input_poll(retro_input_poll_t callback) { input_poll_callback = callback; } RETRO_API void retro_set_input_state(retro_input_state_t callback) { input_state_callback = callback; } /* Library global initialization/deinitialization. */ RETRO_API void retro_init(void) { log(RETRO_LOG_INFO, "[LowRes NX] Initialization\n"); core = calloc(1, sizeof(struct Core)); if (core) { core_init(core); coreDelegate.interpreterDidFail = interpreterDidFail; coreDelegate.diskDriveWillAccess = diskDriveWillAccess; coreDelegate.diskDriveDidSave = diskDriveDidSave; coreDelegate.diskDriveIsFull = diskDriveIsFull; coreDelegate.controlsDidChange = controlsDidChange; coreDelegate.persistentRamWillAccess = persistentRamWillAccess; coreDelegate.persistentRamDidChange = persistentRamDidChange; core_setDelegate(core, &coreDelegate); } pixels = calloc(VIDEO_PIXELS, sizeof(uint32_t)); audio_buf = calloc(AUDIO_SAMPLES, sizeof(int16_t)); init_joysticks(); bootNX(); } RETRO_API void retro_deinit(void) { log(RETRO_LOG_INFO, "[LowRes NX] Deinitialization\n"); if (core) { core_deinit(core); free(core); core = NULL; } if (pixels) { free(pixels); pixels = NULL; } if (audio_buf) { free(audio_buf); audio_buf = NULL; } } /* Must return RETRO_API_VERSION. Used to validate ABI compatibility * when the API is revised. */ RETRO_API unsigned retro_api_version(void) { return RETRO_API_VERSION; } /* Gets statically known system info. Pointers provided in *info * must be statically allocated. * Can be called at any time, even before retro_init(). */ RETRO_API void retro_get_system_info(struct retro_system_info *info) { memset(info, 0, sizeof(*info)); info->library_name = "LowRes NX"; info->library_version = CORE_VERSION; info->need_fullpath = false; info->valid_extensions = "nx"; } /* Gets information about system audio/video timings and geometry. * Can be called only after retro_load_game() has successfully completed. * NOTE: The implementation of this function might not initialize every * variable if needed. * E.g. geom.aspect_ratio might not be initialized if core doesn't * desire a particular aspect ratio. */ RETRO_API void retro_get_system_av_info(struct retro_system_av_info *info) { info->timing.fps = 60.0; info->timing.sample_rate = SAMPLING_RATE; info->geometry.base_width = SCREEN_WIDTH; info->geometry.base_height = SCREEN_HEIGHT; info->geometry.max_width = SCREEN_WIDTH; info->geometry.max_height = SCREEN_HEIGHT; info->geometry.aspect_ratio = 0.0f; } /* Sets device to be used for player 'port'. * By default, RETRO_DEVICE_JOYPAD is assumed to be plugged into all * available ports. * Setting a particular device type is not a guarantee that libretro cores * will only poll input based on that particular device type. It is only a * hint to the libretro core when a core cannot automatically detect the * appropriate input device type on its own. It is also relevant when a * core can change its behavior depending on device type. */ RETRO_API void retro_set_controller_port_device(unsigned port, unsigned device) { } /* Resets the current game. */ RETRO_API void retro_reset(void) { log(RETRO_LOG_INFO, "[LowRes NX] Reset\n"); runMainProgram(); } /* Runs the game for one video frame. * During retro_run(), input_poll callback must be called at least once. * * If a frame is not rendered for reasons where a game "dropped" a frame, * this still counts as a frame, and retro_run() should explicitly dupe * a frame if GET_CAN_DUPE returns true. * In this case, the video callback can take a NULL argument for data. */ RETRO_API void retro_run(void) { input_poll_callback(); if (core && pixels && audio_buf) { for (int i = 0; i < NUM_GAMEPADS; ++i) { if (update_gamepad(i)) { hasInput = true; } } if (hasPressesPause && !hasPressesPauseLastUpdate) { coreInput.pause = true; } if (update_mouse()) { hasInput = true; } switch (mainState) { case MainStateUndefined: break; case MainStateBootIntro: core_update(core, &coreInput); if (machine_peek(core, bootIntroStateAddress) == BootIntroStateReadyToRun) { machine_poke(core, bootIntroStateAddress, BootIntroStateDone); runMainProgram(); } break; case MainStateRunningProgram: core_update(core, &coreInput); if (hasInput) { if (core->interpreter->state == StateEnd) { show_message("End of program"); } else if (!coreInput.out_hasUsedInput && !hasUsedInputLastUpdate) { // user hints for controls union IOAttributes attr = core->machine->ioRegisters.attr; if (attr.touchEnabled && !attr.keyboardEnabled) { show_message("Touch/mouse expected"); } if (attr.keyboardEnabled && !attr.touchEnabled) { show_message("Keyboard expected"); } if (attr.gamepadsEnabled && !attr.keyboardEnabled) { show_message("Joypad expected"); } } } break; } hasUsedInputLastUpdate = coreInput.out_hasUsedInput; video_renderScreen(core, pixels); video_refresh_callback(pixels, SCREEN_WIDTH, SCREEN_HEIGHT, sizeof(uint32_t) * SCREEN_WIDTH); audio_renderAudio(core, audio_buf, AUDIO_SAMPLES, SAMPLING_RATE, 0); audio_sample_batch_callback(audio_buf, AUDIO_SAMPLES / 2); } hasInput = false; hasPressesPauseLastUpdate = hasPressesPause; hasPressesPause = false; ++ticks; } /* Returns the amount of data the implementation requires to serialize * internal state (save states). * Between calls to retro_load_game() and retro_unload_game(), the * returned size is never allowed to be larger than a previous returned * value, to ensure that the frontend can allocate a save state buffer once. */ RETRO_API size_t retro_serialize_size(void) { return 0; } /* Serializes internal state. If failed, or size is lower than * retro_serialize_size(), it should return false, true otherwise. */ RETRO_API bool retro_serialize(void *data, size_t size) { return false; } RETRO_API bool retro_unserialize(const void *data, size_t size) { return false; } RETRO_API void retro_cheat_reset(void) { } RETRO_API void retro_cheat_set(unsigned index, bool enabled, const char *code) { } /* Loads a game. */ RETRO_API bool retro_load_game(const struct retro_game_info *game) { log(RETRO_LOG_INFO, "[LowRes NX] Load game\n"); if (core && game && game->data) { sourceCode = calloc(1, game->size + 1); // +1 for terminator if (sourceCode) { memcpy(sourceCode, game->data, game->size); if (mainState == MainStateBootIntro) { machine_poke(core, bootIntroStateAddress, BootIntroStateProgramAvailable); } else { runMainProgram(); } return true; } } return false; } /* Loads a "special" kind of game. Should not be used, * except in extreme cases. */ RETRO_API bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info) { return false; } /* Unloads a currently loaded game. */ RETRO_API void retro_unload_game(void) { if (core) { core_willSuspendProgram(core); } if (sourceCode) { free(sourceCode); sourceCode = NULL; } } /* Gets region of game. */ RETRO_API unsigned retro_get_region(void) { return RETRO_REGION_NTSC; } /* Gets region of memory. */ RETRO_API void *retro_get_memory_data(unsigned id) { switch (id) { case RETRO_MEMORY_SAVE_RAM: return core->machine->persistentRam; default: return NULL; } } RETRO_API size_t retro_get_memory_size(unsigned id) { switch (id) { case RETRO_MEMORY_SAVE_RAM: return PERSISTENT_RAM_SIZE; default: return 0; } } /* ======== LowRes NX ======== */ void bootNX() { if (!core) return; mainState = MainStateBootIntro; struct CoreError error = core_compileProgram(core, bootIntroSourceCode, true); if (error.code != ErrorNone) { core_traceError(core, error); } core->interpreter->debug = false; core_willRunProgram(core, ticks / 60); } void runMainProgram() { if (!core || !sourceCode) return; core_willSuspendProgram(core); struct CoreError error = core_compileProgram(core, sourceCode, false); if (error.code != ErrorNone) { core_traceError(core, error); } else { core_willRunProgram(core, ticks / 60); mainState = MainStateRunningProgram; } messageShownUsingDisk = false; } /** Called on error */ void interpreterDidFail(void *context, struct CoreError coreError) { core_traceError(core, coreError); } /** Returns true if the disk is ready, false if not. In case of not, core_diskLoaded must be called when ready. */ bool diskDriveWillAccess(void *context, struct DataManager *diskDataManager) { if (!messageShownUsingDisk) { show_message("No virtual disk"); messageShownUsingDisk = true; } return true; } /** Called when a disk data entry was saved */ void diskDriveDidSave(void *context, struct DataManager *diskDataManager) { show_message("No virtual disk"); } /** Called when a disk data entry was tried to be saved, but the disk is full */ void diskDriveIsFull(void *context, struct DataManager *diskDataManager) { } /** Called when keyboard or gamepad settings changed */ void controlsDidChange(void *context, struct ControlsInfo controlsInfo) { } /** Called when persistent RAM will be accessed the first time */ void persistentRamWillAccess(void *context, uint8_t *destination, int size) { } /** Called when persistent RAM should be saved */ void persistentRamDidChange(void *context, uint8_t *data, int size) { } ================================================ FILE: libretro/libretro_main.h ================================================ // // Copyright 2021 Timo Kloss, Antoine Fauroux // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef libretro_main_h #define libretro_main_h #include enum MainState { MainStateUndefined, MainStateBootIntro, MainStateRunningProgram }; #endif /* libretro_main_h */ ================================================ FILE: platform/GameShell/README.md ================================================ # ClockworkPi GameShell Compile the Linux version and copy it to /home/cpi/apps. Copy this folder to the GameShell to add LowRes NX to your menu. ================================================ FILE: platform/GameShell/home/cpi/apps/Menu/55_LowRes NX/action.config ================================================ ROM=/home/cpi/games/LowResNX EXT=nx LAUNCHER=/home/cpi/apps/LowResNX -disabledev yes -fullscreen yes -zoom 3 -mapping 1 -disabledelay yes TITLE=LowRes NX Games ================================================ FILE: platform/GameShell/home/cpi/games/LowResNX/LowRes Galaxy 2 (1.5).nx ================================================ 'TITLE: LOWRES GALAXY 2 'AUTHOR: TIMO KLOSS RANDOMIZE TIMER ' SPRITES ' 0 PLAYER SHIP ' 1 JET ' 2-9 PLAYER BULLETS ' 10-25 ALIENS ' 26-29 EXPLOSIONS ' 30-39 ALIEN BULLETS 'POINTS FOR ALIENS DIM GLOBAL POINTS(4) DATA 0,10,20,400,600 FOR I=0 TO 4 READ POINTS(I) NEXT I 'ALIENS DIM GLOBAL ALIENS(15,5) ' 0 TYPE (0=DISABLED) ' 1 TICK ' 2 START Y ' 3 HITS ' 4 AMPLITUDE ' 5 SPEED 'ALIEN BULLETS DIM GLOBAL ABULLETS(9,3) ' 0 X ' 1 Y ' 2 X VECTOR ' 3 Y VECTOR 'EXPLOSION TICKS DIM GLOBAL EXPLOSIONS(3) GLOBAL HIGHSCORE GLOBAL TICK,BGTICK GLOBAL LEVEL,SCORE,LIVES GLOBAL PX,PY GLOBAL SHIELD,HIDE,PEACE GLOBAL SHDELAY,HEAT,BULLET GLOBAL EXPLOSION GLOBAL ALIEN,ABULLET GLOBAL MSGTIMER FONT 128 CALL DRAWGAMEBG ON RASTER CALL RASTERFX GAMEPAD 1 'READ FROM PERSISTENT RAM HIGHSCORE=PEEKL($E000) TITLE: TICK=0 SPRITE OFF CALL DRAWTITLE SOUND SOURCE ROM(15) MUSIC 0 REPEAT INC BGTICK IF TICK MOD 240=0 THEN CALL SHOWMSG("PRESS ANY BUTTON") ELSE IF TICK MOD 240=120 THEN CALL SHOWMSG("HIGHSCORE: "+STR$(HIGHSCORE)) END IF INC TICK WAIT VBL UNTIL BUTTON TAP(0) STOP GAME: 'INIT PLAYER SPRITES SPRITE 0 PAL 4 SIZE 1 SPRITE 1 PAL 5 FOR I=2 TO 9 SPRITE I PAL 5 NEXT I 'INIT VARIABLES LIVES=5 SCORE=0 LEVEL=0 PX=32 PY=48 SHIELD=120 HIDE=0 PEACE=0 SHDELAY=0 HEAT=0 TICK=0 BULLET=0 ALIEN=0 FOR I=0 TO 15 ALIENS(I,0)=0 NEXT I FOR I=0 TO 9 ABULLETS(I,0)=-32 NEXT I CALL CLEAROVERLAYS CALL DRAWHUD CALL RESETSOUND SOUND SOURCE ROM(15) MUSIC 8 SOUND SOURCE ROM(14) 'GAME LOOP DO 'NEXT LEVEL? IF LIVES>0 AND TICK MOD 1800=0 THEN INC LEVEL CALL SHOWMSG("LEVEL "+STR$(LEVEL)) IF LEVEL>=2 THEN CALL ADDSCORE(LEVEL*100) TRACK 0,3 END IF END IF IF PEACE>0 THEN 'DO NOT SPAWN ALIENS DEC PEACE ELSE 'SPAWN SMALL ALIEN? M=480/(LEVEL+3) IF TICK MOD M=0 THEN CALL SPAWNALIEN(1+RND(1)) END IF 'SPAWN BIG ALIEN? M=5400/(LEVEL+2) IF TICK MOD M=M\2 THEN CALL SPAWNALIEN(3+RND(1)) END IF END IF CALL UPDALIENS CALL UPDBULLETS CALL UPDALIENBULLETS IF LIVES>0 THEN CALL UPDPLAYER ELSE 'GAME OVER IF MSGTIMER>0 THEN DEC MSGTIMER ELSE IF BUTTON TAP(0) THEN GOTO TITLE END IF END IF CALL UPDEXPLOSIONS CALL UPDMSG INC TICK INC BGTICK WAIT VBL LOOP SUB ADDSCORE(P) ADD SCORE,P CALL DRAWHUD END SUB SUB UPDPLAYER IF HIDE>0 THEN DEC HIDE SPRITE OFF 0 TO 1 EXIT SUB END IF 'PLAYER CONTROL IF UP(0) AND PY>0 THEN DEC PY IF DOWN(0) AND PY<112 THEN INC PY IF LEFT(0) AND PX>8 THEN DEC PX IF RIGHT(0) AND PX<128 THEN INC PX 'PLAYER SPRITE IF SHIELD>0 AND SHIELD MOD 4<2 THEN SPRITE OFF 0 TO 1 ELSE SPRITE 0,PX,PY,1 SPRITE 1,PX-8,PY+5,19+INT((TICK MOD 16)/8) END IF 'SHOOT? SHDELAY=SHDELAY-1 IF BUTTON(0) THEN IF SHDELAY<=0 THEN SPRITE BULLET+2,PX+8,PY+11,3 BULLET=(BULLET+1) MOD 8 PLAY 2,50+RND*2 SOUND 0 HEAT=HEAT+1 IF HEAT>=5 THEN SHDELAY=30 HEAT=0 ELSE SHDELAY=8 END IF END IF ELSE HEAT=0 END IF IF SHIELD>0 THEN DEC SHIELD ELSE 'PLAYER HIT BY BULLET? IF SPRITE HIT(0,30 TO 39) THEN SPRITE OFF HIT ABULLETS(HIT-30,0)=-32 CALL LOSESHIP END IF 'COLLISION WITH ALIEN? IF SPRITE HIT(0,10 TO 25) THEN CALL LOSESHIP END IF END IF END SUB SUB LOSESHIP DEC LIVES CALL DRAWHUD SPRITE OFF 0 TO 1 PLAY 2,30 SOUND 4 CALL EXPLODE(PX,PY,0) CALL EXPLODE(PX-8+RND*16,PY-8+RND*16,10) CALL EXPLODE(PX-8+RND*16,PY-8+RND*16,30) IF LIVES=0 THEN CALL DRAWGAMEOVER IF SCORE>HIGHSCORE THEN HIGHSCORE=SCORE CALL SHOWMSG("NEW HIGHSCORE!") 'WRITE TO PERSISTENT RAM POKEL $E000,HIGHSCORE ELSE CALL SHOWMSG("HIGHSCORE: "+STR$(HIGHSCORE)) END IF SOUND SOURCE ROM(15) MUSIC 32 SOUND SOURCE ROM(14) MSGTIMER=100 ELSE HIDE=120 SHIELD=120 PEACE=180 END IF END SUB SUB UPDBULLETS FOR I=2 TO 9 IF SPRITE.X(I)>=0 THEN SPRITE I,SPRITE.X(I)+3,, IF SPRITE.X(I)>160 THEN SPRITE OFF I END IF NEXT I END SUB SUB SPAWNALIEN(TYPE) I=ALIEN IF ALIENS(I,0)>0 THEN EXIT SUB ALIENS(I,0)=TYPE ALIENS(I,1)=0 ALIENS(I,2)=16+RND*80 IF TYPE=1 THEN ALIENS(I,3)=1 ALIENS(I,4)=RND*12 ALIENS(I,5)=0.5 ELSE IF TYPE=2 THEN ALIENS(I,3)=1 ALIENS(I,4)=RND*20 ALIENS(I,5)=0.35 ELSE IF TYPE=3 THEN ALIENS(I,3)=4 ALIENS(I,4)=RND*32 ALIENS(I,5)=0.25 ELSE IF TYPE=4 THEN ALIENS(I,3)=8 ALIENS(I,4)=RND*40 ALIENS(I,5)=0.15 END IF ALIEN=(ALIEN+1) MOD 16 END SUB SUB UPDALIENS FOR I=0 TO 15 IF ALIENS(I,0)>0 THEN CALL UPDALIEN(I) END IF NEXT I END SUB SUB UPDALIEN(I) TYPE=ALIENS(I,0) N=10+I ALIENS(I,1)=ALIENS(I,1)+1 T=ALIENS(I,1) X=160-T*ALIENS(I,5) X=X-SIN(T*0.03)*24 'STILL ON SCREEN? IF X>-32 THEN 'UPDATE SPRITE Y=ALIENS(I,2) AY=ALIENS(I,4) IF TYPE MOD 2=0 THEN P=6 ELSE P=7 IF TYPE<=2 THEN S=0 C=10+INT((T MOD 24)/12) Y=Y+SIN(T/20)*AY ELSE S=1 C=6+INT((T MOD 32)/16)*2 Y=Y+SIN(T/60)*AY END IF SPRITE N PAL P SIZE S SPRITE N,X,Y,C 'HIT BY BULLET? IF SPRITE HIT(N,2 TO 9) THEN SPRITE OFF HIT SPRITE N PAL 3 ALIENS(I,3)=ALIENS(I,3)-1 IF ALIENS(I,3)=0 THEN CALL ADDSCORE(POINTS(TYPE)) SPRITE OFF N ALIENS(I,0)=0 IF S=0 THEN CALL EXPLODE(X-4,Y-4,0) ELSE CALL EXPLODE(X,Y,0) END IF IF TYPE>=3 THEN PLAY 3,40 SOUND 5 ELSE PLAY 3,45 SOUND 2 END IF END IF 'SHOOT? IF PEACE=0 AND X>50 AND T MOD 120=30 THEN CALL ALIENSHOOT(X+4,Y+4) END IF ELSE 'OUT OF SCREEN, RESET SPRITE OFF N ALIENS(I,0)=0 END IF END SUB SUB ALIENSHOOT(X,Y) I=ABULLET IF ABULLETS(I,0)>-32 THEN EXIT SUB N=I+30 PLAY 3,45+RND*2 SOUND 1 SPRITE N,X,Y,4 SPRITE N PAL 3 ABULLETS(I,0)=X ABULLETS(I,1)=Y U=PX+6-X V=PY+6-Y W=SQR(U*U+V*V) ABULLETS(I,2)=U/W ABULLETS(I,3)=V/W ABULLET=(ABULLET+1) MOD 3 END SUB SUB UPDALIENBULLETS FOR I=0 TO 9 N=I+30 X=ABULLETS(I,0) Y=ABULLETS(I,1) X=X+ABULLETS(I,2) Y=Y+ABULLETS(I,3) IF X>=-8 AND X<160 AND Y>=-8 AND Y<128 THEN ABULLETS(I,0)=X ABULLETS(I,1)=Y SPRITE N,X,Y, ELSE ABULLETS(I,0)=-32 SPRITE OFF N END IF NEXT I END SUB SUB EXPLODE(X,Y,DELAY) N=EXPLOSION+40 SPRITE N PAL 5 SIZE 0 SPRITE N,X,Y,0 EXPLOSIONS(EXPLOSION)=20+DELAY EXPLOSION=(EXPLOSION+1) MOD 4 END SUB SUB UPDEXPLOSIONS FOR I=0 TO 3 T=EXPLOSIONS(I) IF T>0 THEN IF T<=20 THEN N=I+40 SPRITE N SIZE 1 SPRITE N,,,32+((20-T)\5)*2 IF T=20 THEN PLAY 3,25+RND*5 SOUND 3 END IF DEC T IF T=0 THEN SPRITE OFF N EXPLOSIONS(I)=T END IF NEXT I END SUB SUB DRAWHUD BG 0 PAL 4 PRIO 1 BG FILL 0,0 TO 4,0 CHAR 0 FOR I=0 TO LIVES-1 CELL I,0,5 NEXT I PAL 0 NUMBER 15,0,SCORE,5 END SUB SUB DRAWGAMEBG BG SOURCE ROM(3) BG 0 BG COPY 0,0,32,16 TO 0,0 BG 1 BG COPY 0,16,32,16 TO 0,0 END SUB SUB DRAWTITLE BG 0 BG FILL 0,0 TO 19,6 CHAR 0 BG SOURCE ROM(4) BG COPY 0,0,20,6 TO 4,1 END SUB SUB DRAWGAMEOVER BG 0 BG SOURCE ROM(4) BG COPY 0,7,8,2 TO 1,5 BG COPY 9,7,8,2 TO 11,5 END SUB SUB CLEAROVERLAYS BG 0 BG FILL 0,0 TO 19,6 CHAR 0 BG 1 BG FILL 0,11 TO 19,11 CHAR 0 END SUB SUB SHOWMSG(MSG$) BG 1 PAL 0 PRIO 1 L=LEN(MSG$) BG FILL 0,11 TO 19,11 CHAR 0 TEXT (20-L)/2,11,MSG$ MSGTIMER=120 END SUB SUB UPDMSG IF MSGTIMER>0 THEN DEC MSGTIMER IF MSGTIMER=0 THEN BG 1 BG FILL 0,11 TO 19,11 CHAR 0 END IF END IF END SUB SUB RESETSOUND STOP FOR I=0 TO 3 VOLUME I,15,%11 NEXT I END SUB SUB RASTERFX 'STARS AND FOREGROUND HILLS IF RASTER=0 THEN SCROLL 1,BGTICK/6,0 ELSE IF RASTER=88 THEN SCROLL 1,0,0 ELSE IF RASTER=96 THEN SCROLL 1,BGTICK,0 END IF 'STATUS BAR AND PLANET SURFACE IF RASTER=0 THEN SCROLL 0,0,0 ELSE IF RASTER=58 THEN SCROLL 0,BGTICK*0.5,0 ELSE IF RASTER=80 THEN SCROLL 0,BGTICK*5/8,0 ELSE IF RASTER=96 THEN SCROLL 0,BGTICK*6/8,0 ELSE IF RASTER=112 THEN SCROLL 0,BGTICK*7/8,0 END IF END SUB #1:MAIN PALETTES 003F1B0500261101003B2612003F3E39 001F2A15003C3006003F3121003F1612 #2:MAIN CHARACTERS 00000000000000000000000000000000 000040600078FF54000040607078FFFF 000000000000FC02000000000000FCFE 04E733040000000006F83C0600000000 60F0F060000000006090906000000000 406070821E764040406070FEF0766040 00041030400C8E0E00071F2F7F73F5F5 002008040260E1E000E0F8FCFE9E5F5F 041028400C8E0E06071F377F73F5F5F9 2008040260E1E0C0E0F8FCFE9E5F5F3F 0042816600006642007EFFBDFFFF6642 42816666000066247EFFBDBDFF7E6624 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 A8FF007802604000FFFF7C787C604000 7FBDFA7C00000000FFC3867C00000000 100409A30904100010060EBC0E061000 40120413041240004012071C07124000 00000000000000000000000000000000 040080403C383018FBFFFF7F3C383018 400001023C1C0C18BFFFFFFE3C1C0C18 800040203C1C0E02FF7F7F3F3C1C0E02 010002043C387040FFFEFEFC3C387040 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 000001070F1F1F3F0000010608101020 000080E0F0F8F8FC0000806010080804 071F3F7F7FFFFFFF0610204000808000 E0F8FCFEFEFFFFFF6008040200010100 071F3F7C78F1E2E404102043078F1E1C E0F81C02780102002008E4FEFE030301 020816285020A000030F1E387060E0C0 4010280400000000C0F0380400000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 3F1F1F0F070100002010100806010000 FCF8F8F0E08000000408081060800000 FFFFFF7F7F3F1F070080800040201006 FFFFFFFEFEFCF8E00001010002040860 C0C8C86868301A043838B81858281607 00000000000000000000000000000000 0080204020100000C0C0606020100000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00400000000002000000000000000200 00000000000000000000020000000000 000008002A0008000000080836080800 1F604080868C8C8C001F3F7F7E7C7C7C 6018180C0C000000E0F8F8FCFC000000 1F604080868C8C8C001F3F7F7F7C7C7C 6018180C0C0C8C8CE0F8F8FCFCFC7C7C EC8C8C8C8C8C8C8C1C7C7C7C7C7C7C7C 00000000000000000000000000000000 EC8C8C8C4C4700001C7C7C7C3C3E3F1F EC8C8C8C981830601C7C7C7C78F8F0E0 00000000000000000000000000000000 00000000000000000000000000000000 FF80800003232323007F7FFF3F1F1F1F FE0000000323232301FFFFFF3F1F1F1F C0C0C0C000000000C0C0C0C000000000 00000000000000000000100000000000 00000000000004000000000000000400 00483030480000000078484878000000 8C8C8C86030000017C7C7C7EFC7F7F1F EC8C8C8C0C0C0C0C1C7C7C7CFCFCFCFC 878080808C8C8C0C787F7F7F7C7C7CFC 8C0C0C0C8C8C8C8C7CFCFCFC7C7C7C7C 8C8C8C8C878080007C7C7C7C787F7FFF 00000000EC0C0C1C000000001CFCFCFC 2046468C8C8C8C0C1F3F3F7C7C7C7CFC 3018180C0C8C8C0CF0F8F8FCFC7C7CFC 0808080808080800070707070707070F C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0 23232323E18080001F1F1F1F1E7F7FFF 23232323E00000001F1F1F1F1FFFFFFF 00000000C0C0C0C000000000C0C0C0C0 0000000000000000FFFFFFFFFFFFFFFF 00007EC3C37E0000FFFFFFFCFC81FFFF 00000000000000000000000000000000 000000000000082200000000001876DF 00000000000000000000000000000000 B0988C878280808070787C7E7D7F7F7F 2C4C8C0C0C0C0C0C1C3C7CFCFCFCFCFC FF8080808C8C8680007F7F7F7C7C797F EC0C0C0C0000C0C01CFCFCFC0000C0C0 8C8C8C07020000007C7C7CFE7D3F1F0F 8C8C8C0C183060C07C7C7CFCF8F0E0C0 FF8080808C8C8C8D007F7F7F7D7C7C7C 6018180C0C0C0C0CE0F8F8FCFCFCFCFC 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 0000000038443800FFFFFFFFFFFBC7FF 00001C0E00000000FFFFFFF1FFFFFFFF 000001040102164C0103060B1E3D69B3 0000000000000000FFFFFFFFFFFFFFFF 00804060B010280480C0E0F0F8FCFEFF 898C8C8C8C8C8C0C7F7C7C7C7C7C7CFC 8C8C8C8C8C8C8C0C7C7C7C7C7C7C7CFC 80808C8C878080007F7F7C7C787F7FFF C0C00000EC0C0C0CC0C000001CFCFCFC 8C8C8C87020000007C7C7C7EFD7F7F1F 8C8C8C0C0C1818607C7C7CFCFCF8F8E0 868080808C8C8C0C797F7F7F7F7D7CFC 18186030180C0C0CF8F8E0F0F8FCFCFC 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00181818180018000000000000000000 006C6C24000000000000000000000000 00247E24247E24000000000000000000 00083E380E3E08000000000000000000 00626408102646000000000000000000 001C34386E643A000000000000000000 00181830000000000000000000000000 000C183030180C000000000000000000 0030180C0C1830000000000000000000 000024187E1824000000000000000000 000018187E1818000000000000000000 00000000181830000000000000000000 000000007E0000000000000000000000 00000000001818000000000000000000 00060C18306040000000000000000000 003C666E76663C000000000000000000 0018381818187E000000000000000000 003C660C18307E000000000000000000 003C660C06663C000000000000000000 0066667E060606000000000000000000 007E607C06067C000000000000000000 001C307C66663C000000000000000000 007E060C183030000000000000000000 003C663C66663C000000000000000000 003C663E06663C000000000000000000 00000018001800000000000000000000 00000018001830000000000000000000 00000C1830180C000000000000000000 0000007E007E00000000000000000000 000030180C1830000000000000000000 003C660C180018000000000000000000 003C666E6E603C000000000000000000 00183C667E6666000000000000000000 007C667C66667C000000000000000000 003C666060663C000000000000000000 00786C66666C78000000000000000000 007E607860607E000000000000000000 007E6078606060000000000000000000 003C606E66663C000000000000000000 0066667E666666000000000000000000 003C181818183C000000000000000000 001E060606663C000000000000000000 00666C78786C66000000000000000000 0060606060607E000000000000000000 0042667E7E6666000000000000000000 0066767E6E6666000000000000000000 003C666666663C000000000000000000 007C667C606060000000000000000000 003C66666A6C3E000000000000000000 007C667C786C66000000000000000000 003E603C06067C000000000000000000 007E1818181818000000000000000000 0066666666663C000000000000000000 00666666663C18000000000000000000 0066667E7E6642000000000000000000 00663C183C6666000000000000000000 0066663C181818000000000000000000 007E0C1830607E000000000000000000 003C303030303C000000000000000000 006030180C0602000000000000000000 003C0C0C0C0C3C000000000000000000 00183C66000000000000000000000000 0000000000007E000000000000000000 #3:MAIN BG 00002020000000000000002100210021 00210000002100210021000000000000 00000021002100210021002100000021 00210000000000210021002100210021 00210021000000000000002100210000 00210000002100210021000000000000 00000021002100210000002100000021 00000021000000000021002100210021 00210021002100000000000000210021 00210021002100210021000000210000 00210021002100000021002100000021 00210021002100000000002100000021 00000021000000210000000000000000 00000000002100210021000000000021 00000021000000000021002100000000 00000000000000000021002100210000 00210021000000000000002100210021 00210021000000210021002100000021 00210021000000210021002100000021 00210021002100000021002100210000 00000000000000000000002100210021 00210000002100210021002100210021 00210000000000210021002100000021 00210021002100000000002100210021 00000000002100210021000000000021 00210021000000000021002100210021 00000000002100210021002100000021 00210021002100210000002100210021 00210021002100210021002100210021 00210021002100216201630164010021 00210021000000210021002100210021 00006201630164010021002100210021 00210001000100016201630164010001 62010001640163017201730174010001 00016201630164010021002100010001 63017201730174016301002100216201 63016401630163017201730174016301 63016301720173017301730173017401 63017201730174016301630163017201 73017301730173017301740163017201 73017401600160016001600160016001 60016001710160017301730173016001 60016001710160016001600160016001 71016001600160016001600160016001 60016001700160016001710170017101 70016001710160017001600170017101 70016001600171016001600160017101 60016001710160016001710160016001 71016001600160016001710160016001 60016001600160016001600160016001 60017001610160016001710170016001 60016001700160016001600160016001 60016001700171016001600160017001 60016101700171016001600170016001 60016001700160017101600160016001 61016001600170016001710170017101 70017101600160017001600160016001 60017001600160016001610160017001 60016101600160016001700160017101 60016001600161016001700160016001 60016101700160016001610160017001 60016001600170016001600160017101 70017101600171017001710170016001 70017101700160016001600161016001 70017101400041000000000000000000 51000000000000005000000000000000 50000000000000000000000000000000 00000000000000000000500000000000 40004100500051000000400041000000 00000000000000000000510000000000 00005100000000004000410000000000 00004000410000000000000000000000 50004200000051000000500051000000 50000000420041000000000040004100 00000000510000005000510000000000 00005000510000000000400041000000 00000000000000000000000051000000 51000000500051005000000050005100 00005100000000000000000000000000 00000000000000005100500051000000 51000000000040004100000000000000 00000000000000000000000000000000 00004200000041000000000000005100 40004100000000005201000000000000 00000000000050005100500000004000 50000000000040004100410000000000 00000000510000000000500000000000 50005100000000000000400041000000 00000000000000005000000000005000 51000000000050005100000000004000 50000000500000005100000000005000 00000000000051000000500051000000 00000000000000005000410000000000 00004000410000000000000000005000 51000000000000000000000051000000 00005100000050000000000000004000 41000000500000005000420000005100 62010001640100005201510000005100 50005100000040004100000000004200 00000000000000000000510000005000 51000000510000000000000062016201 00010001000100005100000000000000 00000000000050005100000051000000 50000000000000005100000051000000 00000000400062016201620100010001 00010001500000000000000062220002 64225100000051000000000000000000 00000000400041000000000000000000 00000000500000010001000100010001 41000000000000010001000100020002 64020001000100010001000100010001 00010001000100010001000100010001 00010000000000000000000000000001 00210020002000220002002200226322 00220002000000000000000100010001 00010001000000000000000100010001 00010000000000006221632264220020 00000002000262020002002272227322 74220022000200016222632264220000 00000000000000000000000062226322 64010000000000027222732274220020 00210022000200226322722270227322 73227422002200027222732274226322 63226402000062026322632272227322 74220022002272227322712273227422 00220002002272227322732273226122 73227122742272227322732273227322 73227422002072227322732271227322 73227422 #4:OVERLAY BG 00001410000000000000AC20AF20B720 B220A520B32000000000000000000000 00000000000000000000000043204420 45204620472048204520462049204A20 49204A20000000000000000000000000 00000000532054205520562057205820 5520562059205A205B205C2000000000 00000000000000000000000000000000 00000000000200020002000200000000 00000000000000000000000000000000 00000000000000000000000200024D22 4E224F22000000000000000000000000 00000000000000000000000000000000 0000000000025D225E225F2200000000 00000000000000000000000000000000 00000000000000000000000000020000 00000000000000000000000000000000 00000000000000000000000043254425 45254625652566256725682580254525 462547254725672568256B256C250000 00000000532554255525562575257625 77257825002579257A2569256A257725 78257B257C2500000000000000000005 00050000000000000000000000000000 00000005000500250025000500050005 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000 #14:SFX 5801505019FC00005801506019FB0000 4801004F19FF00007806007F01FE0000 780A04AF19FD00007805027F19FD0000 0800505012FF00000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 296FE6306F00356F002A6F00316F0036 6F002C6F00336F00386F00FF00000000 E0000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 #15:MAIN SOUND 230030AA0004A0002800606019000000 22006060000250002800303019FE0000 38002020000000003800606000000000 21004068120B30002400418A10372000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 800103180002041A000103180002041A 000116180002171A000116180082171A 85064040050B4040090A4040090C4040 05064040050B40400D0E40400D0F4040 11104040111040401314404013144040 13154040139540404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 87884040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 193F00000000193F00000000315F0019 3F00000000193F00000000554500193F 00000000315F00000000000000000000 193F00000000193F00000000315F0019 3F00000000193F00000000000000193F 00554500315F00000000554500554800 1D0F0000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 1B0F0000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 190F0000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 1B0F0000000000000000000000000000 00000000000000000000000000000000 00000000180F00000000000000000000 356F00000000356F00000000386F0000 0000356F00000000000000000000386F 00000000FF0000000000356F00000000 3C6F000000000000000000003A6F0000 0000386F000000000000000000003A6F 00000000000000FF0000000000000000 3D6F000000000000000000003C6F0000 00003A6F00000000FF0000000000356F 00000000000000000000FF0000000000 386F00000000386F000000003A6F0000 0000386F00000000000000000000376F 00000000000000000000FF0000000000 1B2FE81B2F000000001B2F001C2F0000 00001B2F001B2F00000000202F001B2F 001B2F001E2F001B2F001C2F001B2F00 1B2F001B2F000000001B2F001C2F0000 00001B2F001B2F00000000202F001B2F 001B2F001E2F001B2F001C2F001B2F00 337800000000000000FF000000000000 00002E7800000000000000FF00000000 00000000337800000000000000000000 31780000000030780000000000000000 00002E7800000000000000FF00000000 000000002C7800000000000000000000 190F0000000000000000000000000000 0000000000000000FF00000000000000 00000000000000000000000000000000 190F0000000000000000000000000000 0000000000000000FF00000000000000 00000000000000000000000000000000 4278003F78003A78003678003378002E 78002A7800277800FF00000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 1E2F001E2F001E2F00000000192F0000 0000192F001E2F000000001F2F000000 001F2F000000001F2F000000001F2F00 1E2F001E2F001E2F00000000192F0000 0000192F001E2F000000001F2F000000 001F2F000000001F2F001A2F001C2F00 36780000000000000037780000000000 00003678000000000000003778000000 00000000367800000000FF0000000000 2A78000000000000002B780000000000 00002C78000000000000002D78000000 000000002E78000000002F7800000000 2E7800000000000000FF000000000000 00002A7800000000000000FF00000000 00000000257800000000000000000000 277800000000000000000000FF000000 00000000002C78000000000000002D78 00000000317800000000FF0000000000 36780000000000000037780000000000 00003678000000000000003778000000 00000000367800000000FF0000000000 2A78000000000000002B780000000000 00003078000000000000002F78000000 000000002E78000000002D7800000000 1E2F00000000192F000000001E2F0000 0000192F001C2F000000001C2F001B2F 00000000192F00000000172F00000000 152F00152F00000000172F00172F0000 0000192F00192F000000001C2F001C2F 000000001B2F00000000192F00000000 36780000000000000038780000000000 0000397800000000000000FF00000000 00000000000000000000000000000000 34780000000000000033780000000000 00003178000000000000002F78000000 00000000317800000000FF0000000000 36780000000000000038780000000000 0000397800000000000000000000FF00 00000000000000000000000000000000 2D78000000000000002F780000000000 00003178000000000000003478000000 00000000317800000000FF0000000000 33780000000000000000000000000000 0000000000000000000000FF00000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 1B2F001B2F000000001B2F001C2F0000 00001B2F001B2F00000000202F001B2F 000000001C2F000000001C2F00000000 1B2F001B2F00000000162F00162F0000 0000172F00172F00000000122F00122F 00000000122F00000000122F00000000 1B2F001B2F000000000000001B2F0000 00000000000000001B2F000000000000 000000001B2F00000000000000000000 122F00122F000000000000000F2F0000 00000000000000001B2F000F2F000000 000000001B2F000F2F00000000000000 1B0FD000000000000000000000000000 0000000000000000120F000000000000 00000000000000000000000000000000 190F0000000000000000000000000000 0000000000000000140F000000000000 00000000000000000000FF0000000000 2A7800000000000000000000FF000000 00000000000000002E78000000000000 00000000FF0000000000000000000000 2C780000000000000000000025780000 00000000000000002778000000000000 00000000FF0000000000000000000000 2E7800000000000000000000FF000000 00000000000000003178000000000000 00000000FF0000000000000000000000 3678000000000000000000003A780000 00000000000000003378000000000000 00000000FF0000000000000000000000 416F00000000356F000000003C6F0000 0000416F00000000000000FF00000000 00000000000000000000000000000000 436F00000000376F00000000446F0000 0000466F00000000000000FF00000000 00000000000000000000000000000000 3D6F00000000316F00000000FF000000 00003D6F00000000316F00000000FF00 00000000000000000000000000000000 436F00000000436F00000000446F0000 0000436F000000003F6F000000000000 00FF0000000000000000000000000000 2918012C1A02291C01291C022C1C0129 1C02291A012C18020000000000002918 012C1A02291C01291A022C1801000000 271801301A02271C01271C02301C0127 1C02271A013018020000000000002718 01301A02271C01271A02301801000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 251801291A02251C01251C02291C0125 1C02291A012518020000000000002518 01291A02251C01251A02291801000000 271801271A022C1C01271C022C1C012C 1C02271A012C18020000000000002718 012C1A02271C01271A022C1801000000 ================================================ FILE: platform/GameShell/home/cpi/games/LowResNX/Star Scroller 1.2.nx ================================================ 'TITLE: STAR SCROLLER 'AUTHORS: TIMO KLOSS, DESBYC DIM GLOBAL STARX(127) FOR I=0 TO 127 STARX(I)=INT(RND*256) NEXT I BG 1 BG FILL 0,0 TO 0,15 CHAR 3 GLOBAL SX,T ON RASTER CALL RASTERFX T$="WELCOME TO LOWRES NX! A VIRTUAL RETRO GAME CONSOLE,WHICH CAN BE PROGRAMMED IN THE CLASSIC BASIC LANGUAGE. HAVE FUN! ... STAR SCROLLER CODE AND GFX BY TIMO, MUSIC BY DESBYC. GREETINGS TO ALL LOWRES NX CODERS! " CELL SIZE 0,1 BG 0 PAL 1 PRIO 1 P=1 L=LEN(T$) MUSIC DO IF SX MOD 16=0 THEN P$=MID$(T$,P,1) IF P$>="A" AND P$<="Z" THEN N=ASC(P$)-ASC("A") C=(N\8)*32+(N MOD 8)*2+64 ELSE IF P$="." THEN C=164 IF P$="," THEN C=166 IF P$="!" THEN C=168 IF P$="?" THEN C=170 IF P$=" " THEN C=174 END IF CELL SX/16+10,0,C P=(P MOD L)+1 END IF SCROLL 0,SX,SIN(T*0.02)*32-56 P2=PEEK($FF5B) FOR I=0 TO 31 X1=SIN(T*0.02+I*PI/16) Y1=COS(T*0.03+I*PI/16) X2=COS(T*0.045+I*0.09) Y2=SIN(T*0.035+I*0.1) X3=SIN(T*0.025+I*0.12) Y3=COS(T*0.015+I*0.13) Z=1+P2/1536 X=(X1*X2*60+X3*32)*Z Y=(Y1*Y2*60+Y3*32)*Z SPRITE I PAL 2+(I/4) MOD 2 SPRITE I,X+76,Y+60,2 NEXT I T=T+1 SX=SX+1 WAIT VBL LOOP SUB RASTERFX F=0.5 D=((RASTER MOD 4)+1) SCROLL 1,TIMER*F/D+STARX(RASTER),0 END SUB #1:MAIN PALETTES 001B070205383420002F0A05003B2211 00000000000000000000000000000000 #2:MAIN CHARACTERS 00000000000000000000000000000000 00000000000000000000000000000000 2402B1310183463C3C7ECFCFFFFF7E3C 80008080800080800080808000808080 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 0F3F7046C9909090000F3F3970606060 00C0E020309090900000C0C0E0606060 FFFF809F90909F9F007F7F606060607F 00C0E020B09020400000C0C06060C080 0F3F7046C9909090000F3F3970606060 00C0E02030F000000000C0C0E0000000 FFFF809E91909090007F7F6160606060 00C0E020309090900000C0C0E0606060 FFFF809F90909F9F007F7F606060607E F0F010F00000000000E0E00000000000 FFFF809F90909F9F007F7F606060607E F0F010F00000000000E0E00000000000 0F3F7046C9909093000F3F3970606060 00C0E02030F000F00000C0C0E0000000 F0F0909090909F9F006060606060607F F0F090909090909000606060606060E0 9F9F809F909090F0607F7F6060606000 90901090909090F060E0E06060606000 9F9090909F9F80FF60606060607F7F00 20909090A020C000C0606060C0C00000 909090994F46300F60606070393F0F00 0000F0F02020C000000000E0C0C00000 909090919F9E80FF60606060617F7F00 909090902020C000606060E0C0C00000 819F90909F9F80FF7E606060607F7F00 00000000F0F010F00000000000E0E000 819F9090909090F07E60606060606000 00000000000000000000000000000000 939390994F46300F61606070393F0F00 F09090902020C000E06060E0C0C00000 809F9090909090F07F60606060606000 10909090909090F0E060606060606000 3F3F203909090909001F1F0606060606 C0C040C0000000000080800000000000 FFFF80FF00000000007F7F0000000000 F0F010909090909000E0E06060606060 F0F0909193969C99006060606163677E F0F0909020408000006060E0C0800000 F0F09090909090900060606060606060 00000000000000000000000000000000 E0F0998F86909996006070797F6F6660 70F09010109090900060E0E0E0606060 F0F88C86939994920070787C6E676361 F0F0909090909010006060606060E0E0 0F3F7046C9909090000F3F3970606060 00C0E020309090900000C0C0E0606060 FFFF809F90909F9F007F7F606060607F 00C0E020B090A0200000C0C06060C0C0 090909093939203F06060606061F1F00 00000000C0C040C00000000000808000 00000001FFFE80FF00000000017F7F00 909090902020C000606060E0C0C00000 81999492919090F07E67636160606000 0080C060309090F0000080C0E0606000 909090909F9F80FF60606060607F7F00 00000000F0F010F00000000000E0E000 90909090909090F06060606060606000 90909090909090F06060606060606000 91909090909090F06060606060606000 10909090909090F0E060606060606000 909090994F46300F60606070393F0F00 909090902020C000606060E0C0C00000 809F9090909090F07F60606060606000 C0000000000000000000000000000000 0F3F7046C9909090000F3F3970606060 00C0E020309090900000C0C0E0606060 FFFF809F90909F9F007F7F606060607F 00C0E020B090A0200000C0C06060C0C0 0F3F7047C8884F67000F3F387070381F 00C0E030F00000C00000C0E000000000 FFFF80F909090909007F7F0606060606 F0F010F00000000000E0E00000000000 F0F09090909090900060606060606060 F0F09090909090900060606060606060 F0F09090909090900060606060606060 F0F09090909090900060606060606060 F0F09090909090900060606060606060 F0F09090909090900060606060606060 F0F059492F26101000603030191F0F0F F0F0A020404080800060C0C080800000 909090984F47300F60606070383F0F00 90909090A03010F060606060C0E0E000 80989492919090F07F67636160606000 4080C060309090F0800080C0E0606000 1E01F1F15F4E300F01000060313F0F00 E02030102020C000C0C0E0E0C0C00000 090909090909090F0606060606060600 00000000000000000000000000000000 909090994F46300F60606070393F0F00 909090902020C000606060E0C0C00000 905949292F16100F60303010190F0F00 90A020404080800060C0C08080000000 969F9990868990E060666F7F79706000 9090909010109070606060E0E0E06000 101030266949D0F00F0F1F1930306000 8080C0406020B0F000008080C0C06000 F0F059492F26101000603030191F0F0F F0F0A020404080800060C0C080800000 FFFF80FF0103060C007F7F0000010307 F0F010909020408000E0E060E0C08000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 F8F88888888888880070707070707070 00000000000000000000000000000000 0F3F76C9F0010306000F397000000103 00C0E020B09020400000C0C060E0C080 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 090909090909090F0606060606060600 00000000000000000000000000000000 193264C89F9F80FF0E1C3870607F7F00 00000000F0F010F00000000000E0E000 00000070F88888700000000070707000 00000000000000000000000000000000 000000787850D0E00000000030206000 00000000000000000000000000000000 50700070F88888702000000070707000 00000000000000000000000000000000 060D0F00060F09060306000000060600 80000000000000000000000000000000 #15:MAIN SOUND 6801F03200017000680200F508026000 2800303019FE00007801F04F0AFF0000 7801F00F02FF00000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 00404007000103400002030700040340 80040306000403060004030600010306 00020306000440400000030300000305 00000305000403060000030300814007 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 170F44190F00160F44190F00190F0027 0F00190F44250F00170F001A0F001D0F 44190F00190F00250F00190F00190F00 170F001E0F44160F00190F00190F001B 0F00190F00190F00170F001A0F00250F 47190F00190F00190F00190F00190F00 311F0000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 3D1F0000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 192F00000000000000000000192F0000 0000000000000000192F00192F000000 00000000192F00000000000000000000 192F00000000000000000000192F0000 0000000000000000192F000000000000 00000000192F00192F00192F00192F00 461F00471F003B1F003A1F0000000000 0000000000000000441F00451F00391F 00381F00000000000000000000000000 3F1F00401F00341F00331F0000000000 00000000000000003D1F003E1F00321F 00000000411F00000000000000000000 5F4F005F4F005F4F005F4F005F4F005F 4F005F4F005F4F005F4F005F4F005F4F 005F4F005F4F005F4F005F4F005F4F00 5F4F005F4F005F4F005F4F005F4F005F 4F005F4F005F4F005F4F005F4F005F4F 005F4F005F4F005F4F005F4F005F4F00 5F4F005F4F005F4F005F4F003D3F005F 4F005F4F005F4F005F4F005F4F005F4F 005F4F003D3F005F4F005F4F005F4F00 5F4F005F4F005F4F005F4F003D3F005F 4F005F4F005F4F005F4F005F4F005F4F 005F4F003D3F005F4F003D3F003D3F00 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000323F00000000323F00323F00 ================================================ FILE: platform/LibRetro/Makefile ================================================ DEBUG=0 PSS_STYLE=1 EXTERNAL_ZLIB=0 HAVE_GRIFFIN=1 STATIC_LINKING=0 ENDIANNESS_DEFINES= SPACE := SPACE := $(SPACE) $(SPACE) BACKSLASH := BACKSLASH := \$(BACKSLASH) filter_out1 = $(filter-out $(firstword $1),$1) filter_out2 = $(call filter_out1,$(call filter_out1,$1)) unixpath = $(subst \,/,$1) unixcygpath = /$(subst :,,$(call unixpath,$1)) ifeq ($(platform),) platform = unix ifeq ($(shell uname -a),) platform = win else ifneq ($(findstring MINGW,$(shell uname -a)),) platform = win else ifneq ($(findstring Darwin,$(shell uname -a)),) platform = osx arch = intel ifeq ($(shell uname -p),powerpc) arch = ppc endif else ifneq ($(findstring win,$(shell uname -a)),) platform = win endif endif # system platform system_platform = unix ifeq ($(shell uname -a),) EXE_EXT = .exe system_platform = win else ifneq ($(findstring Darwin,$(shell uname -a)),) system_platform = osx arch = intel ifeq ($(shell uname -p),powerpc) arch = ppc endif else ifneq ($(findstring MINGW,$(shell uname -a)),) system_platform = win endif # Replace 'sample' with the name of the core TARGET_NAME := lowresnx GIT_VERSION := " $(shell git rev-parse --short HEAD || echo unknown)" ifneq ($(GIT_VERSION)," unknown") CFLAGS += -DGIT_VERSION=\"$(GIT_VERSION)\" CXXFLAGS += -DGIT_VERSION=\"$(GIT_VERSION)\" endif ifneq (,$(findstring msvc,$(platform))) LIBM := else LIBM := -lm endif LIBS := CORE_DIR := . # Unix ifeq ($(platform), unix) TARGET := $(TARGET_NAME)_libretro.so fpic := -fPIC ifneq ($(findstring SunOS,$(shell uname -a)),) CC = gcc SHARED := -shared -z defs else SHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined endif else ifeq ($(platform), linux-portable) TARGET := $(TARGET_NAME)_libretro.so fpic := -fPIC -nostdlib SHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T LIBM := # Classic Platforms #################### # Platform affix = classic__<µARCH> # Help at https://modmyclassic.com/comp # (armv7 a7, hard point, neon based) ### # NESC, SNESC, C64 mini else ifeq ($(platform), classic_armv7_a7) CC = gcc TARGET := $(TARGET_NAME)_libretro.so fpic := -fPIC LDFLAGS += $(fpic) -shared -Wl,--version-script=link.T CFLAGS += -Ofast \ -fwhole-program -fuse-linker-plugin \ -fdata-sections -ffunction-sections -Wl,--gc-sections \ -fno-stack-protector -fno-ident -fomit-frame-pointer \ -falign-functions=1 -falign-jumps=1 -falign-loops=1 \ -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-unroll-loops \ -fmerge-all-constants -fno-math-errno \ -marm -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard CXXFLAGS += $(CFLAGS) CPPFLAGS += $(CFLAGS) ASFLAGS += $(CFLAGS) HAVE_NEON = 1 ARCH = arm BUILTIN_GPU = neon USE_DYNAREC = 1 CPU_ARCH := arm ARM = 1 ifeq ($(shell echo `$(CC) -dumpversion` "< 4.9" | bc -l), 1) CFLAGS += -march=armv7-a else CFLAGS += -march=armv7ve # If gcc is 5.0 or later ifeq ($(shell echo `$(CC) -dumpversion` ">= 5" | bc -l), 1) LDFLAGS += -static-libgcc -static-libstdc++ endif endif ####################################### # OS X else ifeq ($(platform), osx) TARGET := $(TARGET_NAME)_libretro.dylib fpic := -fPIC SHARED := -dynamiclib ifeq ($(arch),ppc) ENDIANNESS_DEFINES += -DMSB_FIRST CFLAGS += -std=gnu99 endif OSXVER = `sw_vers -productVersion | cut -d. -f 2` OSX_LT_MAVERICKS = `(( $(OSXVER) <= 9)) && echo "YES"` ifeq ($(OSX_LT_MAVERICKS),YES) fpic += -mmacosx-version-min=10.1 endif ifeq ($(CROSS_COMPILE),1) TARGET_RULE = -target $(LIBRETRO_APPLE_PLATFORM) -isysroot $(LIBRETRO_APPLE_ISYSROOT) CFLAGS += $(TARGET_RULE) CPPFLAGS += $(TARGET_RULE) CXXFLAGS += $(TARGET_RULE) LDFLAGS += $(TARGET_RULE) endif CFLAGS += $(ARCHFLAGS) CXXFLAGS += $(ARCHFLAGS) LDFLAGS += $(ARCHFLAGS) # iOS else ifneq (,$(findstring ios,$(platform))) TARGET := $(TARGET_NAME)_libretro_ios.dylib fpic := -fPIC SHARED := -dynamiclib MINVERSION := ifeq ($(IOSSDK),) IOSSDK := $(shell xcodebuild -version -sdk iphoneos Path) endif ifeq ($(platform),ios-arm64) CC = cc -arch arm64 -isysroot $(IOSSDK) else CC = cc -arch armv7 -isysroot $(IOSSDK) endif CFLAGS += -DIOS CXXFLAGS += -DIOS ifeq ($(platform),$(filter $(platform),ios9 ios-arm64)) MINVERSION = -miphoneos-version-min=8.0 else MINVERSION = -miphoneos-version-min=5.0 endif CFLAGS += $(MINVERSION) CXXFLAGS += $(MINVERSION) LDFLAGS += $(MINVERSION) else ifeq ($(platform), tvos-arm64) TARGET := $(TARGET_NAME)_libretro_tvos.dylib fpic := -fPIC SHARED := -dynamiclib CFLAGS += -DIOS CXXFLAGS += -DIOS ifeq ($(IOSSDK),) IOSSDK := $(shell xcodebuild -version -sdk appletvos Path) endif CC = cc -arch arm64 -isysroot $(IOSSDK) CXX = c++ -arch arm64 -isysroot $(IOSSDK) # Theos iOS else ifeq ($(platform), theos_ios) DEPLOYMENT_IOSVERSION = 5.0 TARGET = iphone:latest:$(DEPLOYMENT_IOSVERSION) ARCHS = armv7 armv7s TARGET_IPHONEOS_DEPLOYMENT_VERSION=$(DEPLOYMENT_IOSVERSION) THEOS_BUILD_DIR := objs include $(THEOS)/makefiles/common.mk LIBRARY_NAME = $(TARGET_NAME)_libretro_ios # QNX else ifeq ($(platform), qnx) TARGET := $(TARGET_NAME)_libretro_qnx.so fpic := -fPIC SHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined CC = qcc -Vgcc_ntoarmv7le AR = qcc -Vgcc_ntoarmv7le PLATFORM_DEFINES := -D__BLACKBERRY_QNX__ -marm -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=softfp # PS3 else ifeq ($(platform), ps3) TARGET := $(TARGET_NAME)_libretro_$(platform).a CC = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-gcc.exe AR = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-ar.exe PLATFORM_DEFINES := -D__CELLOS_LV2 ENDIANNESS_DEFINES += -DMSB_FIRST STATIC_LINKING=1 EXTERNAL_ZLIB=1 # sncps3 else ifeq ($(platform), sncps3) TARGET := $(TARGET_NAME)_libretro_ps3.a CC = $(CELL_SDK)/host-win32/sn/bin/ps3ppusnc.exe AR = $(CELL_SDK)/host-win32/sn/bin/ps3snarl.exe PLATFORM_DEFINES := -D__CELLOS_LV2 ENDIANNESS_DEFINES += -DMSB_FIRST STATIC_LINKING=1 EXTERNAL_ZLIB=1 else ifeq ($(platform), ps2) TARGET := $(TARGET_NAME)_libretro_$(platform).a CC = mips64r5900el-ps2-elf-gcc$(EXE_EXT) CXX = mips64r5900el-ps2-elf-g++$(EXE_EXT) AR = mips64r5900el-ps2-elf-ar$(EXE_EXT) PLATFORM_DEFINES := -DPS2 -G0 -fomit-frame-pointer -ffast-math STATIC_LINKING=1 EXTERNAL_ZLIB=1 # PSP else ifeq ($(platform), psp1) TARGET := $(TARGET_NAME)_libretro_$(platform).a CC = psp-gcc$(EXE_EXT) AR = psp-ar$(EXE_EXT) PLATFORM_DEFINES := -DPSP -G0 PLATFORM_DEFINES += -march=allegrex -mfp32 -mgp32 -mlong32 -mabi=eabi PLATFORM_DEFINES += -fomit-frame-pointer -fstrict-aliasing PLATFORM_DEFINES += -falign-functions=32 -falign-loops -falign-labels -falign-jumps STATIC_LINKING=1 EXTERNAL_ZLIB=1 # Vita else ifeq ($(platform), vita) TARGET := $(TARGET_NAME)_libretro_$(platform).a CC = arm-vita-eabi-gcc$(EXE_EXT) AR = arm-vita-eabi-ar$(EXE_EXT) PLATFORM_DEFINES := -DVITA STATIC_LINKING=1 EXTERNAL_ZLIB=1 # DOS else ifeq ($(platform), dos) TARGET := $(TARGET_NAME)_libretro_$(platform).a CC = i586-pc-msdosdjgpp-gcc CXX = i586-pc-msdosdjgpp-g++ AR = i586-pc-msdosdjgpp-ar CFLAGS += -march=i386 CXXFLAGS += -march=i386 STATIC_LINKING=1 EXTERNAL_ZLIB=1 # CTR(3DS) else ifeq ($(platform), ctr) TARGET := $(TARGET_NAME)_libretro_$(platform).a CC = $(DEVKITARM)/bin/arm-none-eabi-gcc$(EXE_EXT) AR = $(DEVKITARM)/bin/arm-none-eabi-ar$(EXE_EXT) PLATFORM_DEFINES := -DARM11 -D_3DS CFLAGS += -march=armv6k -mtune=mpcore -mfloat-abi=hard CFLAGS += -Wall -mword-relocations CFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math CXXFLAGS += -march=armv6k -mtune=mpcore -mfloat-abi=hard CXXFLAGS += -Wall -mword-relocations CXXFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math STATIC_LINKING=1 EXTERNAL_ZLIB=1 # Nintendo Switch (libnx) else ifeq ($(platform), libnx) include $(DEVKITPRO)/libnx/switch_rules PORTLIBS := $(PORTLIBS_PATH)/switch TARGET := $(TARGET_NAME)_libretro_$(platform).a DEFINES := -DSWITCH=1 -D__SWITCH__ -DARM CFLAGS += $(DEFINES) -fPIE -I$(PORTLIBS)/include/ -I$(LIBNX)/include/ -ffunction-sections -fdata-sections -ftls-model=local-exec -specs=$(LIBNX)/switch.specs CFLAGS += -march=armv8-a -mtune=cortex-a57 -mtp=soft -mcpu=cortex-a57+crc+fp+simd -ffast-math CXXFLAGS += $(CFLAGS) STATIC_LINKING=1 EXTERNAL_ZLIB=1 # Raspberry Pi 1 else ifeq ($(platform), rpi1) TARGET := $(TARGET_NAME)_libretro.so fpic := -fPIC SHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined CFLAGS += -DARM11 CFLAGS += -marm -march=armv6j -mfpu=vfp -mfloat-abi=hard -funsafe-math-optimizations CFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math CXXFLAGS += -DARM11 CXXFLAGS += -marm -march=armv6j -mfpu=vfp -mfloat-abi=hard -funsafe-math-optimizations CXXFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math # Raspberry Pi 2 else ifeq ($(platform), rpi2) TARGET := $(TARGET_NAME)_libretro.so fpic := -fPIC SHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined CFLAGS += -DARM CFLAGS += -marm -mcpu=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard -funsafe-math-optimizations CFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math CXXFLAGS += -DARM CXXFLAGS += -marm -mcpu=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard -funsafe-math-optimizations CXXFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math # Lightweight PS3 Homebrew SDK else ifeq ($(platform), psl1ght) TARGET := $(TARGET_NAME)_libretro_$(platform).a CC = $(PS3DEV)/ppu/bin/ppu-gcc$(EXE_EXT) AR = $(PS3DEV)/ppu/bin/ppu-ar$(EXE_EXT) PLATFORM_DEFINES := -D__CELLOS_LV2 ENDIANNESS_DEFINES += -DMSB_FIRST STATIC_LINKING=1 EXTERNAL_ZLIB=1 # Xbox 360 else ifeq ($(platform), xenon) TARGET := $(TARGET_NAME)_libretro_xenon360.a CC = xenon-gcc$(EXE_EXT) AR = xenon-ar$(EXE_EXT) PLATFORM_DEFINES := -D__LIBXENON__ ENDIANNESS_DEFINES += -DMSB_FIRST STATIC_LINKING=1 EXTERNAL_ZLIB=1 # Nintendo Game Cube else ifeq ($(platform), ngc) TARGET := $(TARGET_NAME)_libretro_$(platform).a CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT) AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT) PLATFORM_DEFINES := -DGEKKO -DHW_DOL -mrvl -mcpu=750 -meabi -mhard-float PLATFORM_DEFINES += -U__INT32_TYPE__ -U __UINT32_TYPE__ -D__INT32_TYPE__=int ENDIANNESS_DEFINES += -DMSB_FIRST STATIC_LINKING=1 EXTERNAL_ZLIB=1 # Nintendo Wii else ifeq ($(platform), wii) TARGET := $(TARGET_NAME)_libretro_$(platform).a CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT) AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT) PLATFORM_DEFINES := -DGEKKO -DHW_RVL -mrvl -mcpu=750 -meabi -mhard-float PLATFORM_DEFINES += -U__INT32_TYPE__ -U __UINT32_TYPE__ -D__INT32_TYPE__=int ENDIANNESS_DEFINES += -DMSB_FIRST STATIC_LINKING=1 EXTERNAL_ZLIB=1 # Nintendo WiiU else ifeq ($(platform), wiiu) TARGET := $(TARGET_NAME)_libretro_$(platform).a CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT) AR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT) PLATFORM_DEFINES := -DGEKKO -DHW_RVL -mcpu=750 -meabi -mhard-float PLATFORM_DEFINES += -ffunction-sections -fdata-sections -D__wiiu__ -D__wut__ ENDIANNESS_DEFINES += -DMSB_FIRST STATIC_LINKING=1 EXTERNAL_ZLIB=1 # Nintendo Switch (libtransistor) else ifeq ($(platform), switch) EXT=a TARGET := $(TARGET_NAME)_libretro_$(platform).$(EXT) include $(LIBTRANSISTOR_HOME)/libtransistor.mk STATIC_LINKING=1 EXTERNAL_ZLIB=1 # ARM else ifneq (,$(findstring armv,$(platform))) TARGET := $(TARGET_NAME)_libretro.so SHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined fpic := -fPIC ifneq (,$(findstring cortexa5,$(platform))) PLATFORM_DEFINES += -marm -mcpu=cortex-a5 else ifneq (,$(findstring cortexa8,$(platform))) PLATFORM_DEFINES += -marm -mcpu=cortex-a8 else ifneq (,$(findstring cortexa9,$(platform))) PLATFORM_DEFINES += -marm -mcpu=cortex-a9 else ifneq (,$(findstring cortexa15a7,$(platform))) PLATFORM_DEFINES += -marm -mcpu=cortex-a15.cortex-a7 else PLATFORM_DEFINES += -marm endif ifneq (,$(findstring softfloat,$(platform))) PLATFORM_DEFINES += -mfloat-abi=softfp else ifneq (,$(findstring hardfloat,$(platform))) PLATFORM_DEFINES += -mfloat-abi=hard endif PLATFORM_DEFINES += -DARM # emscripten else ifeq ($(platform), emscripten) TARGET := $(TARGET_NAME)_libretro_$(platform).bc STATIC_LINKING=1 EXTERNAL_ZLIB=1 # GCW0 else ifeq ($(platform), gcw0) TARGET := $(TARGET_NAME)_libretro.so CC = /opt/gcw0-toolchain/usr/bin/mipsel-linux-gcc CXX = /opt/gcw0-toolchain/usr/bin/mipsel-linux-g++ AR = /opt/gcw0-toolchain/usr/bin/mipsel-linux-ar fpic := -fPIC SHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined PLATFORM_DEFINES += -ffast-math -march=mips32 -mtune=mips32r2 -mhard-float CFLAGS += -std=gnu11 EXTERNAL_ZLIB = 1 # RS90 else ifeq ($(platform), rs90) TARGET := $(TARGET_NAME)_libretro.so CC = /opt/rs90-toolchain/usr/bin/mipsel-linux-gcc CXX = /opt/rs90-toolchain/usr/bin/mipsel-linux-g++ AR = /opt/rs90-toolchain/usr/bin/mipsel-linux-ar fpic := -fPIC SHARED := -shared -Wl,-version-script=$(CORE_DIR)/link.T PLATFORM_DEFINES := -DCC_RESAMPLER -DCC_RESAMPLER_NO_HIGHPASS CFLAGS += -fomit-frame-pointer -ffast-math -march=mips32 -mtune=mips32 CXXFLAGS += -fomit-frame-pointer -ffast-math -march=mips32 -mtune=mips32 # RETROFW else ifeq ($(platform), retrofw) EXT ?= so TARGET := $(TARGET_NAME)_libretro.$(EXT) CC = /opt/retrofw-toolchain/usr/bin/mipsel-linux-gcc AR = /opt/retrofw-toolchain/usr/bin/mipsel-linux-ar fpic := -fPIC SHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,--no-undefined CFLAGS += -ffast-math -march=mips32 -mtune=mips32 -mhard-float CXXFLAGS += -ffast-math -march=mips32 -mtune=mips32 -mhard-float LIBS = -lm # Miyoo else ifeq ($(platform), miyoo) TARGET := $(TARGET_NAME)_libretro.so CC = /opt/miyoo/usr/bin/arm-linux-gcc CXX = /opt/miyoo/usr/bin/arm-linux-g++ AR = /opt/miyoo/usr/bin/arm-linux-ar fpic := -fPIC SHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined PLATFORM_DEFINES += -ffast-math -mcpu=arm926ej-s EXTERNAL_ZLIB = 1 # Windows MSVC 2017 all architectures else ifneq (,$(findstring windows_msvc2017,$(platform))) PlatformSuffix = $(subst windows_msvc2017_,,$(platform)) ifneq (,$(findstring desktop,$(PlatformSuffix))) WinPartition = desktop MSVC2017CompileFlags = -DWINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP -FS LDFLAGS += -MANIFEST -LTCG:incremental -NXCOMPAT -DYNAMICBASE -DEBUG -OPT:REF -INCREMENTAL:NO -SUBSYSTEM:WINDOWS -MANIFESTUAC:"level='asInvoker' uiAccess='false'" -OPT:ICF -ERRORREPORT:PROMPT -NOLOGO -TLBID:1 LIBS += kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib else ifneq (,$(findstring uwp,$(PlatformSuffix))) WinPartition = uwp MSVC2017CompileFlags = -DWINAPI_FAMILY=WINAPI_FAMILY_APP -DWINDLL -D_UNICODE -DUNICODE -DWRL_NO_DEFAULT_LIB -FS LDFLAGS += -APPCONTAINER -NXCOMPAT -DYNAMICBASE -MANIFEST:NO -LTCG -OPT:REF -SUBSYSTEM:CONSOLE -MANIFESTUAC:NO -OPT:ICF -ERRORREPORT:PROMPT -NOLOGO -TLBID:1 -DEBUG:FULL -WINMD:NO LIBS += WindowsApp.lib endif CFLAGS += $(MSVC2017CompileFlags) CXXFLAGS += $(MSVC2017CompileFlags) TargetArchMoniker = $(subst $(WinPartition)_,,$(PlatformSuffix)) CC = cl.exe CXX = cl.exe LD = link.exe reg_query = $(call filter_out2,$(subst $2,,$(shell reg query "$2" -v "$1" 2>nul))) fix_path = $(subst $(SPACE),\ ,$(subst \,/,$1)) ProgramFiles86w := $(shell cmd /c "echo %PROGRAMFILES(x86)%") ProgramFiles86 := $(shell cygpath "$(ProgramFiles86w)") WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0) WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_CURRENT_USER\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0) WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0) WindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_CURRENT_USER\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v10.0) WindowsSdkDir := $(WindowsSdkDir) WindowsSDKVersion ?= $(firstword $(foreach folder,$(subst $(subst \,/,$(WindowsSdkDir)Include/),,$(wildcard $(call fix_path,$(WindowsSdkDir)Include\*))),$(if $(wildcard $(call fix_path,$(WindowsSdkDir)Include/$(folder)/um/Windows.h)),$(folder),)))$(BACKSLASH) WindowsSDKVersion := $(WindowsSDKVersion) VsInstallBuildTools = $(ProgramFiles86)/Microsoft Visual Studio/2017/BuildTools VsInstallEnterprise = $(ProgramFiles86)/Microsoft Visual Studio/2017/Enterprise VsInstallProfessional = $(ProgramFiles86)/Microsoft Visual Studio/2017/Professional VsInstallCommunity = $(ProgramFiles86)/Microsoft Visual Studio/2017/Community VsInstallRoot ?= $(shell if [ -d "$(VsInstallBuildTools)" ]; then echo "$(VsInstallBuildTools)"; fi) ifeq ($(VsInstallRoot), ) VsInstallRoot = $(shell if [ -d "$(VsInstallEnterprise)" ]; then echo "$(VsInstallEnterprise)"; fi) endif ifeq ($(VsInstallRoot), ) VsInstallRoot = $(shell if [ -d "$(VsInstallProfessional)" ]; then echo "$(VsInstallProfessional)"; fi) endif ifeq ($(VsInstallRoot), ) VsInstallRoot = $(shell if [ -d "$(VsInstallCommunity)" ]; then echo "$(VsInstallCommunity)"; fi) endif VsInstallRoot := $(VsInstallRoot) VcCompilerToolsVer := $(shell cat "$(VsInstallRoot)/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt" | grep -o '[0-9\.]*') VcCompilerToolsDir := $(VsInstallRoot)/VC/Tools/MSVC/$(VcCompilerToolsVer) WindowsSDKSharedIncludeDir := $(shell cygpath -w "$(WindowsSdkDir)\Include\$(WindowsSDKVersion)\shared") WindowsSDKUCRTIncludeDir := $(shell cygpath -w "$(WindowsSdkDir)\Include\$(WindowsSDKVersion)\ucrt") WindowsSDKUMIncludeDir := $(shell cygpath -w "$(WindowsSdkDir)\Include\$(WindowsSDKVersion)\um") WindowsSDKUCRTLibDir := $(shell cygpath -w "$(WindowsSdkDir)\Lib\$(WindowsSDKVersion)\ucrt\$(TargetArchMoniker)") WindowsSDKUMLibDir := $(shell cygpath -w "$(WindowsSdkDir)\Lib\$(WindowsSDKVersion)\um\$(TargetArchMoniker)") # For some reason the HostX86 compiler doesn't like compiling for x64 # ("no such file" opening a shared library), and vice-versa. # Work around it for now by using the strictly x86 compiler for x86, and x64 for x64. # NOTE: What about ARM? ifneq (,$(findstring x64,$(TargetArchMoniker))) VCCompilerToolsBinDir := $(VcCompilerToolsDir)\bin\HostX64 else VCCompilerToolsBinDir := $(VcCompilerToolsDir)\bin\HostX86 endif PATH := $(shell IFS=$$'\n'; cygpath "$(VCCompilerToolsBinDir)/$(TargetArchMoniker)"):$(PATH) PATH := $(PATH):$(shell IFS=$$'\n'; cygpath "$(VsInstallRoot)/Common7/IDE") INCLUDE := $(shell IFS=$$'\n'; cygpath -w "$(VcCompilerToolsDir)/include") LIB := $(shell IFS=$$'\n'; cygpath -w "$(VcCompilerToolsDir)/lib/$(TargetArchMoniker)") export INCLUDE := $(INCLUDE);$(WindowsSDKSharedIncludeDir);$(WindowsSDKUCRTIncludeDir);$(WindowsSDKUMIncludeDir) export LIB := $(LIB);$(WindowsSDKUCRTLibDir);$(WindowsSDKUMLibDir) TARGET := $(TARGET_NAME)_libretro.dll PSS_STYLE :=2 LDFLAGS += -DLL # Windows MSVC 2010 x64 else ifeq ($(platform), windows_msvc2010_x64) CC = cl.exe CXX = cl.exe PATH := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/bin/amd64"):$(PATH) PATH := $(PATH):$(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../IDE") INCLUDE := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/include") LIB := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/lib/amd64") BIN := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/bin") WindowsSdkDir := $(shell reg query "HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0A" -v "InstallationFolder" | grep -o '[A-Z]:\\.*')lib/x64 WindowsSdkDir ?= $(shell reg query "HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A" -v "InstallationFolder" | grep -o '[A-Z]:\\.*')lib/x64 export INCLUDE := $(INCLUDE) export LIB := $(LIB);$(WindowsSdkDir) TARGET := $(TARGET_NAME)_libretro.dll PSS_STYLE :=2 LDFLAGS += -DLL # Windows MSVC 2010 x86 else ifeq ($(platform), windows_msvc2010_x86) CC = cl.exe CXX = cl.exe PATH := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/bin"):$(PATH) PATH := $(PATH):$(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../IDE") INCLUDE := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/include") LIB := $(shell IFS=$$'\n'; cygpath -w "$(VS100COMNTOOLS)../../VC/lib") BIN := $(shell IFS=$$'\n'; cygpath "$(VS100COMNTOOLS)../../VC/bin") WindowsSdkDir := $(shell reg query "HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0A" -v "InstallationFolder" | grep -o '[A-Z]:\\.*')lib WindowsSdkDir ?= $(shell reg query "HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.1A" -v "InstallationFolder" | grep -o '[A-Z]:\\.*')lib export INCLUDE := $(INCLUDE) export LIB := $(LIB);$(WindowsSdkDir) TARGET := $(TARGET_NAME)_libretro.dll PSS_STYLE :=2 LDFLAGS += -DLL # Windows MSVC 2003 Xbox 1 else ifeq ($(platform), xbox1_msvc2003) TARGET := $(TARGET_NAME)_libretro_xdk1.lib CC = CL.exe CXX = CL.exe LD = lib.exe export INCLUDE := $(XDK)\xbox\include export LIB := $(XDK)\xbox\lib PATH := $(call unixcygpath,$(XDK)/xbox/bin/vc71):$(PATH) PSS_STYLE :=2 CFLAGS += -D_XBOX -D_XBOX1 CXXFLAGS += -D_XBOX -D_XBOX1 STATIC_LINKING=1 # Windows MSVC 2010 Xbox 360 else ifeq ($(platform), xbox360_msvc2010) TARGET := $(TARGET_NAME)_libretro_xdk360.lib CC = cl.exe CXX = cl.exe LD = lib.exe export INCLUDE := $(XEDK)/include/xbox export LIB := $(XEDK)/lib/xbox PATH := $(call unixcygpath,$(XEDK)/bin/win32):$(PATH) PSS_STYLE :=2 CFLAGS += -D_XBOX -D_XBOX360 CXXFLAGS += -D_XBOX -D_XBOX360 ENDIANNESS_DEFINES += -DMSB_FIRST STATIC_LINKING=1 # Windows MSVC 2005 x86 else ifeq ($(platform), windows_msvc2005_x86) CC = cl.exe CXX = cl.exe PATH := $(shell IFS=$$'\n'; cygpath "$(VS80COMNTOOLS)../../VC/bin"):$(PATH) PATH := $(PATH):$(shell IFS=$$'\n'; cygpath "$(VS80COMNTOOLS)../IDE") INCLUDE := $(shell IFS=$$'\n'; cygpath "$(VS80COMNTOOLS)../../VC/include") LIB := $(shell IFS=$$'\n'; cygpath -w "$(VS80COMNTOOLS)../../VC/lib") BIN := $(shell IFS=$$'\n'; cygpath "$(VS80COMNTOOLS)../../VC/bin") WindowsSdkDir := $(INETSDK) export INCLUDE := $(INCLUDE);$(INETSDK)/Include;src/drivers/libretro/msvc/msvc-2005 export LIB := $(LIB);$(WindowsSdkDir);$(INETSDK)/Lib TARGET := $(TARGET_NAME)_libretro.dll PSS_STYLE :=2 LDFLAGS += -DLL CFLAGS += -D_CRT_SECURE_NO_DEPRECATE # Windows MSVC 2003 x86 else ifeq ($(platform), windows_msvc2003_x86) CC = cl.exe CXX = cl.exe PATH := $(shell IFS=$$'\n'; cygpath "$(VS71COMNTOOLS)../../Vc7/bin"):$(PATH) PATH := $(PATH):$(shell IFS=$$'\n'; cygpath "$(VS71COMNTOOLS)../IDE") INCLUDE := $(shell IFS=$$'\n'; cygpath "$(VS71COMNTOOLS)../../Vc7/include") LIB := $(shell IFS=$$'\n'; cygpath -w "$(VS71COMNTOOLS)../../Vc7/lib") BIN := $(shell IFS=$$'\n'; cygpath "$(VS71COMNTOOLS)../../Vc7/bin") WindowsSdkDir := $(INETSDK) export INCLUDE := $(INCLUDE);$(INETSDK)/Include;src/drivers/libretro/msvc/msvc-2005 export LIB := $(LIB);$(WindowsSdkDir);$(INETSDK)/Lib TARGET := $(TARGET_NAME)_libretro.dll PSS_STYLE :=2 LDFLAGS += -DLL CFLAGS += -D_CRT_SECURE_NO_DEPRECATE # Windows else TARGET := $(TARGET_NAME)_libretro.dll CC ?= gcc CXX ?= g++ SHARED := -shared -static-libgcc -static-libstdc++ -s -Wl,--version-script=$(CORE_DIR)/link.T PSS_STYLE :=2 endif ifeq ($(DEBUG), 1) ifneq (,$(findstring msvc,$(platform))) ifeq ($(STATIC_LINKING),1) CFLAGS += -MTd CXXFLAGS += -MTd else CFLAGS += -MDd CXXFLAGS += -MDd endif CFLAGS += -Od -Zi -DDEBUG -D_DEBUG CXXFLAGS += -Od -Zi -DDEBUG -D_DEBUG else CFLAGS += -O0 -g -DDEBUG CXXFLAGS += -O0 -g -DDEBUG endif else ifneq (,$(findstring msvc,$(platform))) ifeq ($(STATIC_LINKING),1) CFLAGS += -MT CXXFLAGS += -MT else CFLAGS += -MD CXXFLAGS += -MD endif CFLAGS += -O2 -DNDEBUG CXXFLAGS += -O2 -DNDEBUG else CFLAGS += -O2 -DNDEBUG CXXFLAGS += -O2 -DNDEBUG endif endif ifeq ($(EXTERNAL_ZLIB), 1) CFLAGS += -DHAVE_EXTERNAL_ZLIB CXXFLAGS += -DHAVE_EXTERNAL_ZLIB endif include Makefile.common OBJECTS := $(SOURCES_C:.c=.o) $(SOURCES_CXX:.cpp=.o) DEFINES := $(COREDEFINES) $(PLATFORM_DEFINES) ifeq ($(STATIC_LINKING),1) DEFINES += -DSTATIC_LINKING endif ifeq ($(platform), sncps3) WARNING_DEFINES = else ifneq (,$(findstring msvc,$(platform))) WARNING_DEFINES = LIBM := else WARNING_DEFINES = -Wno-write-strings endif CFLAGS += $(COREDEFINES) $(fpic) $(WARNING_DEFINES) $(DEFINES) $(ENDIANNESS_DEFINES) CXXFLAGS += $(COREDEFINES) $(fpic) $(WARNING_DEFINES) $(DEFINES) $(ENDIANNESS_DEFINES) LDFLAGS += $(LIBM) ifeq ($(platform), psp1) INCFLAGS += -I$(shell psp-config --pspsdk-path)/include endif ifneq (,$(findstring msvc,$(platform))) OBJOUT = -Fo LINKOUT = -out: ifeq ($(STATIC_LINKING),1) LD ?= lib.exe STATIC_LINKING=0 else LD = link.exe endif else OBJOUT = -o LINKOUT = -o LD = $(CC) endif ifeq ($(platform), theos_ios) COMMON_FLAGS := -DIOS $(COMMON_DEFINES) $(INCFLAGS) -I$(THEOS_INCLUDE_PATH) -Wno-error $(LIBRARY_NAME)_CFLAGS += $(COMMON_FLAGS) $(CFLAGS) $(LIBRARY_NAME)_CXXFLAGS += $(COMMON_FLAGS) $(CXXFLAGS) ${LIBRARY_NAME}_FILES = $(SOURCES_C) include $(THEOS_MAKE_PATH)/library.mk else all: $(TARGET) $(TARGET): $(OBJECTS) ifeq ($(STATIC_LINKING),1) ifneq (,$(findstring msvc,$(platform))) $(LD) $(LINKOUT)$@ $(OBJECTS) else $(AR) rcs $@ $(OBJECTS) endif else $(LD) $(LINKOUT)$@ $(SHARED) $(OBJECTS) $(LDFLAGS) $(LIBS) endif %.o: %.cpp $(CXX) -c $(OBJOUT)$@ $< $(CXXFLAGS) $(INCFLAGS) %.o: %.c $(CC) -c $(OBJOUT)$@ $< $(CFLAGS) $(INCFLAGS) clean-objs: rm -f $(OBJECTS) clean: rm -f $(OBJECTS) rm -f $(TARGET) .PHONY: clean clean-objs endif print-%: @echo '$*=$($*)' ================================================ FILE: platform/LibRetro/Makefile.common ================================================ INCFLAGS := -I ../../core \ -I ../../core/machine \ -I ../../core/accessories \ -I ../../core/datamanager \ -I ../../core/interpreter \ -I ../../core/libraries \ -I ../../core/overlay COREDEFINES = -D__LIBRETRO__ ifneq (,$(findstring msvc,$(platform))) COREDEFINES += -DINLINE=_inline else COREDEFINES += -DINLINE=inline endif ifeq ($(PSS_STYLE),2) COREDEFINES += -DPSS_STYLE=2 else COREDEFINES += -DPSS_STYLE=1 endif # Add C sourcecode files to this SOURCES_C := $(wildcard ../../core/*.c)\ $(wildcard ../../core/*/*.c)\ $(wildcard ../../libretro/*.c) # Add C++ sourcecode files to this SOURCES_CXX := ================================================ FILE: platform/LibRetro/jni/Android.mk ================================================ LOCAL_PATH := $(call my-dir) SRCDIR := $(LOCAL_PATH)/../../../ INCFLAGS := -I $(SRCDIR)/core \ -I $(SRCDIR)/core/machine \ -I $(SRCDIR)/core/accessories \ -I $(SRCDIR)/core/datamanager \ -I $(SRCDIR)/core/interpreter \ -I $(SRCDIR)/core/libraries \ -I $(SRCDIR)/core/overlay COREFLAGS := -fomit-frame-pointer -ffast-math -D__LIBRETRO__ $(INCFLAGS) COREFLAGS += -DGB_INTERNAL -DDISABLE_DEBUGGER -DPLATFORM_ANDROID GIT_VERSION := " $(shell git rev-parse --short HEAD || echo unknown)" ifneq ($(GIT_VERSION)," unknown") COREFLAGS += -DGIT_VERSION=\"$(GIT_VERSION)\" endif SRCFILES := $(wildcard $(SRCDIR)/core/*.c)\ $(wildcard $(SRCDIR)/core/*/*.c)\ $(wildcard $(SRCDIR)/libretro/*.c) include $(CLEAR_VARS) LOCAL_MODULE := retro LOCAL_SRC_FILES := $(SRCFILES) LOCAL_CPPFLAGS := -std=c++17 $(COREFLAGS) LOCAL_CFLAGS := $(COREFLAGS) LOCAL_LDFLAGS := -Wl,-version-script=../link.T LOCAL_CPP_FEATURES := exceptions rtti include $(BUILD_SHARED_LIBRARY) ================================================ FILE: platform/LibRetro/jni/Application.mk ================================================ APP_ABI := all APP_STL := c++_static ================================================ FILE: platform/LibRetro/link.T ================================================ { global: retro_*; local: *; }; ================================================ FILE: platform/Linux/README.md ================================================ Build for LINUX =============== ## Installing Dependencies ### Ubuntu > Tested with Ubuntu 18.04 ```bash sudo apt install git make build-essential libsdl2-dev ``` ### Debian > Tested with Debian 11 (Bullseye) ```bash sudo apt install git make build-essential libsdl2-dev libdrm-dev libgbm-dev ``` ### Arch Linux > Tested with Arch Linux (x86_64, kernel version 6.9.3-arch1-1) ```bash sudo pacman -S git make base-devel sdl2 ``` ## Building and Running These steps have been tested on all the distributions listed above and work without modification. ```bash git clone https://github.com/timoinutilis/lowres-nx.git cd lowres-nx/platform/Linux/ make ./output/LowResNX ``` ================================================ FILE: platform/Linux/makefile ================================================ # Declaration of variables CC = gcc CC_FLAGS = -D __LINUX__ -w -I ../../core -I ../../core/machine -I ../../core/accessories -I ../../core/datamanager -I ../../core/interpreter -I ../../core/libraries -I ../../core/overlay -lSDL2main -lSDL2 -lm CC_FLAGS_MAIN = -D __LINUX__ -lSDL2main -lSDL2 -lm `sdl2-config --cflags --static-libs` # File names EXEC = output/LowResNX SOURCES = $(wildcard ../../core/*.c) $(wildcard ../../core/*/*.c) $(wildcard ../../sdl/*.c) OBJECTS = $(SOURCES:.c=.bc) # Main target $(EXEC): $(OBJECTS) @mkdir -p $(@D) $(CC) $(OBJECTS) -o $(EXEC) $(CC_FLAGS_MAIN) # To obtain object files %.bc: %.c $(CC) -c $(CC_FLAGS) $< -o $@ # To remove generated files clean: rm -f $(OBJECTS) ================================================ FILE: platform/Windows/LowRes NX Win/LowRes NX Win.vcxproj ================================================ Debug Win32 Release Win32 Debug x64 Release x64 15.0 {5F65EDCE-D405-4962-BD3B-65C49E160B3E} LowResNXWin 10.0.17134.0 Application true v141 MultiByte Application false v141 true MultiByte Application true v141 MultiByte Application false v141 true MultiByte LowRes NX LowRes NX Level3 Disabled false true ..\..\..\sdl;..\..\..\core\overlay;..\..\..\core\machine;..\..\..\core\libraries;..\..\..\core\interpreter;..\..\..\core\datamanager;..\..\..\core\accessories;..\..\..\core;C:\Users\BARBARA\Documents\Timo\SDL2\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) C:\Users\BARBARA\Documents\Timo\SDL2\lib\x86;%(AdditionalLibraryDirectories) SDL2.lib;SDL2main.lib;%(AdditionalDependencies) Windows Level3 Disabled false true ..\..\..\sdl;..\..\..\core\overlay;..\..\..\core\machine;..\..\..\core\libraries;..\..\..\core\interpreter;..\..\..\core\datamanager;..\..\..\core\accessories;..\..\..\core;C:\Users\BARBARA\Documents\Timo\SDL2\include;%(AdditionalIncludeDirectories) Level3 MaxSpeed true true false true ..\..\..\sdl;..\..\..\core\overlay;..\..\..\core\machine;..\..\..\core\libraries;..\..\..\core\interpreter;..\..\..\core\datamanager;..\..\..\core\accessories;..\..\..\core;C:\Users\BARBARA\Documents\Timo\SDL2\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) MultiThreaded true true C:\Users\BARBARA\Documents\Timo\SDL2\lib\x86;%(AdditionalLibraryDirectories) SDL2.lib;SDL2main.lib;%(AdditionalDependencies) Windows Level3 MaxSpeed true true false true ..\..\..\sdl;..\..\..\core\overlay;..\..\..\core\machine;..\..\..\core\libraries;..\..\..\core\interpreter;..\..\..\core\datamanager;..\..\..\core\accessories;..\..\..\core;C:\Users\BARBARA\Documents\Timo\SDL2\include;%(AdditionalIncludeDirectories) true true ================================================ FILE: platform/Windows/LowRes NX Win/LowRes NX Win.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Resource Files Resource Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files ================================================ FILE: platform/Windows/LowRes NX Win/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by LowRes NX Win.rc // #define IDI_ICON1 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: platform/Windows/LowRes NX Win/resource1.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by LowRes NX Win.rc // #define IDI_ICON1 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: platform/Windows/LowRes NX Win.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27703.2035 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LowRes NX Win", "LowRes NX Win\LowRes NX Win.vcxproj", "{5F65EDCE-D405-4962-BD3B-65C49E160B3E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {5F65EDCE-D405-4962-BD3B-65C49E160B3E}.Debug|x64.ActiveCfg = Debug|x64 {5F65EDCE-D405-4962-BD3B-65C49E160B3E}.Debug|x64.Build.0 = Debug|x64 {5F65EDCE-D405-4962-BD3B-65C49E160B3E}.Debug|x86.ActiveCfg = Debug|Win32 {5F65EDCE-D405-4962-BD3B-65C49E160B3E}.Debug|x86.Build.0 = Debug|Win32 {5F65EDCE-D405-4962-BD3B-65C49E160B3E}.Release|x64.ActiveCfg = Release|x64 {5F65EDCE-D405-4962-BD3B-65C49E160B3E}.Release|x64.Build.0 = Release|x64 {5F65EDCE-D405-4962-BD3B-65C49E160B3E}.Release|x86.ActiveCfg = Release|Win32 {5F65EDCE-D405-4962-BD3B-65C49E160B3E}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {13C7A20F-9918-41F2-B5D1-CCE0C942A3EC} EndGlobalSection EndGlobal ================================================ FILE: platform/iOS/LowRes NX iOS/AppDelegate.swift ================================================ // // AppDelegate.swift // LowRes NX iOS // // Created by Timo Kloss on 1/9/17. // Copyright © 2017 Inutilis Software. All rights reserved. // import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { return true } func applicationWillResignActive(_ application: UIApplication) { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. } func applicationDidEnterBackground(_ application: UIApplication) { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } func applicationWillEnterForeground(_ application: UIApplication) { // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. } func applicationDidBecomeActive(_ application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } } ================================================ FILE: platform/iOS/LowRes NX iOS/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "size" : "20x20", "scale" : "2x" }, { "idiom" : "iphone", "size" : "20x20", "scale" : "3x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, { "idiom" : "iphone", "size" : "29x29", "scale" : "3x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, { "idiom" : "iphone", "size" : "40x40", "scale" : "3x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "2x" }, { "idiom" : "iphone", "size" : "60x60", "scale" : "3x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "1x" }, { "idiom" : "ipad", "size" : "20x20", "scale" : "2x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "1x" }, { "idiom" : "ipad", "size" : "29x29", "scale" : "2x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "1x" }, { "idiom" : "ipad", "size" : "40x40", "scale" : "2x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "1x" }, { "idiom" : "ipad", "size" : "76x76", "scale" : "2x" }, { "idiom" : "ipad", "size" : "83.5x83.5", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: platform/iOS/LowRes NX iOS/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: platform/iOS/LowRes NX iOS/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: platform/iOS/LowRes NX iOS/Base.lproj/Main.storyboard ================================================ ================================================ FILE: platform/iOS/LowRes NX iOS/CoreWrapper.swift ================================================ // // CoreWrapper.swift // LowRes NX iOS // // Created by Timo Kloss on 2/9/17. // Copyright © 2017 Inutilis Software. All rights reserved. // import Foundation protocol CoreWrapperDelegate: class { func coreInterpreterDidFail(coreError: CoreError) -> Void func coreDiskDriveWillAccess(diskDataManager: UnsafeMutablePointer?) -> Bool func coreDiskDriveDidSave(diskDataManager: UnsafeMutablePointer?) -> Void func coreControlsDidChange(controlsInfo: ControlsInfo) -> Void } class CoreWrapper: NSObject { weak var delegate: CoreWrapperDelegate? var core = Core() private var coreDelegate = CoreDelegate() override init() { super.init() core_init(&core) coreDelegate.context = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) coreDelegate.interpreterDidFail = interpreterDidFail coreDelegate.diskDriveWillAccess = diskDriveWillAccess coreDelegate.diskDriveDidSave = diskDriveDidSave coreDelegate.controlsDidChange = controlsDidChange core_setDelegate(&core, &coreDelegate) } deinit { core_deinit(&core) } } class LowResNXError: NSError { let coreError: CoreError init(error: CoreError, sourceCode: String) { coreError = error let index = sourceCode.index(sourceCode.startIndex, offsetBy: String.IndexDistance(error.sourcePosition)) let lineRange = sourceCode.lineRange(for: index ..< index) let lineString = sourceCode[lineRange].trimmingCharacters(in: CharacterSet.whitespaces) let lineNumber = sourceCode.countLines(index: index) let errorString = String(cString:err_getString(error.code)) let errorText = "Error in line \(lineNumber): \(errorString)\n\(lineString)" super.init(domain: "LowResNX", code: Int(error.code.rawValue), userInfo: [NSLocalizedDescriptionKey: errorText]) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension String { func countLines(index: String.Index) -> Int { var count = 1 var searchRange = startIndex ..< endIndex while let foundRange = rangeOfCharacter(from: CharacterSet.newlines, options: .literal, range: searchRange), index >= foundRange.upperBound { searchRange = characters.index(after: foundRange.lowerBound) ..< endIndex count += 1 } return count; } } //MARK: - Core Delegate Functions Wrapper func interpreterDidFail(context: UnsafeMutableRawPointer?, coreError: CoreError) -> Void { let wrapper = Unmanaged.fromOpaque(context!).takeUnretainedValue() wrapper.delegate?.coreInterpreterDidFail(coreError: coreError) } func diskDriveWillAccess(context: UnsafeMutableRawPointer?, diskDataManager: UnsafeMutablePointer?) -> Bool { let wrapper = Unmanaged.fromOpaque(context!).takeUnretainedValue() return wrapper.delegate?.coreDiskDriveWillAccess(diskDataManager: diskDataManager) ?? true } func diskDriveDidSave(context: UnsafeMutableRawPointer?, diskDataManager: UnsafeMutablePointer?) -> Void { let wrapper = Unmanaged.fromOpaque(context!).takeUnretainedValue() wrapper.delegate?.coreDiskDriveDidSave(diskDataManager: diskDataManager) } func controlsDidChange(context: UnsafeMutableRawPointer?, controlsInfo: ControlsInfo) -> Void { let wrapper = Unmanaged.fromOpaque(context!).takeUnretainedValue() wrapper.delegate?.coreControlsDidChange(controlsInfo: controlsInfo) } ================================================ FILE: platform/iOS/LowRes NX iOS/Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString 0.1 CFBundleVersion 1 LSRequiresIPhoneOS UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UIStatusBarHidden UIStatusBarStyle UIStatusBarStyleDefault UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortraitUpsideDown UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ================================================ FILE: platform/iOS/LowRes NX iOS/LowResNX-Bridging-Header.h ================================================ // // LowResNX-Bridging-Header.h // LowRes NX iOS // // Created by Timo Kloss on 23/4/17. // Copyright © 2017 Inutilis Software. All rights reserved. // #include "core.h" ================================================ FILE: platform/iOS/LowRes NX iOS/LowResNXView.swift ================================================ // // LowResNXView.swift // LowRes NX iOS // // Created by Timo Kloss on 1/9/17. // Copyright © 2017 Inutilis Software. All rights reserved. // import UIKit class LowResNXView: UIView { var coreWrapper: CoreWrapper? private var data: UnsafeMutablePointer? private var dataProvider: CGDataProvider? required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) let dataLength = Int(SCREEN_WIDTH) * Int(SCREEN_HEIGHT) * 4; data = UnsafeMutablePointer.allocate(capacity: dataLength) var callbacks = CGDataProviderDirectCallbacks(version: 0, getBytePointer: getBytePointerCallback, releaseBytePointer: nil, getBytesAtPosition: nil, releaseInfo: nil) dataProvider = CGDataProvider(directInfo: data, size: off_t(dataLength), callbacks: &callbacks) } override func awakeFromNib() { super.awakeFromNib() isMultipleTouchEnabled = true } func render() { if let coreWrapper = coreWrapper, let dataProvider = dataProvider { video_renderScreen(&coreWrapper.core, data, SCREEN_WIDTH*4) let image = CGImage(width: Int(SCREEN_WIDTH), height: Int(SCREEN_HEIGHT), bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: Int(SCREEN_WIDTH)*4, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.noneSkipLast.rawValue), provider: dataProvider, decode: nil, shouldInterpolate: false, intent: .defaultIntent) layer.contents = image layer.magnificationFilter = kCAFilterNearest } } override func touchesBegan(_ touches: Set, with event: UIEvent?) { if let coreWrapper = coreWrapper { for touch in touches { let point = screenPoint(touch: touch) core_touchPressed(&coreWrapper.core, Int32(point.x), Int32(point.y), Unmanaged.passUnretained(touch).toOpaque()) } } } override func touchesMoved(_ touches: Set, with event: UIEvent?) { if let coreWrapper = coreWrapper { for touch in touches { let point = screenPoint(touch: touch) core_touchDragged(&coreWrapper.core, Int32(point.x), Int32(point.y), Unmanaged.passUnretained(touch).toOpaque()) } } } override func touchesEnded(_ touches: Set, with event: UIEvent?) { if let coreWrapper = coreWrapper { for touch in touches { core_touchReleased(&coreWrapper.core, Unmanaged.passUnretained(touch).toOpaque()) } } } override func touchesCancelled(_ touches: Set, with event: UIEvent?) { if let coreWrapper = coreWrapper { for touch in touches { core_touchReleased(&coreWrapper.core, Unmanaged.passUnretained(touch).toOpaque()) } } } private func screenPoint(touch: UITouch) -> CGPoint { let viewPoint = touch.location(in: self) let x = viewPoint.x * CGFloat(SCREEN_WIDTH) / bounds.size.width; let y = viewPoint.y * CGFloat(SCREEN_HEIGHT) / bounds.size.height; return CGPoint(x: x, y: y); } } func getBytePointerCallback(_ data: UnsafeMutableRawPointer?) -> UnsafeRawPointer? { return UnsafeRawPointer(data) } ================================================ FILE: platform/iOS/LowRes NX iOS/ViewController.swift ================================================ // // ViewController.swift // LowRes NX iOS // // Created by Timo Kloss on 1/9/17. // Copyright © 2017 Inutilis Software. All rights reserved. // import UIKit import GameController class ViewController: UIViewController, UIKeyInput, CoreWrapperDelegate { @IBOutlet weak var nxView: LowResNXView! @IBOutlet weak var containerView: UIView! @IBOutlet weak var widthConstraint: NSLayoutConstraint! @IBOutlet weak var keyboardConstraint: NSLayoutConstraint! var programSourceCode: String? var coreWrapper = CoreWrapper() var displayLink: CADisplayLink? var compilerError: Error? var pixelExactScaling: Bool = true { didSet { view.setNeedsLayout() } } override func viewDidLoad() { super.viewDidLoad() if let filePath = Bundle.main.path(forResource: "program", ofType: "nx") { do { let programSourceCode = try String(contentsOfFile: filePath, encoding: .ascii) let cString = programSourceCode.cString(using: .ascii) let error = itp_compileProgram(&coreWrapper.core, cString) if error.code != ErrorNone { compilerError = LowResNXError(error: error, sourceCode: programSourceCode) } else { self.programSourceCode = programSourceCode } } catch { compilerError = error } } nxView.coreWrapper = coreWrapper coreWrapper.delegate = self core_willRunProgram(&coreWrapper.core, 0) configureGameControllers() let recognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap)) view.addGestureRecognizer(recognizer) let displayLink = CADisplayLink(target: self, selector: #selector(update)) self.displayLink = displayLink NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: .UIKeyboardWillShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: .UIKeyboardWillHide, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(controllerDidConnect), name: .GCControllerDidConnect, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(controllerDidDisconnect), name: .GCControllerDidDisconnect, object: nil) } deinit { NotificationCenter.default.removeObserver(self) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) displayLink!.add(to: .current, forMode: .defaultRunLoopMode) if let error = compilerError { let alert = UIAlertController(title: "Cannot Run Program", message: error.localizedDescription, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) present(alert, animated: true, completion: nil) } } override var prefersStatusBarHidden: Bool { return true } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() let screenWidth = containerView.bounds.size.width let screenHeight = containerView.bounds.size.height var maxWidthFactor: CGFloat var maxHeightFactor: CGFloat if pixelExactScaling { let scale: CGFloat = view.window?.screen.scale ?? 1.0 maxWidthFactor = floor(screenWidth * scale / CGFloat(SCREEN_WIDTH)) / scale maxHeightFactor = floor(screenHeight * scale / CGFloat(SCREEN_HEIGHT)) / scale } else { maxWidthFactor = screenWidth / CGFloat(SCREEN_WIDTH) maxHeightFactor = screenHeight / CGFloat(SCREEN_HEIGHT) } widthConstraint.constant = (maxWidthFactor < maxHeightFactor) ? maxWidthFactor * CGFloat(SCREEN_WIDTH) : maxHeightFactor * CGFloat(SCREEN_WIDTH) } @objc func update(displaylink: CADisplayLink) { updateGameControllers() core_update(&coreWrapper.core) nxView.render() } func configureGameControllers() { let gameControllers = GCController.controllers() core_setNumPhysicalGamepads(&coreWrapper.core, Int32(gameControllers.count)) var count = 0 for gameController in gameControllers { gameController.playerIndex = GCControllerPlayerIndex(rawValue: count)! gameController.controllerPausedHandler = { [weak self] (controller) in if let coreWrapper = self?.coreWrapper { core_pausePressed(&coreWrapper.core) } } count += 1 } } func updateGameControllers() { for gameController in GCController.controllers() { if let gamepad = gameController.gamepad, gameController.playerIndex != .indexUnset { var up = gamepad.dpad.up.isPressed var down = gamepad.dpad.down.isPressed var left = gamepad.dpad.left.isPressed var right = gamepad.dpad.right.isPressed if let stick = gameController.extendedGamepad?.leftThumbstick { up = up || stick.up.isPressed down = down || stick.down.isPressed left = left || stick.left.isPressed right = right || stick.right.isPressed } let buttonA = gamepad.buttonA.isPressed || gamepad.buttonX.isPressed let buttonB = gamepad.buttonB.isPressed || gamepad.buttonY.isPressed core_setGamepad(&coreWrapper.core, Int32(gameController.playerIndex.rawValue), up, down, left, right, buttonA, buttonB) } } } @objc func handleTap(sender: UITapGestureRecognizer) { if sender.state == .ended { if core_getKeyboardEnabled(&coreWrapper.core) { becomeFirstResponder() } } } override var canBecomeFirstResponder: Bool { return core_getKeyboardEnabled(&coreWrapper.core) } @objc func keyboardWillShow(_ notification: NSNotification) { if let frameValue = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue { let frame = frameValue.cgRectValue keyboardConstraint.constant = view.bounds.size.height - frame.origin.y UIView.animate(withDuration: 0.3, animations: { self.view.layoutIfNeeded() }) } } @objc func keyboardWillHide(_ notification: NSNotification) { keyboardConstraint.constant = 0 UIView.animate(withDuration: 0.3, animations: { self.view.layoutIfNeeded() }) } @objc func controllerDidConnect(_ notification: NSNotification) { configureGameControllers() } @objc func controllerDidDisconnect(_ notification: NSNotification) { configureGameControllers() } // MARK: - Core Wrapper Delegate func coreInterpreterDidFail(coreError: CoreError) { let interpreterError = LowResNXError(error: coreError, sourceCode: programSourceCode!) let alert = UIAlertController(title: "Runtime Error", message: interpreterError.localizedDescription, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) present(alert, animated: true, completion: nil) } func coreDiskDriveWillAccess(diskDataManager: UnsafeMutablePointer?) -> Bool { return true } func coreDiskDriveDidSave(diskDataManager: UnsafeMutablePointer?) { } func coreControlsDidChange(controlsInfo: ControlsInfo) { if controlsInfo.isKeyboardEnabled { becomeFirstResponder() } else { resignFirstResponder() } } // MARK: - UIKeyInput var autocorrectionType: UITextAutocorrectionType = .no var spellCheckingType: UITextSpellCheckingType = .no var keyboardAppearance: UIKeyboardAppearance = .dark var hasText: Bool { return true } func insertText(_ text: String) { if text == "\n" { core_returnPressed(&coreWrapper.core) } else if let key = text.uppercased().unicodeScalars.first?.value { if key < 127 { core_keyPressed(&coreWrapper.core, Int8(key)) } } } func deleteBackward() { core_backspacePressed(&coreWrapper.core) } // this is from UITextInput, needed because of crash on iPhone 6 keyboard (left/right arrows) var selectedTextRange: UITextRange? { return nil } } ================================================ FILE: platform/iOS/LowRes NX iOS.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 9205E8DA1F599F7300927A3F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8D91F599F7300927A3F /* AppDelegate.swift */; }; 9205E8DC1F599F7300927A3F /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8DB1F599F7300927A3F /* ViewController.swift */; }; 9205E8DF1F599F7300927A3F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9205E8DD1F599F7300927A3F /* Main.storyboard */; }; 9205E8E11F599F7300927A3F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9205E8E01F599F7300927A3F /* Assets.xcassets */; }; 9205E8E41F599F7300927A3F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9205E8E21F599F7300927A3F /* LaunchScreen.storyboard */; }; 9205E92D1F599FE000927A3F /* disk_drive.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8ED1F599FE000927A3F /* disk_drive.c */; }; 9205E92E1F599FE000927A3F /* core.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8EF1F599FE000927A3F /* core.c */; }; 9205E92F1F599FE000927A3F /* cmd_background.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8F31F599FE000927A3F /* cmd_background.c */; }; 9205E9301F599FE000927A3F /* cmd_control.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8F51F599FE000927A3F /* cmd_control.c */; }; 9205E9311F599FE000927A3F /* cmd_data.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8F71F599FE000927A3F /* cmd_data.c */; }; 9205E9321F599FE000927A3F /* cmd_files.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8F91F599FE000927A3F /* cmd_files.c */; }; 9205E9331F599FE000927A3F /* cmd_io.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8FB1F599FE000927A3F /* cmd_io.c */; }; 9205E9341F599FE000927A3F /* cmd_maths.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8FD1F599FE000927A3F /* cmd_maths.c */; }; 9205E9351F599FE000927A3F /* cmd_memory.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8FF1F599FE000927A3F /* cmd_memory.c */; }; 9205E9361F599FE000927A3F /* cmd_screen.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9011F599FE000927A3F /* cmd_screen.c */; }; 9205E9371F599FE000927A3F /* cmd_sprites.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9031F599FE000927A3F /* cmd_sprites.c */; }; 9205E9381F599FE000927A3F /* cmd_strings.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9051F599FE000927A3F /* cmd_strings.c */; }; 9205E9391F599FE000927A3F /* cmd_text.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9071F599FE000927A3F /* cmd_text.c */; }; 9205E93A1F599FE000927A3F /* cmd_variables.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9091F599FE000927A3F /* cmd_variables.c */; }; 9205E93B1F599FE000927A3F /* data.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E90B1F599FE000927A3F /* data.c */; }; 9205E93C1F599FE000927A3F /* error.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E90D1F599FE000927A3F /* error.c */; }; 9205E93D1F599FE000927A3F /* interpreter.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E90F1F599FE000927A3F /* interpreter.c */; }; 9205E93E1F599FE000927A3F /* interpreter_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9121F599FE000927A3F /* interpreter_utils.c */; }; 9205E93F1F599FE000927A3F /* labels.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9141F599FE000927A3F /* labels.c */; }; 9205E9401F599FE000927A3F /* rcstring.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9161F599FE000927A3F /* rcstring.c */; }; 9205E9411F599FE000927A3F /* token.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9181F599FE000927A3F /* token.c */; }; 9205E9421F599FE000927A3F /* value.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E91A1F599FE000927A3F /* value.c */; }; 9205E9431F599FE000927A3F /* variables.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E91C1F599FE000927A3F /* variables.c */; }; 9205E9441F599FE000927A3F /* default_characters.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E91F1F599FE000927A3F /* default_characters.c */; }; 9205E9451F599FE000927A3F /* sprites_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9211F599FE000927A3F /* sprites_lib.c */; }; 9205E9461F599FE000927A3F /* text_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9231F599FE000927A3F /* text_lib.c */; }; 9205E9471F599FE000927A3F /* audio_chip.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9261F599FE000927A3F /* audio_chip.c */; }; 9205E9481F599FE000927A3F /* machine.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9291F599FE000927A3F /* machine.c */; }; 9205E9491F599FE000927A3F /* video_chip.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E92B1F599FE000927A3F /* video_chip.c */; }; 9205E94C1F59A96600927A3F /* program.nx in Resources */ = {isa = PBXBuildFile; fileRef = 9205E94B1F59A96600927A3F /* program.nx */; }; 9205E94E1F59BEE900927A3F /* LowResNXView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9205E94D1F59BEE900927A3F /* LowResNXView.swift */; }; 92521BCF1F5B105E0078A7C7 /* CoreWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92521BCE1F5B105E0078A7C7 /* CoreWrapper.swift */; }; 9286A7141F8FB816005C9EC1 /* charsets.c in Sources */ = {isa = PBXBuildFile; fileRef = 9286A70E1F8FB815005C9EC1 /* charsets.c */; }; 9286A7151F8FB816005C9EC1 /* string_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 9286A7111F8FB816005C9EC1 /* string_utils.c */; }; 9286A7161F8FB816005C9EC1 /* tokenizer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9286A7121F8FB816005C9EC1 /* tokenizer.c */; }; 9286A71A1F8FB84A005C9EC1 /* data_manager.c in Sources */ = {isa = PBXBuildFile; fileRef = 9286A7181F8FB84A005C9EC1 /* data_manager.c */; }; 9286A71C1F8FCE1A005C9EC1 /* core_delegate.c in Sources */ = {isa = PBXBuildFile; fileRef = 9286A71B1F8FCE1A005C9EC1 /* core_delegate.c */; }; 92AE47D41F6D94A1008BD49C /* overlay.c in Sources */ = {isa = PBXBuildFile; fileRef = 92AE47D01F6D94A1008BD49C /* overlay.c */; }; 92AE47D51F6D94A1008BD49C /* overlay_data.c in Sources */ = {isa = PBXBuildFile; fileRef = 92AE47D21F6D94A1008BD49C /* overlay_data.c */; }; 92ED99731FA4F7B90073559B /* startup_sequence.c in Sources */ = {isa = PBXBuildFile; fileRef = 92ED99711FA4F7B80073559B /* startup_sequence.c */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 9205E8D61F599F7300927A3F /* LowRes NX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "LowRes NX.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 9205E8D91F599F7300927A3F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 9205E8DB1F599F7300927A3F /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 9205E8E01F599F7300927A3F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 9205E8E51F599F7300927A3F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9205E8ED1F599FE000927A3F /* disk_drive.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = disk_drive.c; sourceTree = ""; }; 9205E8EE1F599FE000927A3F /* disk_drive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = disk_drive.h; sourceTree = ""; }; 9205E8EF1F599FE000927A3F /* core.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = core.c; sourceTree = ""; }; 9205E8F01F599FE000927A3F /* core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core.h; sourceTree = ""; }; 9205E8F11F599FE000927A3F /* core_delegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core_delegate.h; sourceTree = ""; }; 9205E8F31F599FE000927A3F /* cmd_background.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_background.c; sourceTree = ""; }; 9205E8F41F599FE000927A3F /* cmd_background.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_background.h; sourceTree = ""; }; 9205E8F51F599FE000927A3F /* cmd_control.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_control.c; sourceTree = ""; }; 9205E8F61F599FE000927A3F /* cmd_control.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_control.h; sourceTree = ""; }; 9205E8F71F599FE000927A3F /* cmd_data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_data.c; sourceTree = ""; }; 9205E8F81F599FE000927A3F /* cmd_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_data.h; sourceTree = ""; }; 9205E8F91F599FE000927A3F /* cmd_files.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_files.c; sourceTree = ""; }; 9205E8FA1F599FE000927A3F /* cmd_files.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_files.h; sourceTree = ""; }; 9205E8FB1F599FE000927A3F /* cmd_io.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_io.c; sourceTree = ""; }; 9205E8FC1F599FE000927A3F /* cmd_io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_io.h; sourceTree = ""; }; 9205E8FD1F599FE000927A3F /* cmd_maths.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_maths.c; sourceTree = ""; }; 9205E8FE1F599FE000927A3F /* cmd_maths.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_maths.h; sourceTree = ""; }; 9205E8FF1F599FE000927A3F /* cmd_memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_memory.c; sourceTree = ""; }; 9205E9001F599FE000927A3F /* cmd_memory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_memory.h; sourceTree = ""; }; 9205E9011F599FE000927A3F /* cmd_screen.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_screen.c; sourceTree = ""; }; 9205E9021F599FE000927A3F /* cmd_screen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_screen.h; sourceTree = ""; }; 9205E9031F599FE000927A3F /* cmd_sprites.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_sprites.c; sourceTree = ""; }; 9205E9041F599FE000927A3F /* cmd_sprites.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_sprites.h; sourceTree = ""; }; 9205E9051F599FE000927A3F /* cmd_strings.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_strings.c; sourceTree = ""; }; 9205E9061F599FE000927A3F /* cmd_strings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_strings.h; sourceTree = ""; }; 9205E9071F599FE000927A3F /* cmd_text.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_text.c; sourceTree = ""; }; 9205E9081F599FE000927A3F /* cmd_text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_text.h; sourceTree = ""; }; 9205E9091F599FE000927A3F /* cmd_variables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_variables.c; sourceTree = ""; }; 9205E90A1F599FE000927A3F /* cmd_variables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_variables.h; sourceTree = ""; }; 9205E90B1F599FE000927A3F /* data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = data.c; sourceTree = ""; }; 9205E90C1F599FE000927A3F /* data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data.h; sourceTree = ""; }; 9205E90D1F599FE000927A3F /* error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = error.c; sourceTree = ""; }; 9205E90E1F599FE000927A3F /* error.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = error.h; sourceTree = ""; }; 9205E90F1F599FE000927A3F /* interpreter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = interpreter.c; sourceTree = ""; }; 9205E9101F599FE000927A3F /* interpreter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interpreter.h; sourceTree = ""; }; 9205E9111F599FE000927A3F /* interpreter_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interpreter_config.h; sourceTree = ""; }; 9205E9121F599FE000927A3F /* interpreter_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = interpreter_utils.c; sourceTree = ""; }; 9205E9131F599FE000927A3F /* interpreter_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interpreter_utils.h; sourceTree = ""; }; 9205E9141F599FE000927A3F /* labels.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = labels.c; sourceTree = ""; }; 9205E9151F599FE000927A3F /* labels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = labels.h; sourceTree = ""; }; 9205E9161F599FE000927A3F /* rcstring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rcstring.c; sourceTree = ""; }; 9205E9171F599FE000927A3F /* rcstring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rcstring.h; sourceTree = ""; }; 9205E9181F599FE000927A3F /* token.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = token.c; sourceTree = ""; }; 9205E9191F599FE000927A3F /* token.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = token.h; sourceTree = ""; }; 9205E91A1F599FE000927A3F /* value.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = value.c; sourceTree = ""; }; 9205E91B1F599FE000927A3F /* value.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = value.h; sourceTree = ""; }; 9205E91C1F599FE000927A3F /* variables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = variables.c; sourceTree = ""; }; 9205E91D1F599FE000927A3F /* variables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = variables.h; sourceTree = ""; }; 9205E91F1F599FE000927A3F /* default_characters.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = default_characters.c; sourceTree = ""; }; 9205E9201F599FE000927A3F /* default_characters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = default_characters.h; sourceTree = ""; }; 9205E9211F599FE000927A3F /* sprites_lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sprites_lib.c; sourceTree = ""; }; 9205E9221F599FE000927A3F /* sprites_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sprites_lib.h; sourceTree = ""; }; 9205E9231F599FE000927A3F /* text_lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = text_lib.c; sourceTree = ""; }; 9205E9241F599FE000927A3F /* text_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = text_lib.h; sourceTree = ""; }; 9205E9261F599FE000927A3F /* audio_chip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = audio_chip.c; sourceTree = ""; }; 9205E9271F599FE000927A3F /* audio_chip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audio_chip.h; sourceTree = ""; }; 9205E9281F599FE000927A3F /* io_chip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = io_chip.h; sourceTree = ""; }; 9205E9291F599FE000927A3F /* machine.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = machine.c; sourceTree = ""; }; 9205E92A1F599FE000927A3F /* machine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = machine.h; sourceTree = ""; }; 9205E92B1F599FE000927A3F /* video_chip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = video_chip.c; sourceTree = ""; }; 9205E92C1F599FE000927A3F /* video_chip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = video_chip.h; sourceTree = ""; }; 9205E94A1F59A3A400927A3F /* LowResNX-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LowResNX-Bridging-Header.h"; sourceTree = ""; }; 9205E94B1F59A96600927A3F /* program.nx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = program.nx; sourceTree = ""; }; 9205E94D1F59BEE900927A3F /* LowResNXView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LowResNXView.swift; sourceTree = ""; }; 9205E9511F59C57D00927A3F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 9205E9521F59C57D00927A3F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 92521BCE1F5B105E0078A7C7 /* CoreWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreWrapper.swift; sourceTree = ""; }; 9286A70E1F8FB815005C9EC1 /* charsets.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = charsets.c; sourceTree = ""; }; 9286A70F1F8FB815005C9EC1 /* tokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tokenizer.h; sourceTree = ""; }; 9286A7101F8FB816005C9EC1 /* string_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_utils.h; sourceTree = ""; }; 9286A7111F8FB816005C9EC1 /* string_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = string_utils.c; sourceTree = ""; }; 9286A7121F8FB816005C9EC1 /* tokenizer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tokenizer.c; sourceTree = ""; }; 9286A7131F8FB816005C9EC1 /* charsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = charsets.h; sourceTree = ""; }; 9286A7181F8FB84A005C9EC1 /* data_manager.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = data_manager.c; sourceTree = ""; }; 9286A7191F8FB84A005C9EC1 /* data_manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data_manager.h; sourceTree = ""; }; 9286A71B1F8FCE1A005C9EC1 /* core_delegate.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = core_delegate.c; sourceTree = ""; }; 92AE47D01F6D94A1008BD49C /* overlay.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = overlay.c; sourceTree = ""; }; 92AE47D11F6D94A1008BD49C /* overlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = overlay.h; sourceTree = ""; }; 92AE47D21F6D94A1008BD49C /* overlay_data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = overlay_data.c; sourceTree = ""; }; 92AE47D31F6D94A1008BD49C /* overlay_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = overlay_data.h; sourceTree = ""; }; 92ED99711FA4F7B80073559B /* startup_sequence.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = startup_sequence.c; sourceTree = ""; }; 92ED99721FA4F7B80073559B /* startup_sequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = startup_sequence.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 9205E8D31F599F7300927A3F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 9205E8CD1F599F7300927A3F = { isa = PBXGroup; children = ( 9205E94B1F59A96600927A3F /* program.nx */, 9205E8EB1F599FE000927A3F /* core */, 9205E8D81F599F7300927A3F /* LowRes NX iOS */, 9205E8D71F599F7300927A3F /* Products */, ); sourceTree = ""; }; 9205E8D71F599F7300927A3F /* Products */ = { isa = PBXGroup; children = ( 9205E8D61F599F7300927A3F /* LowRes NX.app */, ); name = Products; sourceTree = ""; }; 9205E8D81F599F7300927A3F /* LowRes NX iOS */ = { isa = PBXGroup; children = ( 9205E94A1F59A3A400927A3F /* LowResNX-Bridging-Header.h */, 9205E8D91F599F7300927A3F /* AppDelegate.swift */, 92521BCE1F5B105E0078A7C7 /* CoreWrapper.swift */, 9205E8DB1F599F7300927A3F /* ViewController.swift */, 9205E94D1F59BEE900927A3F /* LowResNXView.swift */, 9205E8DD1F599F7300927A3F /* Main.storyboard */, 9205E8E01F599F7300927A3F /* Assets.xcassets */, 9205E8E21F599F7300927A3F /* LaunchScreen.storyboard */, 9205E8E51F599F7300927A3F /* Info.plist */, ); path = "LowRes NX iOS"; sourceTree = ""; }; 9205E8EB1F599FE000927A3F /* core */ = { isa = PBXGroup; children = ( 9205E8EF1F599FE000927A3F /* core.c */, 9205E8F01F599FE000927A3F /* core.h */, 9205E8F11F599FE000927A3F /* core_delegate.h */, 9286A71B1F8FCE1A005C9EC1 /* core_delegate.c */, 9286A7171F8FB84A005C9EC1 /* datamanager */, 9205E8F21F599FE000927A3F /* interpreter */, 9205E91E1F599FE000927A3F /* libraries */, 9205E9251F599FE000927A3F /* machine */, 9205E8EC1F599FE000927A3F /* accessories */, 92AE47CF1F6D94A1008BD49C /* overlay */, ); name = core; path = ../../core; sourceTree = ""; }; 9205E8EC1F599FE000927A3F /* accessories */ = { isa = PBXGroup; children = ( 9205E8ED1F599FE000927A3F /* disk_drive.c */, 9205E8EE1F599FE000927A3F /* disk_drive.h */, ); path = accessories; sourceTree = ""; }; 9205E8F21F599FE000927A3F /* interpreter */ = { isa = PBXGroup; children = ( 9286A70E1F8FB815005C9EC1 /* charsets.c */, 9286A7131F8FB816005C9EC1 /* charsets.h */, 9286A7111F8FB816005C9EC1 /* string_utils.c */, 9286A7101F8FB816005C9EC1 /* string_utils.h */, 9286A7121F8FB816005C9EC1 /* tokenizer.c */, 9286A70F1F8FB815005C9EC1 /* tokenizer.h */, 9205E8F31F599FE000927A3F /* cmd_background.c */, 9205E8F41F599FE000927A3F /* cmd_background.h */, 9205E8F51F599FE000927A3F /* cmd_control.c */, 9205E8F61F599FE000927A3F /* cmd_control.h */, 9205E8F71F599FE000927A3F /* cmd_data.c */, 9205E8F81F599FE000927A3F /* cmd_data.h */, 9205E8F91F599FE000927A3F /* cmd_files.c */, 9205E8FA1F599FE000927A3F /* cmd_files.h */, 9205E8FB1F599FE000927A3F /* cmd_io.c */, 9205E8FC1F599FE000927A3F /* cmd_io.h */, 9205E8FD1F599FE000927A3F /* cmd_maths.c */, 9205E8FE1F599FE000927A3F /* cmd_maths.h */, 9205E8FF1F599FE000927A3F /* cmd_memory.c */, 9205E9001F599FE000927A3F /* cmd_memory.h */, 9205E9011F599FE000927A3F /* cmd_screen.c */, 9205E9021F599FE000927A3F /* cmd_screen.h */, 9205E9031F599FE000927A3F /* cmd_sprites.c */, 9205E9041F599FE000927A3F /* cmd_sprites.h */, 9205E9051F599FE000927A3F /* cmd_strings.c */, 9205E9061F599FE000927A3F /* cmd_strings.h */, 9205E9071F599FE000927A3F /* cmd_text.c */, 9205E9081F599FE000927A3F /* cmd_text.h */, 9205E9091F599FE000927A3F /* cmd_variables.c */, 9205E90A1F599FE000927A3F /* cmd_variables.h */, 9205E90B1F599FE000927A3F /* data.c */, 9205E90C1F599FE000927A3F /* data.h */, 9205E90D1F599FE000927A3F /* error.c */, 9205E90E1F599FE000927A3F /* error.h */, 9205E90F1F599FE000927A3F /* interpreter.c */, 9205E9101F599FE000927A3F /* interpreter.h */, 9205E9111F599FE000927A3F /* interpreter_config.h */, 9205E9121F599FE000927A3F /* interpreter_utils.c */, 9205E9131F599FE000927A3F /* interpreter_utils.h */, 9205E9141F599FE000927A3F /* labels.c */, 9205E9151F599FE000927A3F /* labels.h */, 9205E9161F599FE000927A3F /* rcstring.c */, 9205E9171F599FE000927A3F /* rcstring.h */, 9205E9181F599FE000927A3F /* token.c */, 9205E9191F599FE000927A3F /* token.h */, 9205E91A1F599FE000927A3F /* value.c */, 9205E91B1F599FE000927A3F /* value.h */, 9205E91C1F599FE000927A3F /* variables.c */, 9205E91D1F599FE000927A3F /* variables.h */, ); path = interpreter; sourceTree = ""; }; 9205E91E1F599FE000927A3F /* libraries */ = { isa = PBXGroup; children = ( 92ED99711FA4F7B80073559B /* startup_sequence.c */, 92ED99721FA4F7B80073559B /* startup_sequence.h */, 9205E91F1F599FE000927A3F /* default_characters.c */, 9205E9201F599FE000927A3F /* default_characters.h */, 9205E9211F599FE000927A3F /* sprites_lib.c */, 9205E9221F599FE000927A3F /* sprites_lib.h */, 9205E9231F599FE000927A3F /* text_lib.c */, 9205E9241F599FE000927A3F /* text_lib.h */, ); path = libraries; sourceTree = ""; }; 9205E9251F599FE000927A3F /* machine */ = { isa = PBXGroup; children = ( 9205E9261F599FE000927A3F /* audio_chip.c */, 9205E9271F599FE000927A3F /* audio_chip.h */, 9205E9281F599FE000927A3F /* io_chip.h */, 9205E9291F599FE000927A3F /* machine.c */, 9205E92A1F599FE000927A3F /* machine.h */, 9205E92B1F599FE000927A3F /* video_chip.c */, 9205E92C1F599FE000927A3F /* video_chip.h */, ); path = machine; sourceTree = ""; }; 9286A7171F8FB84A005C9EC1 /* datamanager */ = { isa = PBXGroup; children = ( 9286A7181F8FB84A005C9EC1 /* data_manager.c */, 9286A7191F8FB84A005C9EC1 /* data_manager.h */, ); path = datamanager; sourceTree = ""; }; 92AE47CF1F6D94A1008BD49C /* overlay */ = { isa = PBXGroup; children = ( 92AE47D01F6D94A1008BD49C /* overlay.c */, 92AE47D11F6D94A1008BD49C /* overlay.h */, 92AE47D21F6D94A1008BD49C /* overlay_data.c */, 92AE47D31F6D94A1008BD49C /* overlay_data.h */, ); path = overlay; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 9205E8D51F599F7300927A3F /* LowRes NX */ = { isa = PBXNativeTarget; buildConfigurationList = 9205E8E81F599F7300927A3F /* Build configuration list for PBXNativeTarget "LowRes NX" */; buildPhases = ( 9205E8D21F599F7300927A3F /* Sources */, 9205E8D31F599F7300927A3F /* Frameworks */, 9205E8D41F599F7300927A3F /* Resources */, ); buildRules = ( ); dependencies = ( ); name = "LowRes NX"; productName = "LowRes NX iOS"; productReference = 9205E8D61F599F7300927A3F /* LowRes NX.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 9205E8CE1F599F7300927A3F /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0830; LastUpgradeCheck = 0900; ORGANIZATIONNAME = "Inutilis Software"; TargetAttributes = { 9205E8D51F599F7300927A3F = { CreatedOnToolsVersion = 8.3.3; DevelopmentTeam = BZ4VC623NH; LastSwiftMigration = 0900; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 9205E8D11F599F7300927A3F /* Build configuration list for PBXProject "LowRes NX iOS" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 9205E8CD1F599F7300927A3F; productRefGroup = 9205E8D71F599F7300927A3F /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 9205E8D51F599F7300927A3F /* LowRes NX */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 9205E8D41F599F7300927A3F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 9205E8E41F599F7300927A3F /* LaunchScreen.storyboard in Resources */, 9205E8E11F599F7300927A3F /* Assets.xcassets in Resources */, 9205E94C1F59A96600927A3F /* program.nx in Resources */, 9205E8DF1F599F7300927A3F /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 9205E8D21F599F7300927A3F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 9205E93E1F599FE000927A3F /* interpreter_utils.c in Sources */, 9205E9361F599FE000927A3F /* cmd_screen.c in Sources */, 9205E9351F599FE000927A3F /* cmd_memory.c in Sources */, 9286A71C1F8FCE1A005C9EC1 /* core_delegate.c in Sources */, 9205E92E1F599FE000927A3F /* core.c in Sources */, 92ED99731FA4F7B90073559B /* startup_sequence.c in Sources */, 9205E8DC1F599F7300927A3F /* ViewController.swift in Sources */, 92AE47D51F6D94A1008BD49C /* overlay_data.c in Sources */, 9205E9321F599FE000927A3F /* cmd_files.c in Sources */, 9205E9461F599FE000927A3F /* text_lib.c in Sources */, 9205E9381F599FE000927A3F /* cmd_strings.c in Sources */, 9286A7151F8FB816005C9EC1 /* string_utils.c in Sources */, 92521BCF1F5B105E0078A7C7 /* CoreWrapper.swift in Sources */, 9205E9451F599FE000927A3F /* sprites_lib.c in Sources */, 9205E9441F599FE000927A3F /* default_characters.c in Sources */, 9205E94E1F59BEE900927A3F /* LowResNXView.swift in Sources */, 9205E9411F599FE000927A3F /* token.c in Sources */, 9205E9401F599FE000927A3F /* rcstring.c in Sources */, 9205E92D1F599FE000927A3F /* disk_drive.c in Sources */, 9205E8DA1F599F7300927A3F /* AppDelegate.swift in Sources */, 9286A71A1F8FB84A005C9EC1 /* data_manager.c in Sources */, 9205E9391F599FE000927A3F /* cmd_text.c in Sources */, 9205E93F1F599FE000927A3F /* labels.c in Sources */, 9205E9331F599FE000927A3F /* cmd_io.c in Sources */, 9286A7161F8FB816005C9EC1 /* tokenizer.c in Sources */, 9286A7141F8FB816005C9EC1 /* charsets.c in Sources */, 9205E9431F599FE000927A3F /* variables.c in Sources */, 9205E9371F599FE000927A3F /* cmd_sprites.c in Sources */, 9205E9341F599FE000927A3F /* cmd_maths.c in Sources */, 9205E93C1F599FE000927A3F /* error.c in Sources */, 9205E93A1F599FE000927A3F /* cmd_variables.c in Sources */, 9205E9301F599FE000927A3F /* cmd_control.c in Sources */, 9205E9491F599FE000927A3F /* video_chip.c in Sources */, 9205E93D1F599FE000927A3F /* interpreter.c in Sources */, 9205E92F1F599FE000927A3F /* cmd_background.c in Sources */, 9205E9421F599FE000927A3F /* value.c in Sources */, 9205E9471F599FE000927A3F /* audio_chip.c in Sources */, 92AE47D41F6D94A1008BD49C /* overlay.c in Sources */, 9205E9481F599FE000927A3F /* machine.c in Sources */, 9205E9311F599FE000927A3F /* cmd_data.c in Sources */, 9205E93B1F599FE000927A3F /* data.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 9205E8DD1F599F7300927A3F /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 9205E9511F59C57D00927A3F /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 9205E8E21F599F7300927A3F /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 9205E9521F59C57D00927A3F /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 9205E8E61F599F7300927A3F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 9205E8E71F599F7300927A3F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Release; }; 9205E8E91F599F7300927A3F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = BZ4VC623NH; INFOPLIST_FILE = "LowRes NX iOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.inutilis.ios.LowRes-NX"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "LowRes NX iOS/LowResNX-Bridging-Header.h"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 4.0; }; name = Debug; }; 9205E8EA1F599F7300927A3F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = BZ4VC623NH; INFOPLIST_FILE = "LowRes NX iOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.inutilis.ios.LowRes-NX"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "LowRes NX iOS/LowResNX-Bridging-Header.h"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 4.0; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 9205E8D11F599F7300927A3F /* Build configuration list for PBXProject "LowRes NX iOS" */ = { isa = XCConfigurationList; buildConfigurations = ( 9205E8E61F599F7300927A3F /* Debug */, 9205E8E71F599F7300927A3F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 9205E8E81F599F7300927A3F /* Build configuration list for PBXNativeTarget "LowRes NX" */ = { isa = XCConfigurationList; buildConfigurations = ( 9205E8E91F599F7300927A3F /* Debug */, 9205E8EA1F599F7300927A3F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 9205E8CE1F599F7300927A3F /* Project object */; } ================================================ FILE: platform/iOS/LowRes NX iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: platform/iOS/LowRes NX iOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: platform/iOS/program.nx ================================================ RANDOMIZE TIMER 'VALUES FOR 16 ALIENS DIM ALIENS(15,3) ' 0 TYPE (0=DISABLED) ' 1 TICK ' 2 START Y ' 3 HITS BG 0 BG COPY 0,0,32,16 TO 0,0 BG 1 BG COPY 0,16,32,16 TO 0,0 GAMEPAD 1 ON RASTER GOSUB RASTERFX 'INIT SPRITES SPRITE.A 0,(4,,,,1) SPRITE.A 1,(5,,,,) FOR I=2 TO 9 SPRITE.A I,(5,,,,) NEXT I 'INIT VARIABLES SCORE=0 PX=64 PY=80 BULLET=0 ALIEN=0 'HUD BG 0 FONT ,(0,,,1,) TEXT 0,0,"BETA" 'GAME LOOP DO TICK=TICK+1 'SPAWN ALIENS IF TICK MOD 60=0 THEN ALIENS(ALIEN,0)=INT(RND*4)+1 ALIENS(ALIEN,1)=0 ALIENS(ALIEN,2)=32+RND*96 ALIENS(ALIEN,3)=0 ALIEN=(ALIEN+1) MOD 16 END IF 'UPDATE ALIENS FOR I=0 TO 15 ATYPE=ALIENS(I,0) IF ATYPE>0 THEN ALIENS(I,1)=ALIENS(I,1)+1 ATICK=ALIENS(I,1) X=192-ATICK Y=ALIENS(I,2) IF X>0 THEN IF ATYPE MOD 2=0 THEN PAL=6 ELSE PAL=7 IF ATYPE<=2 THEN SIZE=0 FRAME=10+INT((ATICK MOD 12)/6) Y=Y+SIN(ATICK/10)*16 ELSE SIZE=1 FRAME=6+INT((ATICK MOD 16)/8)*2 Y=Y+SIN(ATICK/30)*32 END IF SPRITE.A 10+I,(PAL,,,,SIZE) SPRITE 10+I,X,Y,FRAME IF SPRITE HIT(10+I,2 TO 9) THEN SPRITE HIT,0,0, SPRITE.A 10+I,(3,,,,) ALIENS(I,3)=ALIENS(I,3)+1 SCORE=SCORE+20 END IF ELSE 'RESET SPRITE 10+I,0,0, ALIENS(I,0)=0 END IF END IF NEXT I 'UPDATE BULLETS FOR I=2 TO 9 IF SPRITE.Y(I)>0 THEN SPRITE I,SPRITE.X(I)+6,, IF SPRITE.X(I)>192 THEN SPRITE I,0,0, END IF NEXT I 'PLAYER CONTROL IF UP(0) AND PY>32 THEN PY=PY-2 IF DOWN(0) AND PY<144 THEN PY=PY+2 IF LEFT(0) AND PX>40 THEN PX=PX-2 IF RIGHT(0) AND PX<160 THEN PX=PX+2 'PLAYER SPRITE SPRITE 0,PX,PY,1 SPRITE 1,PX-8,PY+5,19+INT((TICK MOD 8)/4) 'SHOOT IF BUTTON TAP(0,0) OR BUTTON TAP(0,1) THEN SPRITE BULLET+2,PX+8,PY+11,3 BULLET=(BULLET+1) MOD 8 END IF 'HUD NUMBER 15,0,SCORE,5 WAIT VBL LOOP RASTERFX: 'STARS AND FOREGROUND HILLS IF RASTER=0 THEN DISPLAY 1,TICK/3,0 ELSE IF RASTER=80 THEN DISPLAY 1,TICK*2,0 END IF 'STATUS BAR AND PLANET SURFACE IF RASTER=0 THEN DISPLAY 0,0,0 ELSE IF RASTER=8 THEN DISPLAY 0,TICK,0 ELSE IF RASTER=80 THEN DISPLAY 0,TICK*5/4,0 ELSE IF RASTER=96 THEN DISPLAY 0,TICK*6/4,0 ELSE IF RASTER=112 THEN DISPLAY 0,TICK*7/4,0 END IF RETURN #1:MAIN PALETTES 00 3F 1B 05 00 26 11 01 00 3B 26 12 00 3F 3E 39 00 1F 2A 15 00 3C 30 06 00 3F 31 21 00 3F 16 12 #2:MAIN CHARACTERS 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 60 00 78 FF 54 00 00 40 60 70 78 FF FF 00 00 00 00 00 00 FC 02 00 00 00 00 00 00 FC FE 00 B3 00 00 00 00 00 00 02 BC 02 00 00 00 00 00 90 60 60 90 00 00 00 00 F0 90 90 F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 10 30 40 0C 8E 0E 00 07 1F 2F 7F 73 F5 F5 00 20 08 04 02 60 E1 E0 00 E0 F8 FC FE 9E 5F 5F 04 10 28 40 0C 8E 0E 06 07 1F 37 7F 73 F5 F5 F9 20 08 04 02 60 E1 E0 C0 E0 F8 FC FE 9E 5F 5F 3F 00 42 81 66 00 00 66 42 00 7E FF BD FF FF 66 42 42 81 66 66 00 00 66 24 7E FF BD BD FF 7E 66 24 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A8 FF 00 78 02 60 40 00 FF FF 7C 78 7C 60 40 00 7F BD FA 7C 00 00 00 00 FF C3 86 7C 00 00 00 00 10 04 09 A3 09 04 10 00 10 06 0E BC 0E 06 10 00 40 12 04 13 04 12 40 00 40 12 07 1C 07 12 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 80 40 3C 38 30 18 FB FF FF 7F 3C 38 30 18 40 00 01 02 3C 1C 0C 18 BF FF FF FE 3C 1C 0C 18 80 00 40 20 3C 1C 0E 02 FF 7F 7F 3F 3C 1C 0E 02 01 00 02 04 3C 38 70 40 FF FE FE FC 3C 38 70 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 02 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 08 00 2A 00 08 00 00 00 08 08 36 08 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00 00 48 30 30 48 00 00 00 00 78 48 48 78 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 00 7E C3 C3 7E 00 00 FF FF FF FC FC 81 FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 22 00 00 00 00 00 18 76 DF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 38 44 38 00 FF FF FF FF FF FB C7 FF 00 00 1C 0E 00 00 00 00 FF FF FF F1 FF FF FF FF 00 00 01 04 01 02 16 4C 01 03 06 0B 1E 3D 69 B3 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 80 40 60 B0 10 28 04 80 C0 E0 F0 F8 FC FE FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 18 3C 3C 3C 3C 18 3C 18 18 24 24 24 24 18 24 18 6C FE FE 7E 24 00 00 00 6C 92 92 5A 24 00 00 00 24 7E FF 7E 7E FF 7E 24 24 5A 81 5A 5A 81 5A 24 08 3E 7F 7E 3F 7F 3E 08 08 36 41 46 31 41 36 08 62 F7 FE 7C 3E 7F EF 46 62 95 9A 74 2E 59 A9 46 1C 3E 7E 7E FF FE 7F 3A 1C 22 4A 46 91 9A 45 3A 18 3C 3C 78 30 00 00 00 18 24 24 48 30 00 00 00 0C 1E 3C 78 78 3C 1E 0C 0C 12 24 48 48 24 12 0C 30 78 3C 1E 1E 3C 78 30 30 48 24 12 12 24 48 30 00 24 7E 7E FF 7E 7E 24 00 24 5A 66 81 66 5A 24 00 18 3C 7E FF 7E 3C 18 00 18 24 66 81 66 24 18 00 00 00 18 3C 3C 78 30 00 00 00 18 24 24 48 30 00 00 00 7E FF 7E 00 00 00 00 00 7E 81 7E 00 00 00 00 00 00 18 3C 3C 18 00 00 00 00 18 24 24 18 06 0F 1E 3C 78 F0 E0 40 06 09 12 24 48 90 A0 40 3C 7E FF FF FF FF 7E 3C 3C 42 99 91 89 99 42 3C 18 3C 7C 3C 3C 7E FF 7E 18 24 44 24 24 66 81 7E 3C 7E FF 7E 3C 7E FF 7E 3C 42 99 72 24 4E 81 7E 3C 7E FF 7E 6F FF 7E 3C 3C 42 99 72 69 99 42 3C 66 FF FF FF 7F 0F 0F 06 66 99 99 81 79 09 09 06 7E FF FE FE 7F 7F FE 7C 7E 81 9E 82 79 79 82 7C 1C 3E 7C FE FF FF 7E 3C 1C 22 4C 82 99 99 42 3C 7E FF 7F 1E 3C 78 78 30 7E 81 79 12 24 48 48 30 3C 7E FF 7E FF FF 7E 3C 3C 42 99 42 99 99 42 3C 3C 7E FF 7F 7F FF 7E 3C 3C 42 99 41 79 99 42 3C 00 00 18 3C 18 3C 18 00 00 00 18 24 18 24 18 00 00 00 18 3C 18 3C 78 30 00 00 18 24 18 24 48 30 00 0C 1E 3C 78 3C 1E 0C 00 0C 12 24 48 24 12 0C 00 00 7E FF 7E FF 7E 00 00 00 7E 81 7E 81 7E 00 00 30 78 3C 1E 3C 78 30 00 30 48 24 12 24 48 30 3C 7E FF 7E 3C 18 3C 18 3C 42 99 72 24 18 24 18 3C 7E FF FF FF FE 7E 3C 3C 42 99 91 91 9E 42 3C 18 3C 7E FF FF FF FF 66 18 24 42 99 81 99 99 66 7C FE FF FE FF FF FE 7C 7C 82 99 82 99 99 82 7C 3C 7E FF F6 F6 FF 7E 3C 3C 42 99 96 96 99 42 3C 78 FC FE FF FF FE FC 78 78 84 92 99 99 92 84 78 7E FF FE FC F8 FE FF 7E 7E 81 9E 84 98 9E 81 7E 7E FF FE FC F8 F0 F0 60 7E 81 9E 84 98 90 90 60 3C 7E FE FF FF FF 7E 3C 3C 42 9E 91 99 99 42 3C 66 FF FF FF FF FF FF 66 66 99 99 81 99 99 99 66 3C 7E 3C 3C 3C 3C 7E 3C 3C 42 24 24 24 24 42 3C 1E 3F 1F 0F 6F FF 7E 3C 1E 21 19 09 69 99 42 3C 66 FF FE FC FC FE FF 66 66 99 92 84 84 92 99 66 60 F0 F0 F0 F0 FE FF 7E 60 90 90 90 90 9E 81 7E 42 E7 FF FF FF FF FF 66 42 A5 99 81 81 99 99 66 66 FF FF FF FF FF FF 66 66 99 89 81 91 99 99 66 3C 7E FF FF FF FF 7E 3C 3C 42 99 99 99 99 42 3C 7C FE FF FE FC F0 F0 60 7C 82 99 82 9C 90 90 60 3C 7E FF FF FF FE 7F 3E 3C 42 99 99 95 92 41 3E 7C FE FF FE FC FE FF 66 7C 82 99 82 84 92 99 66 3E 7F FE 7E 3F 7F FE 7C 3E 41 9E 42 39 79 82 7C 7E FF 7E 3C 3C 3C 3C 18 7E 81 66 24 24 24 24 18 66 FF FF FF FF FF 7E 3C 66 99 99 99 99 99 42 3C 66 FF FF FF FF 7E 3C 18 66 99 99 99 99 42 24 18 66 FF FF FF FF FF E7 42 66 99 99 81 81 99 A5 42 66 FF 7E 3C 7E FF FF 66 66 99 42 24 42 99 99 66 66 FF FF 7E 3C 3C 3C 18 66 99 99 42 24 24 24 18 7E FF 7E 3C 78 FE FF 7E 7E 81 72 24 48 9E 81 7E 3C 7E 7C 78 78 7C 7E 3C 3C 42 4C 48 48 4C 42 3C 60 F0 78 3C 1E 0F 07 02 60 90 48 24 12 09 05 02 3C 7E 3E 1E 1E 3E 7E 3C 3C 42 32 12 12 32 42 3C 18 3C 7E FF 66 00 00 00 18 24 42 99 66 00 00 00 00 00 00 00 00 7E FF 7E 00 00 00 00 00 7E 81 7E #3:MAIN BG 00 00 00 00 00 00 00 21 00 21 00 21 00 21 00 00 00 21 00 21 00 21 00 00 00 00 00 00 00 00 00 21 00 21 00 21 00 21 00 21 00 00 00 21 00 21 00 00 00 00 00 21 00 21 00 21 00 21 00 21 00 21 00 21 00 00 00 00 00 00 00 21 00 21 00 00 00 21 00 00 00 21 00 21 00 21 00 00 00 00 00 00 00 00 00 21 00 21 00 21 00 00 00 21 00 00 00 21 00 00 00 21 00 00 00 00 00 21 00 21 00 21 00 21 00 21 00 21 00 21 00 00 00 00 00 00 00 21 00 21 00 21 00 21 00 21 00 21 00 21 00 00 00 21 00 00 00 21 00 21 00 21 00 00 00 21 00 21 00 00 00 21 00 21 00 21 00 21 00 00 00 00 00 21 00 00 00 21 00 00 00 21 00 00 00 21 00 00 00 00 00 00 00 00 00 00 00 00 00 21 00 21 00 21 00 00 00 00 00 21 00 00 00 21 00 00 00 00 00 21 00 21 00 00 00 00 00 00 00 00 00 00 00 00 00 21 00 21 00 21 00 00 00 21 00 21 00 00 00 00 00 00 00 21 00 21 00 21 00 21 00 21 00 00 00 21 00 21 00 21 00 00 00 21 00 21 00 21 00 00 00 21 00 21 00 21 00 00 00 21 00 21 00 21 00 21 00 00 00 21 00 21 00 21 00 00 00 00 00 00 00 00 00 00 00 00 00 21 00 21 00 21 00 21 00 00 00 21 00 21 00 21 00 21 00 21 00 21 00 21 00 00 00 00 00 21 00 21 00 21 00 00 00 21 00 21 00 21 00 21 00 00 00 00 00 21 00 21 00 21 00 00 00 00 00 21 00 21 00 21 00 00 00 00 00 21 00 21 00 21 00 00 00 00 00 21 00 21 00 21 00 21 00 00 00 00 00 21 00 21 00 21 00 21 00 00 00 21 00 21 00 21 00 21 00 21 00 00 00 21 00 21 00 21 00 21 00 21 00 21 00 21 00 21 00 21 00 21 00 21 00 21 00 21 00 21 00 21 62 01 63 01 64 01 00 21 00 21 00 21 00 00 00 21 00 21 00 21 00 21 00 21 00 00 62 01 63 01 64 01 00 21 00 21 00 21 00 21 00 21 00 01 00 01 00 01 62 01 63 01 64 01 00 01 62 01 00 01 64 01 63 01 72 01 73 01 74 01 00 01 00 01 62 01 63 01 64 01 00 21 00 21 00 01 00 01 63 01 72 01 73 01 74 01 63 01 00 21 00 21 62 01 63 01 64 01 63 01 63 01 72 01 73 01 74 01 63 01 63 01 63 01 72 01 73 01 73 01 73 01 73 01 74 01 63 01 72 01 73 01 74 01 63 01 63 01 63 01 72 01 73 01 73 01 73 01 73 01 73 01 74 01 63 01 72 01 73 01 74 01 60 01 60 01 60 01 60 01 60 01 60 01 60 01 60 01 71 01 60 01 73 01 73 01 73 01 60 01 60 01 60 01 71 01 60 01 60 01 60 01 60 01 60 01 71 01 60 01 60 01 60 01 60 01 60 01 60 01 60 01 60 01 60 01 70 01 60 01 60 01 71 01 70 01 71 01 70 01 60 01 71 01 60 01 70 01 60 01 70 01 71 01 70 01 60 01 60 01 71 01 60 01 60 01 60 01 71 01 60 01 60 01 71 01 60 01 60 01 71 01 60 01 60 01 71 01 60 01 60 01 60 01 60 01 71 01 60 01 60 01 60 01 60 01 60 01 60 01 60 01 60 01 60 01 60 01 60 01 70 01 61 01 60 01 60 01 71 01 70 01 60 01 60 01 60 01 70 01 60 01 60 01 60 01 60 01 60 01 60 01 60 01 70 01 71 01 60 01 60 01 60 01 70 01 60 01 61 01 70 01 71 01 60 01 60 01 70 01 60 01 60 01 60 01 70 01 60 01 71 01 60 01 60 01 60 01 61 01 60 01 60 01 70 01 60 01 71 01 70 01 71 01 70 01 71 01 60 01 60 01 70 01 60 01 60 01 60 01 60 01 70 01 60 01 60 01 60 01 61 01 60 01 70 01 60 01 61 01 60 01 60 01 60 01 70 01 60 01 71 01 60 01 60 01 60 01 61 01 60 01 70 01 60 01 60 01 60 01 61 01 70 01 60 01 60 01 61 01 60 01 70 01 60 01 60 01 60 01 70 01 60 01 60 01 60 01 71 01 70 01 71 01 60 01 71 01 70 01 71 01 70 01 60 01 70 01 71 01 70 01 60 01 60 01 60 01 61 01 60 01 70 01 71 01 40 00 41 00 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00 50 00 00 00 00 00 00 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 50 00 00 00 00 00 40 00 41 00 50 00 51 00 00 00 40 00 41 00 00 00 00 00 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00 51 00 00 00 00 00 40 00 41 00 00 00 00 00 00 00 40 00 41 00 00 00 00 00 00 00 00 00 00 00 50 00 42 00 00 00 51 00 00 00 50 00 51 00 00 00 50 00 00 00 42 00 41 00 00 00 00 00 40 00 41 00 00 00 00 00 51 00 00 00 50 00 51 00 00 00 00 00 00 00 50 00 51 00 00 00 00 00 40 00 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 51 00 00 00 51 00 00 00 50 00 51 00 50 00 00 00 50 00 51 00 00 00 51 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 51 00 50 00 51 00 00 00 51 00 00 00 00 00 40 00 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 42 00 00 00 41 00 00 00 00 00 00 00 51 00 40 00 41 00 00 00 00 00 52 01 00 00 00 00 00 00 00 00 00 00 00 00 50 00 51 00 50 00 00 00 40 00 50 00 00 00 00 00 40 00 41 00 41 00 00 00 00 00 00 00 00 00 51 00 00 00 00 00 50 00 00 00 00 00 50 00 51 00 00 00 00 00 00 00 40 00 41 00 00 00 00 00 00 00 00 00 00 00 50 00 00 00 00 00 50 00 51 00 00 00 00 00 50 00 51 00 00 00 00 00 40 00 50 00 00 00 50 00 00 00 51 00 00 00 00 00 50 00 00 00 00 00 00 00 51 00 00 00 50 00 51 00 00 00 00 00 00 00 00 00 00 00 50 00 41 00 00 00 00 00 00 00 40 00 41 00 00 00 00 00 00 00 00 00 50 00 51 00 00 00 00 00 00 00 00 00 00 00 51 00 00 00 00 00 51 00 00 00 50 00 00 00 00 00 00 00 40 00 41 00 00 00 50 00 00 00 50 00 42 00 00 00 51 00 62 01 00 01 64 01 00 00 52 01 51 00 00 00 51 00 50 00 51 00 00 00 40 00 41 00 00 00 00 00 42 00 00 00 00 00 00 00 00 00 00 00 51 00 00 00 50 00 51 00 00 00 51 00 00 00 00 00 00 00 62 01 62 01 00 01 00 01 00 01 00 00 51 00 00 00 00 00 00 00 00 00 00 00 00 00 50 00 51 00 00 00 51 00 00 00 50 00 00 00 00 00 00 00 51 00 00 00 51 00 00 00 00 00 00 00 40 00 62 01 62 01 62 01 00 01 00 01 00 01 00 01 50 00 00 00 00 00 00 00 62 22 00 02 64 22 51 00 00 00 51 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 41 00 00 00 00 00 00 00 00 00 00 00 00 00 50 00 00 01 00 01 00 01 00 01 00 01 41 00 00 00 00 00 00 01 00 01 00 01 00 02 00 02 64 02 00 01 00 01 00 01 00 01 00 01 00 01 00 01 00 01 00 01 00 01 00 01 00 01 00 01 00 01 00 01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 21 00 20 00 20 00 22 00 02 00 22 00 22 63 22 00 22 00 02 00 00 00 00 00 00 00 01 00 01 00 01 00 01 00 01 00 00 00 00 00 00 00 01 00 01 00 01 00 01 00 00 00 00 00 00 62 21 63 22 64 22 00 20 00 00 00 02 00 02 62 02 00 02 00 22 72 22 73 22 74 22 00 22 00 02 00 01 62 22 63 22 64 22 65 22 00 00 00 00 00 00 00 00 00 00 00 00 62 22 63 22 64 01 00 00 00 00 00 02 72 22 73 22 74 22 00 20 00 21 00 22 00 02 00 22 63 22 72 22 70 22 73 22 73 22 74 22 00 22 00 02 72 22 73 22 74 22 63 22 63 22 64 02 00 00 62 02 63 22 63 22 72 22 73 22 74 22 00 22 00 22 72 22 73 22 71 22 73 22 74 22 00 22 00 02 00 22 72 22 73 22 73 22 73 22 61 22 73 22 71 22 74 22 72 22 73 22 73 22 73 22 73 22 73 22 74 22 00 20 72 22 73 22 73 22 71 22 73 22 73 22 74 22 ================================================ FILE: platform/macOS/LowRes NX macOS/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "size" : "16x16", "idiom" : "mac", "filename" : "Mac App Icon 16.png", "scale" : "1x" }, { "size" : "16x16", "idiom" : "mac", "filename" : "Mac App Icon 16@2x.png", "scale" : "2x" }, { "size" : "32x32", "idiom" : "mac", "filename" : "Mac App Icon 32.png", "scale" : "1x" }, { "size" : "32x32", "idiom" : "mac", "filename" : "Mac App Icon 64.png", "scale" : "2x" }, { "size" : "128x128", "idiom" : "mac", "filename" : "Mac App Icon 128.png", "scale" : "1x" }, { "size" : "128x128", "idiom" : "mac", "filename" : "Mac App Icon 256-1.png", "scale" : "2x" }, { "size" : "256x256", "idiom" : "mac", "filename" : "Mac App Icon 256.png", "scale" : "1x" }, { "size" : "256x256", "idiom" : "mac", "filename" : "Mac App Icon 512-1.png", "scale" : "2x" }, { "size" : "512x512", "idiom" : "mac", "filename" : "Mac App Icon 512.png", "scale" : "1x" }, { "size" : "512x512", "idiom" : "mac", "filename" : "Mac App Icon 512@2x.png", "scale" : "2x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: platform/macOS/LowRes NX macOS/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: platform/macOS/LowRes NX macOS/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDocumentTypes CFBundleTypeExtensions nx CFBundleTypeName LowRes NX Program CFBundleTypeRole Editor LSTypeIsPackage 0 CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIconFile CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion $(CURRENT_PROJECT_VERSION) LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSHumanReadableCopyright Copyright © 2017-2021 Timo Kloss. This program is released under the zlib License. NSPrincipalClass NSApplication SDL_FILESYSTEM_BASE_DIR_TYPE parent ================================================ FILE: platform/macOS/LowRes NX macOS/LowRes_NX_macOS_SDL.entitlements ================================================ ================================================ FILE: platform/macOS/LowRes NX macOS.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 50; objects = { /* Begin PBXBuildFile section */ 921E0F152104BD7100F3C512 /* settings.c in Sources */ = {isa = PBXBuildFile; fileRef = 921E0F142104BD7100F3C512 /* settings.c */; }; 921F986620DAD4DF0052F233 /* boot_intro.c in Sources */ = {isa = PBXBuildFile; fileRef = 921F986520DAD4DF0052F233 /* boot_intro.c */; }; 9251FDE621259EDE003915D9 /* cmd_audio.c in Sources */ = {isa = PBXBuildFile; fileRef = 9251FDE521259EDE003915D9 /* cmd_audio.c */; }; 9258F207218C49C000CE81D6 /* utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 9258F206218C49C000CE81D6 /* utils.c */; }; 925D482D2121FF76003F1B0D /* audio_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 925D482C2121FF76003F1B0D /* audio_lib.c */; }; 925F6BEE25039D12005E1179 /* core_stats.c in Sources */ = {isa = PBXBuildFile; fileRef = 925F6BEC25039D11005E1179 /* core_stats.c */; }; 9284722B20F9F4FA00B82653 /* dev_menu.c in Sources */ = {isa = PBXBuildFile; fileRef = 9284722A20F9F4FA00B82653 /* dev_menu.c */; }; 9287EAC920D85E4E000BBEB1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9287EAC820D85E4E000BBEB1 /* Assets.xcassets */; }; 9287EB2D20D85EEB000BBEB1 /* cmd_variables.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EADC20D85EEB000BBEB1 /* cmd_variables.c */; }; 9287EB2E20D85EEB000BBEB1 /* cmd_data.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EADD20D85EEB000BBEB1 /* cmd_data.c */; }; 9287EB2F20D85EEB000BBEB1 /* rcstring.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EADF20D85EEB000BBEB1 /* rcstring.c */; }; 9287EB3020D85EEB000BBEB1 /* cmd_background.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAE120D85EEB000BBEB1 /* cmd_background.c */; }; 9287EB3120D85EEB000BBEB1 /* cmd_files.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAE220D85EEB000BBEB1 /* cmd_files.c */; }; 9287EB3220D85EEB000BBEB1 /* cmd_control.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAE420D85EEB000BBEB1 /* cmd_control.c */; }; 9287EB3320D85EEB000BBEB1 /* variables.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAE620D85EEB000BBEB1 /* variables.c */; }; 9287EB3420D85EEB000BBEB1 /* token.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAE920D85EEB000BBEB1 /* token.c */; }; 9287EB3520D85EEB000BBEB1 /* cmd_maths.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAEA20D85EEB000BBEB1 /* cmd_maths.c */; }; 9287EB3620D85EEB000BBEB1 /* charsets.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAEE20D85EEB000BBEB1 /* charsets.c */; }; 9287EB3720D85EEB000BBEB1 /* cmd_text.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAEF20D85EEB000BBEB1 /* cmd_text.c */; }; 9287EB3820D85EEB000BBEB1 /* interpreter_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAF020D85EEB000BBEB1 /* interpreter_utils.c */; }; 9287EB3920D85EEB000BBEB1 /* labels.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAF220D85EEB000BBEB1 /* labels.c */; }; 9287EB3A20D85EEB000BBEB1 /* error.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAF520D85EEB000BBEB1 /* error.c */; }; 9287EB3B20D85EEB000BBEB1 /* cmd_memory.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAF620D85EEB000BBEB1 /* cmd_memory.c */; }; 9287EB3C20D85EEB000BBEB1 /* cmd_strings.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAF720D85EEB000BBEB1 /* cmd_strings.c */; }; 9287EB3D20D85EEB000BBEB1 /* string_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAFC20D85EEB000BBEB1 /* string_utils.c */; }; 9287EB3E20D85EEB000BBEB1 /* cmd_subs.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAFD20D85EEB000BBEB1 /* cmd_subs.c */; }; 9287EB3F20D85EEB000BBEB1 /* cmd_io.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAFF20D85EEB000BBEB1 /* cmd_io.c */; }; 9287EB4020D85EEB000BBEB1 /* interpreter.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB0020D85EEB000BBEB1 /* interpreter.c */; }; 9287EB4120D85EEB000BBEB1 /* data.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB0220D85EEB000BBEB1 /* data.c */; }; 9287EB4220D85EEB000BBEB1 /* cmd_sprites.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB0420D85EEB000BBEB1 /* cmd_sprites.c */; }; 9287EB4320D85EEB000BBEB1 /* value.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB0720D85EEB000BBEB1 /* value.c */; }; 9287EB4420D85EEB000BBEB1 /* cmd_screen.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB0820D85EEB000BBEB1 /* cmd_screen.c */; }; 9287EB4520D85EEB000BBEB1 /* tokenizer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB0B20D85EEB000BBEB1 /* tokenizer.c */; }; 9287EB4620D85EEB000BBEB1 /* disk_drive.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB0F20D85EEB000BBEB1 /* disk_drive.c */; }; 9287EB4720D85EEB000BBEB1 /* data_manager.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB1220D85EEB000BBEB1 /* data_manager.c */; }; 9287EB4820D85EEB000BBEB1 /* sprites_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB1520D85EEB000BBEB1 /* sprites_lib.c */; }; 9287EB4920D85EEB000BBEB1 /* default_characters.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB1A20D85EEB000BBEB1 /* default_characters.c */; }; 9287EB4A20D85EEB000BBEB1 /* startup_sequence.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB1B20D85EEB000BBEB1 /* startup_sequence.c */; }; 9287EB4B20D85EEB000BBEB1 /* text_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB1C20D85EEB000BBEB1 /* text_lib.c */; }; 9287EB4C20D85EEB000BBEB1 /* core.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB1E20D85EEB000BBEB1 /* core.c */; }; 9287EB4D20D85EEB000BBEB1 /* audio_chip.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB2220D85EEB000BBEB1 /* audio_chip.c */; }; 9287EB4E20D85EEB000BBEB1 /* video_chip.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB2420D85EEB000BBEB1 /* video_chip.c */; }; 9287EB4F20D85EEB000BBEB1 /* machine.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB2520D85EEB000BBEB1 /* machine.c */; }; 9287EB5020D85EEB000BBEB1 /* overlay_data.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB2820D85EEB000BBEB1 /* overlay_data.c */; }; 9287EB5120D85EEB000BBEB1 /* overlay.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB2A20D85EEB000BBEB1 /* overlay.c */; }; 9287EB5220D85EEB000BBEB1 /* core_delegate.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB2C20D85EEB000BBEB1 /* core_delegate.c */; }; 9287EB5620D85EF7000BBEB1 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB5520D85EF7000BBEB1 /* main.c */; }; 9287EB5920D85F43000BBEB1 /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9287EB5820D85F43000BBEB1 /* SDL2.framework */; }; 9289C403212866FD009BE093 /* SDL2.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9287EB5820D85F43000BBEB1 /* SDL2.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 928CA2E62189DC370085125F /* runner.c in Sources */ = {isa = PBXBuildFile; fileRef = 928CA2E52189DC370085125F /* runner.c */; }; 92D708942181C0ED00F40043 /* screenshot.c in Sources */ = {isa = PBXBuildFile; fileRef = 92D708932181C0ED00F40043 /* screenshot.c */; }; 92D7089E2181E0A400F40043 /* system_paths.c in Sources */ = {isa = PBXBuildFile; fileRef = 92D7089D2181E0A400F40043 /* system_paths.c */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 9289C402212866E8009BE093 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 9289C403212866FD009BE093 /* SDL2.framework in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 921E0F132104BD7100F3C512 /* settings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = settings.h; sourceTree = ""; }; 921E0F142104BD7100F3C512 /* settings.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = settings.c; sourceTree = ""; }; 921F986420DAD4DF0052F233 /* boot_intro.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = boot_intro.h; sourceTree = ""; }; 921F986520DAD4DF0052F233 /* boot_intro.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = boot_intro.c; sourceTree = ""; }; 9251FDE421259EDE003915D9 /* cmd_audio.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cmd_audio.h; sourceTree = ""; }; 9251FDE521259EDE003915D9 /* cmd_audio.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cmd_audio.c; sourceTree = ""; }; 9258F205218C49C000CE81D6 /* utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = utils.h; sourceTree = ""; }; 9258F206218C49C000CE81D6 /* utils.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = utils.c; sourceTree = ""; }; 925D482B2121FF76003F1B0D /* audio_lib.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = audio_lib.h; sourceTree = ""; }; 925D482C2121FF76003F1B0D /* audio_lib.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = audio_lib.c; sourceTree = ""; }; 925F6BEC25039D11005E1179 /* core_stats.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = core_stats.c; sourceTree = ""; }; 925F6BED25039D12005E1179 /* core_stats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core_stats.h; sourceTree = ""; }; 92648F1625BAD73D008559AA /* libretro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libretro.h; sourceTree = ""; }; 92648F6425BAE851008559AA /* libretro_main.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = libretro_main.h; sourceTree = ""; }; 92648F6525BAE851008559AA /* libretro_main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = libretro_main.c; sourceTree = ""; }; 9284722920F9F4FA00B82653 /* dev_menu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = dev_menu.h; sourceTree = ""; }; 9284722A20F9F4FA00B82653 /* dev_menu.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = dev_menu.c; sourceTree = ""; }; 9284722C20F9FA5900B82653 /* dev_menu_data.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = dev_menu_data.h; sourceTree = ""; }; 9287EAC220D85E4D000BBEB1 /* LowRes NX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "LowRes NX.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 9287EAC820D85E4E000BBEB1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 9287EACD20D85E4E000BBEB1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9287EAD020D85E4E000BBEB1 /* LowRes_NX_macOS_SDL.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LowRes_NX_macOS_SDL.entitlements; sourceTree = ""; }; 9287EADA20D85EEB000BBEB1 /* cmd_memory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_memory.h; sourceTree = ""; }; 9287EADB20D85EEB000BBEB1 /* error.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = error.h; sourceTree = ""; }; 9287EADC20D85EEB000BBEB1 /* cmd_variables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_variables.c; sourceTree = ""; }; 9287EADD20D85EEB000BBEB1 /* cmd_data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_data.c; sourceTree = ""; }; 9287EADE20D85EEB000BBEB1 /* string_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_utils.h; sourceTree = ""; }; 9287EADF20D85EEB000BBEB1 /* rcstring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rcstring.c; sourceTree = ""; }; 9287EAE020D85EEB000BBEB1 /* cmd_subs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_subs.h; sourceTree = ""; }; 9287EAE120D85EEB000BBEB1 /* cmd_background.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_background.c; sourceTree = ""; }; 9287EAE220D85EEB000BBEB1 /* cmd_files.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_files.c; sourceTree = ""; }; 9287EAE320D85EEB000BBEB1 /* cmd_strings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_strings.h; sourceTree = ""; }; 9287EAE420D85EEB000BBEB1 /* cmd_control.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_control.c; sourceTree = ""; }; 9287EAE520D85EEB000BBEB1 /* data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data.h; sourceTree = ""; }; 9287EAE620D85EEB000BBEB1 /* variables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = variables.c; sourceTree = ""; }; 9287EAE720D85EEB000BBEB1 /* interpreter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interpreter.h; sourceTree = ""; }; 9287EAE820D85EEB000BBEB1 /* cmd_io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_io.h; sourceTree = ""; }; 9287EAE920D85EEB000BBEB1 /* token.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = token.c; sourceTree = ""; }; 9287EAEA20D85EEB000BBEB1 /* cmd_maths.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_maths.c; sourceTree = ""; }; 9287EAEB20D85EEB000BBEB1 /* tokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tokenizer.h; sourceTree = ""; }; 9287EAEC20D85EEB000BBEB1 /* cmd_screen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_screen.h; sourceTree = ""; }; 9287EAED20D85EEB000BBEB1 /* value.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = value.h; sourceTree = ""; }; 9287EAEE20D85EEB000BBEB1 /* charsets.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = charsets.c; sourceTree = ""; }; 9287EAEF20D85EEB000BBEB1 /* cmd_text.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_text.c; sourceTree = ""; }; 9287EAF020D85EEB000BBEB1 /* interpreter_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = interpreter_utils.c; sourceTree = ""; }; 9287EAF120D85EEB000BBEB1 /* cmd_sprites.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_sprites.h; sourceTree = ""; }; 9287EAF220D85EEB000BBEB1 /* labels.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = labels.c; sourceTree = ""; }; 9287EAF320D85EEB000BBEB1 /* cmd_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_data.h; sourceTree = ""; }; 9287EAF420D85EEB000BBEB1 /* cmd_variables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_variables.h; sourceTree = ""; }; 9287EAF520D85EEB000BBEB1 /* error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = error.c; sourceTree = ""; }; 9287EAF620D85EEB000BBEB1 /* cmd_memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_memory.c; sourceTree = ""; }; 9287EAF720D85EEB000BBEB1 /* cmd_strings.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_strings.c; sourceTree = ""; }; 9287EAF820D85EEB000BBEB1 /* cmd_control.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_control.h; sourceTree = ""; }; 9287EAF920D85EEB000BBEB1 /* cmd_files.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_files.h; sourceTree = ""; }; 9287EAFA20D85EEB000BBEB1 /* cmd_background.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_background.h; sourceTree = ""; }; 9287EAFB20D85EEB000BBEB1 /* rcstring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rcstring.h; sourceTree = ""; }; 9287EAFC20D85EEB000BBEB1 /* string_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = string_utils.c; sourceTree = ""; }; 9287EAFD20D85EEB000BBEB1 /* cmd_subs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_subs.c; sourceTree = ""; }; 9287EAFE20D85EEB000BBEB1 /* token.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = token.h; sourceTree = ""; }; 9287EAFF20D85EEB000BBEB1 /* cmd_io.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_io.c; sourceTree = ""; }; 9287EB0020D85EEB000BBEB1 /* interpreter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = interpreter.c; sourceTree = ""; }; 9287EB0120D85EEB000BBEB1 /* variables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = variables.h; sourceTree = ""; }; 9287EB0220D85EEB000BBEB1 /* data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = data.c; sourceTree = ""; }; 9287EB0320D85EEB000BBEB1 /* interpreter_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interpreter_config.h; sourceTree = ""; }; 9287EB0420D85EEB000BBEB1 /* cmd_sprites.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_sprites.c; sourceTree = ""; }; 9287EB0520D85EEB000BBEB1 /* labels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = labels.h; sourceTree = ""; }; 9287EB0620D85EEB000BBEB1 /* charsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = charsets.h; sourceTree = ""; }; 9287EB0720D85EEB000BBEB1 /* value.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = value.c; sourceTree = ""; }; 9287EB0820D85EEB000BBEB1 /* cmd_screen.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_screen.c; sourceTree = ""; }; 9287EB0920D85EEB000BBEB1 /* interpreter_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interpreter_utils.h; sourceTree = ""; }; 9287EB0A20D85EEB000BBEB1 /* cmd_text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_text.h; sourceTree = ""; }; 9287EB0B20D85EEB000BBEB1 /* tokenizer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tokenizer.c; sourceTree = ""; }; 9287EB0C20D85EEB000BBEB1 /* cmd_maths.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_maths.h; sourceTree = ""; }; 9287EB0D20D85EEB000BBEB1 /* core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core.h; sourceTree = ""; }; 9287EB0F20D85EEB000BBEB1 /* disk_drive.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = disk_drive.c; sourceTree = ""; }; 9287EB1020D85EEB000BBEB1 /* disk_drive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = disk_drive.h; sourceTree = ""; }; 9287EB1220D85EEB000BBEB1 /* data_manager.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = data_manager.c; sourceTree = ""; }; 9287EB1320D85EEB000BBEB1 /* data_manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data_manager.h; sourceTree = ""; }; 9287EB1520D85EEB000BBEB1 /* sprites_lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sprites_lib.c; sourceTree = ""; }; 9287EB1620D85EEB000BBEB1 /* default_characters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = default_characters.h; sourceTree = ""; }; 9287EB1720D85EEB000BBEB1 /* startup_sequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = startup_sequence.h; sourceTree = ""; }; 9287EB1820D85EEB000BBEB1 /* text_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = text_lib.h; sourceTree = ""; }; 9287EB1920D85EEB000BBEB1 /* sprites_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sprites_lib.h; sourceTree = ""; }; 9287EB1A20D85EEB000BBEB1 /* default_characters.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = default_characters.c; sourceTree = ""; }; 9287EB1B20D85EEB000BBEB1 /* startup_sequence.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = startup_sequence.c; sourceTree = ""; }; 9287EB1C20D85EEB000BBEB1 /* text_lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = text_lib.c; sourceTree = ""; }; 9287EB1D20D85EEB000BBEB1 /* core_delegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core_delegate.h; sourceTree = ""; }; 9287EB1E20D85EEB000BBEB1 /* core.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = core.c; sourceTree = ""; }; 9287EB2020D85EEB000BBEB1 /* video_chip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = video_chip.h; sourceTree = ""; }; 9287EB2120D85EEB000BBEB1 /* machine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = machine.h; sourceTree = ""; }; 9287EB2220D85EEB000BBEB1 /* audio_chip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = audio_chip.c; sourceTree = ""; }; 9287EB2320D85EEB000BBEB1 /* io_chip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = io_chip.h; sourceTree = ""; }; 9287EB2420D85EEB000BBEB1 /* video_chip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = video_chip.c; sourceTree = ""; }; 9287EB2520D85EEB000BBEB1 /* machine.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = machine.c; sourceTree = ""; }; 9287EB2620D85EEB000BBEB1 /* audio_chip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audio_chip.h; sourceTree = ""; }; 9287EB2820D85EEB000BBEB1 /* overlay_data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = overlay_data.c; sourceTree = ""; }; 9287EB2920D85EEB000BBEB1 /* overlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = overlay.h; sourceTree = ""; }; 9287EB2A20D85EEB000BBEB1 /* overlay.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = overlay.c; sourceTree = ""; }; 9287EB2B20D85EEB000BBEB1 /* overlay_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = overlay_data.h; sourceTree = ""; }; 9287EB2C20D85EEB000BBEB1 /* core_delegate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = core_delegate.c; sourceTree = ""; }; 9287EB5520D85EF7000BBEB1 /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; 9287EB5820D85F43000BBEB1 /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = ../../../../../../Library/Frameworks/SDL2.framework; sourceTree = ""; }; 928CA2E42189DC370085125F /* runner.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = runner.h; sourceTree = ""; }; 928CA2E52189DC370085125F /* runner.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = runner.c; sourceTree = ""; }; 928CA2E72189DCE90085125F /* main.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = main.h; sourceTree = ""; }; 92ADAC74218B475C0040A1F1 /* config.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = ""; }; 92AF302F219179930053EA80 /* sdl_include.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sdl_include.h; sourceTree = ""; }; 92AF303021917A9D0053EA80 /* stb_image_write.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = stb_image_write.h; sourceTree = ""; }; 92D708922181C0ED00F40043 /* screenshot.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = screenshot.h; sourceTree = ""; }; 92D708932181C0ED00F40043 /* screenshot.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = screenshot.c; sourceTree = ""; }; 92D708952181C39900F40043 /* libz.1.1.3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.1.1.3.tbd; path = usr/lib/libz.1.1.3.tbd; sourceTree = SDKROOT; }; 92D708972181C67000F40043 /* libpng16.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpng16.a; path = ../../../../../../usr/local/Cellar/libpng/1.6.21/lib/libpng16.a; sourceTree = ""; }; 92D708992181C80200F40043 /* libz.1.2.8.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.1.2.8.tbd; path = usr/lib/libz.1.2.8.tbd; sourceTree = SDKROOT; }; 92D7089C2181E0A400F40043 /* system_paths.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = system_paths.h; sourceTree = ""; }; 92D7089D2181E0A400F40043 /* system_paths.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = system_paths.c; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 9287EABF20D85E4D000BBEB1 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 9287EB5920D85F43000BBEB1 /* SDL2.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 92648F1225BAD356008559AA /* libretro */ = { isa = PBXGroup; children = ( 92648F1625BAD73D008559AA /* libretro.h */, 92648F6425BAE851008559AA /* libretro_main.h */, 92648F6525BAE851008559AA /* libretro_main.c */, ); name = libretro; path = ../../libretro; sourceTree = ""; }; 9287EAB920D85E4D000BBEB1 = { isa = PBXGroup; children = ( 9287EAD820D85EEB000BBEB1 /* core */, 9287EB5320D85EF7000BBEB1 /* sdl */, 92648F1225BAD356008559AA /* libretro */, 9287EAC420D85E4D000BBEB1 /* LowRes NX macOS */, 9287EAC320D85E4D000BBEB1 /* Products */, 9287EB5720D85F43000BBEB1 /* Frameworks */, ); sourceTree = ""; }; 9287EAC320D85E4D000BBEB1 /* Products */ = { isa = PBXGroup; children = ( 9287EAC220D85E4D000BBEB1 /* LowRes NX.app */, ); name = Products; sourceTree = ""; }; 9287EAC420D85E4D000BBEB1 /* LowRes NX macOS */ = { isa = PBXGroup; children = ( 9287EAC820D85E4E000BBEB1 /* Assets.xcassets */, 9287EACD20D85E4E000BBEB1 /* Info.plist */, 9287EAD020D85E4E000BBEB1 /* LowRes_NX_macOS_SDL.entitlements */, ); path = "LowRes NX macOS"; sourceTree = ""; }; 9287EAD820D85EEB000BBEB1 /* core */ = { isa = PBXGroup; children = ( 9287EB2C20D85EEB000BBEB1 /* core_delegate.c */, 9287EB1D20D85EEB000BBEB1 /* core_delegate.h */, 925F6BEC25039D11005E1179 /* core_stats.c */, 925F6BED25039D12005E1179 /* core_stats.h */, 9287EB1E20D85EEB000BBEB1 /* core.c */, 9287EB0D20D85EEB000BBEB1 /* core.h */, 921F986520DAD4DF0052F233 /* boot_intro.c */, 921F986420DAD4DF0052F233 /* boot_intro.h */, 9287EB0E20D85EEB000BBEB1 /* accessories */, 9287EB1120D85EEB000BBEB1 /* datamanager */, 9287EAD920D85EEB000BBEB1 /* interpreter */, 9287EB1420D85EEB000BBEB1 /* libraries */, 9287EB1F20D85EEB000BBEB1 /* machine */, 9287EB2720D85EEB000BBEB1 /* overlay */, ); name = core; path = ../../core; sourceTree = ""; }; 9287EAD920D85EEB000BBEB1 /* interpreter */ = { isa = PBXGroup; children = ( 9287EAEE20D85EEB000BBEB1 /* charsets.c */, 9287EB0620D85EEB000BBEB1 /* charsets.h */, 9251FDE521259EDE003915D9 /* cmd_audio.c */, 9251FDE421259EDE003915D9 /* cmd_audio.h */, 9287EAE120D85EEB000BBEB1 /* cmd_background.c */, 9287EAFA20D85EEB000BBEB1 /* cmd_background.h */, 9287EAE420D85EEB000BBEB1 /* cmd_control.c */, 9287EAF820D85EEB000BBEB1 /* cmd_control.h */, 9287EADD20D85EEB000BBEB1 /* cmd_data.c */, 9287EAF320D85EEB000BBEB1 /* cmd_data.h */, 9287EAE220D85EEB000BBEB1 /* cmd_files.c */, 9287EAF920D85EEB000BBEB1 /* cmd_files.h */, 9287EAFF20D85EEB000BBEB1 /* cmd_io.c */, 9287EAE820D85EEB000BBEB1 /* cmd_io.h */, 9287EAEA20D85EEB000BBEB1 /* cmd_maths.c */, 9287EB0C20D85EEB000BBEB1 /* cmd_maths.h */, 9287EAF620D85EEB000BBEB1 /* cmd_memory.c */, 9287EADA20D85EEB000BBEB1 /* cmd_memory.h */, 9287EB0820D85EEB000BBEB1 /* cmd_screen.c */, 9287EAEC20D85EEB000BBEB1 /* cmd_screen.h */, 9287EB0420D85EEB000BBEB1 /* cmd_sprites.c */, 9287EAF120D85EEB000BBEB1 /* cmd_sprites.h */, 9287EAF720D85EEB000BBEB1 /* cmd_strings.c */, 9287EAE320D85EEB000BBEB1 /* cmd_strings.h */, 9287EAFD20D85EEB000BBEB1 /* cmd_subs.c */, 9287EAE020D85EEB000BBEB1 /* cmd_subs.h */, 9287EAEF20D85EEB000BBEB1 /* cmd_text.c */, 9287EB0A20D85EEB000BBEB1 /* cmd_text.h */, 9287EADC20D85EEB000BBEB1 /* cmd_variables.c */, 9287EAF420D85EEB000BBEB1 /* cmd_variables.h */, 9287EB0220D85EEB000BBEB1 /* data.c */, 9287EAE520D85EEB000BBEB1 /* data.h */, 9287EAF520D85EEB000BBEB1 /* error.c */, 9287EADB20D85EEB000BBEB1 /* error.h */, 9287EB0320D85EEB000BBEB1 /* interpreter_config.h */, 9287EAF020D85EEB000BBEB1 /* interpreter_utils.c */, 9287EB0920D85EEB000BBEB1 /* interpreter_utils.h */, 9287EB0020D85EEB000BBEB1 /* interpreter.c */, 9287EAE720D85EEB000BBEB1 /* interpreter.h */, 9287EAF220D85EEB000BBEB1 /* labels.c */, 9287EB0520D85EEB000BBEB1 /* labels.h */, 9287EADF20D85EEB000BBEB1 /* rcstring.c */, 9287EAFB20D85EEB000BBEB1 /* rcstring.h */, 9287EAFC20D85EEB000BBEB1 /* string_utils.c */, 9287EADE20D85EEB000BBEB1 /* string_utils.h */, 9287EAE920D85EEB000BBEB1 /* token.c */, 9287EAFE20D85EEB000BBEB1 /* token.h */, 9287EB0B20D85EEB000BBEB1 /* tokenizer.c */, 9287EAEB20D85EEB000BBEB1 /* tokenizer.h */, 9287EB0720D85EEB000BBEB1 /* value.c */, 9287EAED20D85EEB000BBEB1 /* value.h */, 9287EAE620D85EEB000BBEB1 /* variables.c */, 9287EB0120D85EEB000BBEB1 /* variables.h */, ); path = interpreter; sourceTree = ""; }; 9287EB0E20D85EEB000BBEB1 /* accessories */ = { isa = PBXGroup; children = ( 9287EB0F20D85EEB000BBEB1 /* disk_drive.c */, 9287EB1020D85EEB000BBEB1 /* disk_drive.h */, ); path = accessories; sourceTree = ""; }; 9287EB1120D85EEB000BBEB1 /* datamanager */ = { isa = PBXGroup; children = ( 9287EB1220D85EEB000BBEB1 /* data_manager.c */, 9287EB1320D85EEB000BBEB1 /* data_manager.h */, ); path = datamanager; sourceTree = ""; }; 9287EB1420D85EEB000BBEB1 /* libraries */ = { isa = PBXGroup; children = ( 9287EB1A20D85EEB000BBEB1 /* default_characters.c */, 9287EB1620D85EEB000BBEB1 /* default_characters.h */, 9287EB1B20D85EEB000BBEB1 /* startup_sequence.c */, 9287EB1720D85EEB000BBEB1 /* startup_sequence.h */, 9287EB1520D85EEB000BBEB1 /* sprites_lib.c */, 9287EB1920D85EEB000BBEB1 /* sprites_lib.h */, 9287EB1C20D85EEB000BBEB1 /* text_lib.c */, 9287EB1820D85EEB000BBEB1 /* text_lib.h */, 925D482C2121FF76003F1B0D /* audio_lib.c */, 925D482B2121FF76003F1B0D /* audio_lib.h */, ); path = libraries; sourceTree = ""; }; 9287EB1F20D85EEB000BBEB1 /* machine */ = { isa = PBXGroup; children = ( 9287EB2220D85EEB000BBEB1 /* audio_chip.c */, 9287EB2620D85EEB000BBEB1 /* audio_chip.h */, 9287EB2320D85EEB000BBEB1 /* io_chip.h */, 9287EB2520D85EEB000BBEB1 /* machine.c */, 9287EB2120D85EEB000BBEB1 /* machine.h */, 9287EB2420D85EEB000BBEB1 /* video_chip.c */, 9287EB2020D85EEB000BBEB1 /* video_chip.h */, ); path = machine; sourceTree = ""; }; 9287EB2720D85EEB000BBEB1 /* overlay */ = { isa = PBXGroup; children = ( 9287EB2820D85EEB000BBEB1 /* overlay_data.c */, 9287EB2B20D85EEB000BBEB1 /* overlay_data.h */, 9287EB2A20D85EEB000BBEB1 /* overlay.c */, 9287EB2920D85EEB000BBEB1 /* overlay.h */, ); path = overlay; sourceTree = ""; }; 9287EB5320D85EF7000BBEB1 /* sdl */ = { isa = PBXGroup; children = ( 92ADAC74218B475C0040A1F1 /* config.h */, 92AF302F219179930053EA80 /* sdl_include.h */, 928CA2E72189DCE90085125F /* main.h */, 9287EB5520D85EF7000BBEB1 /* main.c */, 928CA2E42189DC370085125F /* runner.h */, 928CA2E52189DC370085125F /* runner.c */, 9284722920F9F4FA00B82653 /* dev_menu.h */, 9284722A20F9F4FA00B82653 /* dev_menu.c */, 9284722C20F9FA5900B82653 /* dev_menu_data.h */, 921E0F132104BD7100F3C512 /* settings.h */, 921E0F142104BD7100F3C512 /* settings.c */, 92D708922181C0ED00F40043 /* screenshot.h */, 92D708932181C0ED00F40043 /* screenshot.c */, 92AF303021917A9D0053EA80 /* stb_image_write.h */, 92D7089C2181E0A400F40043 /* system_paths.h */, 92D7089D2181E0A400F40043 /* system_paths.c */, 9258F205218C49C000CE81D6 /* utils.h */, 9258F206218C49C000CE81D6 /* utils.c */, ); name = sdl; path = ../../sdl; sourceTree = ""; }; 9287EB5720D85F43000BBEB1 /* Frameworks */ = { isa = PBXGroup; children = ( 92D708992181C80200F40043 /* libz.1.2.8.tbd */, 92D708972181C67000F40043 /* libpng16.a */, 92D708952181C39900F40043 /* libz.1.1.3.tbd */, 9287EB5820D85F43000BBEB1 /* SDL2.framework */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 9287EAC120D85E4D000BBEB1 /* LowRes NX */ = { isa = PBXNativeTarget; buildConfigurationList = 9287EAD320D85E4E000BBEB1 /* Build configuration list for PBXNativeTarget "LowRes NX" */; buildPhases = ( 9287EABE20D85E4D000BBEB1 /* Sources */, 9287EABF20D85E4D000BBEB1 /* Frameworks */, 9287EAC020D85E4D000BBEB1 /* Resources */, 9289C402212866E8009BE093 /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = "LowRes NX"; productName = "LowRes NX macOS SDL"; productReference = 9287EAC220D85E4D000BBEB1 /* LowRes NX.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 9287EABA20D85E4D000BBEB1 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 1130; ORGANIZATIONNAME = "Inutilis Software"; TargetAttributes = { 9287EAC120D85E4D000BBEB1 = { CreatedOnToolsVersion = 9.3; SystemCapabilities = { com.apple.Sandbox = { enabled = 0; }; }; }; }; }; buildConfigurationList = 9287EABD20D85E4D000BBEB1 /* Build configuration list for PBXProject "LowRes NX macOS" */; compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 9287EAB920D85E4D000BBEB1; productRefGroup = 9287EAC320D85E4D000BBEB1 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 9287EAC120D85E4D000BBEB1 /* LowRes NX */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 9287EAC020D85E4D000BBEB1 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 9287EAC920D85E4E000BBEB1 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 9287EABE20D85E4D000BBEB1 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 9287EB3420D85EEB000BBEB1 /* token.c in Sources */, 9287EB3220D85EEB000BBEB1 /* cmd_control.c in Sources */, 9287EB2D20D85EEB000BBEB1 /* cmd_variables.c in Sources */, 928CA2E62189DC370085125F /* runner.c in Sources */, 9287EB4020D85EEB000BBEB1 /* interpreter.c in Sources */, 9287EB5020D85EEB000BBEB1 /* overlay_data.c in Sources */, 9287EB4220D85EEB000BBEB1 /* cmd_sprites.c in Sources */, 9287EB4120D85EEB000BBEB1 /* data.c in Sources */, 9287EB2E20D85EEB000BBEB1 /* cmd_data.c in Sources */, 9258F207218C49C000CE81D6 /* utils.c in Sources */, 9287EB3E20D85EEB000BBEB1 /* cmd_subs.c in Sources */, 9284722B20F9F4FA00B82653 /* dev_menu.c in Sources */, 9287EB4520D85EEB000BBEB1 /* tokenizer.c in Sources */, 9287EB4D20D85EEB000BBEB1 /* audio_chip.c in Sources */, 9287EB5220D85EEB000BBEB1 /* core_delegate.c in Sources */, 9287EB4A20D85EEB000BBEB1 /* startup_sequence.c in Sources */, 921E0F152104BD7100F3C512 /* settings.c in Sources */, 9287EB4420D85EEB000BBEB1 /* cmd_screen.c in Sources */, 9287EB4720D85EEB000BBEB1 /* data_manager.c in Sources */, 92D708942181C0ED00F40043 /* screenshot.c in Sources */, 9287EB2F20D85EEB000BBEB1 /* rcstring.c in Sources */, 9287EB5120D85EEB000BBEB1 /* overlay.c in Sources */, 921F986620DAD4DF0052F233 /* boot_intro.c in Sources */, 92D7089E2181E0A400F40043 /* system_paths.c in Sources */, 9287EB3620D85EEB000BBEB1 /* charsets.c in Sources */, 9251FDE621259EDE003915D9 /* cmd_audio.c in Sources */, 9287EB3F20D85EEB000BBEB1 /* cmd_io.c in Sources */, 9287EB3520D85EEB000BBEB1 /* cmd_maths.c in Sources */, 9287EB4C20D85EEB000BBEB1 /* core.c in Sources */, 9287EB3320D85EEB000BBEB1 /* variables.c in Sources */, 9287EB3C20D85EEB000BBEB1 /* cmd_strings.c in Sources */, 9287EB3B20D85EEB000BBEB1 /* cmd_memory.c in Sources */, 9287EB4920D85EEB000BBEB1 /* default_characters.c in Sources */, 9287EB5620D85EF7000BBEB1 /* main.c in Sources */, 9287EB4E20D85EEB000BBEB1 /* video_chip.c in Sources */, 9287EB3A20D85EEB000BBEB1 /* error.c in Sources */, 9287EB4B20D85EEB000BBEB1 /* text_lib.c in Sources */, 9287EB4620D85EEB000BBEB1 /* disk_drive.c in Sources */, 9287EB4F20D85EEB000BBEB1 /* machine.c in Sources */, 9287EB3120D85EEB000BBEB1 /* cmd_files.c in Sources */, 9287EB3920D85EEB000BBEB1 /* labels.c in Sources */, 9287EB3D20D85EEB000BBEB1 /* string_utils.c in Sources */, 9287EB3820D85EEB000BBEB1 /* interpreter_utils.c in Sources */, 9287EB4320D85EEB000BBEB1 /* value.c in Sources */, 9287EB4820D85EEB000BBEB1 /* sprites_lib.c in Sources */, 925D482D2121FF76003F1B0D /* audio_lib.c in Sources */, 925F6BEE25039D12005E1179 /* core_stats.c in Sources */, 9287EB3020D85EEB000BBEB1 /* cmd_background.c in Sources */, 9287EB3720D85EEB000BBEB1 /* cmd_text.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 9287EAD120D85E4E000BBEB1 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "Mac Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.6; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; name = Debug; }; 9287EAD220D85E4E000BBEB1 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "Mac Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.6; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; }; name = Release; }; 9287EAD420D85E4E000BBEB1 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 24; DEVELOPMENT_TEAM = BZ4VC623NH; ENABLE_HARDENED_RUNTIME = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(LOCAL_LIBRARY_DIR)/Frameworks", ); HEADER_SEARCH_PATHS = /usr/local/include; INFOPLIST_FILE = "LowRes NX macOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", /usr/local/lib, /usr/local/Cellar/libpng/1.6.21/lib, ); MARKETING_VERSION = 1.2; PRODUCT_BUNDLE_IDENTIFIER = "com.inutilis.LowRes-NX-macOS"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; }; name = Debug; }; 9287EAD520D85E4E000BBEB1 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 24; DEVELOPMENT_TEAM = BZ4VC623NH; ENABLE_HARDENED_RUNTIME = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(LOCAL_LIBRARY_DIR)/Frameworks", ); HEADER_SEARCH_PATHS = /usr/local/include; INFOPLIST_FILE = "LowRes NX macOS/Info.plist"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", /usr/local/lib, /usr/local/Cellar/libpng/1.6.21/lib, ); MARKETING_VERSION = 1.2; PRODUCT_BUNDLE_IDENTIFIER = "com.inutilis.LowRes-NX-macOS"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 9287EABD20D85E4D000BBEB1 /* Build configuration list for PBXProject "LowRes NX macOS" */ = { isa = XCConfigurationList; buildConfigurations = ( 9287EAD120D85E4E000BBEB1 /* Debug */, 9287EAD220D85E4E000BBEB1 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 9287EAD320D85E4E000BBEB1 /* Build configuration list for PBXNativeTarget "LowRes NX" */ = { isa = XCConfigurationList; buildConfigurations = ( 9287EAD420D85E4E000BBEB1 /* Debug */, 9287EAD520D85E4E000BBEB1 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 9287EABA20D85E4D000BBEB1 /* Project object */; } ================================================ FILE: platform/macOS/LowRes NX macOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: platform/macOS/LowRes NX macOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: platform/web/README.md ================================================ These lines are interesting, but still not a real guide for compiling: source ./emsdk_env.sh emmake make Always clean before build, otherwise changed files will be ignored! emmake make clean ================================================ FILE: platform/web/embed/package/LowResNX120.js ================================================ // The Module object: Our interface to the outside world. We import // and export values on it. There are various ways Module can be used: // 1. Not defined. We create it here // 2. A function parameter, function(Module) { ..generated code.. } // 3. pre-run appended it, var Module = {}; ..generated code.. // 4. External script tag defines var Module. // We need to check if Module already exists (e.g. case 3 above). // Substitution will be replaced with actual code on later stage of the build, // this way Closure Compiler will not mangle it (e.g. case 4. above). // Note that if you want to run closure, and also to use Module // after the generated code, you will need to define var Module = {}; // before the code. Then that object will be used in the code, and you // can continue to use Module afterwards as well. var Module = typeof Module !== 'undefined' ? Module : {}; // --pre-jses are emitted after the Module integration code, so that they can // refer to Module (if they choose; they can also define Module) // {{PRE_JSES}} // Sometimes an existing Module object exists with properties // meant to overwrite the default module functionality. Here // we collect those properties and reapply _after_ we configure // the current environment's defaults to avoid having to be so // defensive during initialization. var moduleOverrides = {}; var key; for (key in Module) { if (Module.hasOwnProperty(key)) { moduleOverrides[key] = Module[key]; } } var arguments_ = []; var thisProgram = './this.program'; var quit_ = function(status, toThrow) { throw toThrow; }; // Determine the runtime environment we are in. You can customize this by // setting the ENVIRONMENT setting at compile time (see settings.js). var ENVIRONMENT_IS_WEB = false; var ENVIRONMENT_IS_WORKER = false; var ENVIRONMENT_IS_NODE = false; var ENVIRONMENT_IS_SHELL = false; ENVIRONMENT_IS_WEB = typeof window === 'object'; ENVIRONMENT_IS_WORKER = typeof importScripts === 'function'; // N.b. Electron.js environment is simultaneously a NODE-environment, but // also a web environment. ENVIRONMENT_IS_NODE = typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node === 'string'; ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; if (Module['ENVIRONMENT']) { throw new Error('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -s ENVIRONMENT=web or -s ENVIRONMENT=node)'); } // `/` should be present at the end if `scriptDirectory` is not empty var scriptDirectory = ''; function locateFile(path) { if (Module['locateFile']) { return Module['locateFile'](path, scriptDirectory); } return scriptDirectory + path; } // Hooks that are implemented differently in different runtime environments. var read_, readAsync, readBinary, setWindowTitle; var nodeFS; var nodePath; if (ENVIRONMENT_IS_NODE) { if (ENVIRONMENT_IS_WORKER) { scriptDirectory = require('path').dirname(scriptDirectory) + '/'; } else { scriptDirectory = __dirname + '/'; } // include: node_shell_read.js read_ = function shell_read(filename, binary) { if (!nodeFS) nodeFS = require('fs'); if (!nodePath) nodePath = require('path'); filename = nodePath['normalize'](filename); return nodeFS['readFileSync'](filename, binary ? null : 'utf8'); }; readBinary = function readBinary(filename) { var ret = read_(filename, true); if (!ret.buffer) { ret = new Uint8Array(ret); } assert(ret.buffer); return ret; }; // end include: node_shell_read.js if (process['argv'].length > 1) { thisProgram = process['argv'][1].replace(/\\/g, '/'); } arguments_ = process['argv'].slice(2); if (typeof module !== 'undefined') { module['exports'] = Module; } process['on']('uncaughtException', function(ex) { // suppress ExitStatus exceptions from showing an error if (!(ex instanceof ExitStatus)) { throw ex; } }); process['on']('unhandledRejection', abort); quit_ = function(status) { process['exit'](status); }; Module['inspect'] = function () { return '[Emscripten Module object]'; }; } else if (ENVIRONMENT_IS_SHELL) { if (typeof read != 'undefined') { read_ = function shell_read(f) { return read(f); }; } readBinary = function readBinary(f) { var data; if (typeof readbuffer === 'function') { return new Uint8Array(readbuffer(f)); } data = read(f, 'binary'); assert(typeof data === 'object'); return data; }; if (typeof scriptArgs != 'undefined') { arguments_ = scriptArgs; } else if (typeof arguments != 'undefined') { arguments_ = arguments; } if (typeof quit === 'function') { quit_ = function(status) { quit(status); }; } if (typeof print !== 'undefined') { // Prefer to use print/printErr where they exist, as they usually work better. if (typeof console === 'undefined') console = /** @type{!Console} */({}); console.log = /** @type{!function(this:Console, ...*): undefined} */ (print); console.warn = console.error = /** @type{!function(this:Console, ...*): undefined} */ (typeof printErr !== 'undefined' ? printErr : print); } } else // Note that this includes Node.js workers when relevant (pthreads is enabled). // Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and // ENVIRONMENT_IS_NODE. if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) { // Check worker, not web, since window could be polyfilled scriptDirectory = self.location.href; } else if (typeof document !== 'undefined' && document.currentScript) { // web scriptDirectory = document.currentScript.src; } // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them. // otherwise, slice off the final part of the url to find the script directory. // if scriptDirectory does not contain a slash, lastIndexOf will return -1, // and scriptDirectory will correctly be replaced with an empty string. if (scriptDirectory.indexOf('blob:') !== 0) { scriptDirectory = scriptDirectory.substr(0, scriptDirectory.lastIndexOf('/')+1); } else { scriptDirectory = ''; } // Differentiate the Web Worker from the Node Worker case, as reading must // be done differently. { // include: web_or_worker_shell_read.js read_ = function(url) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); xhr.send(null); return xhr.responseText; }; if (ENVIRONMENT_IS_WORKER) { readBinary = function(url) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); xhr.responseType = 'arraybuffer'; xhr.send(null); return new Uint8Array(/** @type{!ArrayBuffer} */(xhr.response)); }; } readAsync = function(url, onload, onerror) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'arraybuffer'; xhr.onload = function() { if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0 onload(xhr.response); return; } onerror(); }; xhr.onerror = onerror; xhr.send(null); }; // end include: web_or_worker_shell_read.js } setWindowTitle = function(title) { document.title = title }; } else { throw new Error('environment detection error'); } // Set up the out() and err() hooks, which are how we can print to stdout or // stderr, respectively. var out = Module['print'] || console.log.bind(console); var err = Module['printErr'] || console.warn.bind(console); // Merge back in the overrides for (key in moduleOverrides) { if (moduleOverrides.hasOwnProperty(key)) { Module[key] = moduleOverrides[key]; } } // Free the object hierarchy contained in the overrides, this lets the GC // reclaim data used e.g. in memoryInitializerRequest, which is a large typed array. moduleOverrides = null; // Emit code to handle expected values on the Module object. This applies Module.x // to the proper local x. This has two benefits: first, we only emit it if it is // expected to arrive, and second, by using a local everywhere else that can be // minified. if (Module['arguments']) arguments_ = Module['arguments']; if (!Object.getOwnPropertyDescriptor(Module, 'arguments')) { Object.defineProperty(Module, 'arguments', { configurable: true, get: function() { abort('Module.arguments has been replaced with plain arguments_ (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); } if (Module['thisProgram']) thisProgram = Module['thisProgram']; if (!Object.getOwnPropertyDescriptor(Module, 'thisProgram')) { Object.defineProperty(Module, 'thisProgram', { configurable: true, get: function() { abort('Module.thisProgram has been replaced with plain thisProgram (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); } if (Module['quit']) quit_ = Module['quit']; if (!Object.getOwnPropertyDescriptor(Module, 'quit')) { Object.defineProperty(Module, 'quit', { configurable: true, get: function() { abort('Module.quit has been replaced with plain quit_ (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); } // perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message // Assertions on removed incoming Module JS APIs. assert(typeof Module['memoryInitializerPrefixURL'] === 'undefined', 'Module.memoryInitializerPrefixURL option was removed, use Module.locateFile instead'); assert(typeof Module['pthreadMainPrefixURL'] === 'undefined', 'Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead'); assert(typeof Module['cdInitializerPrefixURL'] === 'undefined', 'Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead'); assert(typeof Module['filePackagePrefixURL'] === 'undefined', 'Module.filePackagePrefixURL option was removed, use Module.locateFile instead'); assert(typeof Module['read'] === 'undefined', 'Module.read option was removed (modify read_ in JS)'); assert(typeof Module['readAsync'] === 'undefined', 'Module.readAsync option was removed (modify readAsync in JS)'); assert(typeof Module['readBinary'] === 'undefined', 'Module.readBinary option was removed (modify readBinary in JS)'); assert(typeof Module['setWindowTitle'] === 'undefined', 'Module.setWindowTitle option was removed (modify setWindowTitle in JS)'); assert(typeof Module['TOTAL_MEMORY'] === 'undefined', 'Module.TOTAL_MEMORY has been renamed Module.INITIAL_MEMORY'); if (!Object.getOwnPropertyDescriptor(Module, 'read')) { Object.defineProperty(Module, 'read', { configurable: true, get: function() { abort('Module.read has been replaced with plain read_ (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); } if (!Object.getOwnPropertyDescriptor(Module, 'readAsync')) { Object.defineProperty(Module, 'readAsync', { configurable: true, get: function() { abort('Module.readAsync has been replaced with plain readAsync (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); } if (!Object.getOwnPropertyDescriptor(Module, 'readBinary')) { Object.defineProperty(Module, 'readBinary', { configurable: true, get: function() { abort('Module.readBinary has been replaced with plain readBinary (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); } if (!Object.getOwnPropertyDescriptor(Module, 'setWindowTitle')) { Object.defineProperty(Module, 'setWindowTitle', { configurable: true, get: function() { abort('Module.setWindowTitle has been replaced with plain setWindowTitle (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); } var IDBFS = 'IDBFS is no longer included by default; build with -lidbfs.js'; var PROXYFS = 'PROXYFS is no longer included by default; build with -lproxyfs.js'; var WORKERFS = 'WORKERFS is no longer included by default; build with -lworkerfs.js'; var NODEFS = 'NODEFS is no longer included by default; build with -lnodefs.js'; var STACK_ALIGN = 16; function alignMemory(size, factor) { if (!factor) factor = STACK_ALIGN; // stack alignment (16-byte) by default return Math.ceil(size / factor) * factor; } function getNativeTypeSize(type) { switch (type) { case 'i1': case 'i8': return 1; case 'i16': return 2; case 'i32': return 4; case 'i64': return 8; case 'float': return 4; case 'double': return 8; default: { if (type[type.length-1] === '*') { return 4; // A pointer } else if (type[0] === 'i') { var bits = Number(type.substr(1)); assert(bits % 8 === 0, 'getNativeTypeSize invalid bits ' + bits + ', type ' + type); return bits / 8; } else { return 0; } } } } function warnOnce(text) { if (!warnOnce.shown) warnOnce.shown = {}; if (!warnOnce.shown[text]) { warnOnce.shown[text] = 1; err(text); } } // include: runtime_functions.js // Wraps a JS function as a wasm function with a given signature. function convertJsFunctionToWasm(func, sig) { // If the type reflection proposal is available, use the new // "WebAssembly.Function" constructor. // Otherwise, construct a minimal wasm module importing the JS function and // re-exporting it. if (typeof WebAssembly.Function === "function") { var typeNames = { 'i': 'i32', 'j': 'i64', 'f': 'f32', 'd': 'f64' }; var type = { parameters: [], results: sig[0] == 'v' ? [] : [typeNames[sig[0]]] }; for (var i = 1; i < sig.length; ++i) { type.parameters.push(typeNames[sig[i]]); } return new WebAssembly.Function(type, func); } // The module is static, with the exception of the type section, which is // generated based on the signature passed in. var typeSection = [ 0x01, // id: section, 0x00, // length: 0 (placeholder) 0x01, // count: 1 0x60, // form: func ]; var sigRet = sig.slice(0, 1); var sigParam = sig.slice(1); var typeCodes = { 'i': 0x7f, // i32 'j': 0x7e, // i64 'f': 0x7d, // f32 'd': 0x7c, // f64 }; // Parameters, length + signatures typeSection.push(sigParam.length); for (var i = 0; i < sigParam.length; ++i) { typeSection.push(typeCodes[sigParam[i]]); } // Return values, length + signatures // With no multi-return in MVP, either 0 (void) or 1 (anything else) if (sigRet == 'v') { typeSection.push(0x00); } else { typeSection = typeSection.concat([0x01, typeCodes[sigRet]]); } // Write the overall length of the type section back into the section header // (excepting the 2 bytes for the section id and length) typeSection[1] = typeSection.length - 2; // Rest of the module is static var bytes = new Uint8Array([ 0x00, 0x61, 0x73, 0x6d, // magic ("\0asm") 0x01, 0x00, 0x00, 0x00, // version: 1 ].concat(typeSection, [ 0x02, 0x07, // import section // (import "e" "f" (func 0 (type 0))) 0x01, 0x01, 0x65, 0x01, 0x66, 0x00, 0x00, 0x07, 0x05, // export section // (export "f" (func 0 (type 0))) 0x01, 0x01, 0x66, 0x00, 0x00, ])); // We can compile this wasm module synchronously because it is very small. // This accepts an import (at "e.f"), that it reroutes to an export (at "f") var module = new WebAssembly.Module(bytes); var instance = new WebAssembly.Instance(module, { 'e': { 'f': func } }); var wrappedFunc = instance.exports['f']; return wrappedFunc; } var freeTableIndexes = []; // Weak map of functions in the table to their indexes, created on first use. var functionsInTableMap; function getEmptyTableSlot() { // Reuse a free index if there is one, otherwise grow. if (freeTableIndexes.length) { return freeTableIndexes.pop(); } // Grow the table try { wasmTable.grow(1); } catch (err) { if (!(err instanceof RangeError)) { throw err; } throw 'Unable to grow wasm table. Set ALLOW_TABLE_GROWTH.'; } return wasmTable.length - 1; } // Add a wasm function to the table. function addFunctionWasm(func, sig) { // Check if the function is already in the table, to ensure each function // gets a unique index. First, create the map if this is the first use. if (!functionsInTableMap) { functionsInTableMap = new WeakMap(); for (var i = 0; i < wasmTable.length; i++) { var item = wasmTable.get(i); // Ignore null values. if (item) { functionsInTableMap.set(item, i); } } } if (functionsInTableMap.has(func)) { return functionsInTableMap.get(func); } // It's not in the table, add it now. var ret = getEmptyTableSlot(); // Set the new value. try { // Attempting to call this with JS function will cause of table.set() to fail wasmTable.set(ret, func); } catch (err) { if (!(err instanceof TypeError)) { throw err; } assert(typeof sig !== 'undefined', 'Missing signature argument to addFunction: ' + func); var wrapped = convertJsFunctionToWasm(func, sig); wasmTable.set(ret, wrapped); } functionsInTableMap.set(func, ret); return ret; } function removeFunction(index) { functionsInTableMap.delete(wasmTable.get(index)); freeTableIndexes.push(index); } // 'sig' parameter is required for the llvm backend but only when func is not // already a WebAssembly function. function addFunction(func, sig) { assert(typeof func !== 'undefined'); return addFunctionWasm(func, sig); } // end include: runtime_functions.js // include: runtime_debug.js // end include: runtime_debug.js function makeBigInt(low, high, unsigned) { return unsigned ? ((+((low>>>0)))+((+((high>>>0)))*4294967296.0)) : ((+((low>>>0)))+((+((high|0)))*4294967296.0)); } var tempRet0 = 0; var setTempRet0 = function(value) { tempRet0 = value; }; var getTempRet0 = function() { return tempRet0; }; function getCompilerSetting(name) { throw 'You must build with -s RETAIN_COMPILER_SETTINGS=1 for getCompilerSetting or emscripten_get_compiler_setting to work'; } // === Preamble library stuff === // Documentation for the public APIs defined in this file must be updated in: // site/source/docs/api_reference/preamble.js.rst // A prebuilt local version of the documentation is available at: // site/build/text/docs/api_reference/preamble.js.txt // You can also build docs locally as HTML or other formats in site/ // An online HTML version (which may be of a different version of Emscripten) // is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html var wasmBinary; if (Module['wasmBinary']) wasmBinary = Module['wasmBinary']; if (!Object.getOwnPropertyDescriptor(Module, 'wasmBinary')) { Object.defineProperty(Module, 'wasmBinary', { configurable: true, get: function() { abort('Module.wasmBinary has been replaced with plain wasmBinary (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); } var noExitRuntime = Module['noExitRuntime'] || true; if (!Object.getOwnPropertyDescriptor(Module, 'noExitRuntime')) { Object.defineProperty(Module, 'noExitRuntime', { configurable: true, get: function() { abort('Module.noExitRuntime has been replaced with plain noExitRuntime (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); } if (typeof WebAssembly !== 'object') { abort('no native wasm support detected'); } // include: runtime_safe_heap.js // In MINIMAL_RUNTIME, setValue() and getValue() are only available when building with safe heap enabled, for heap safety checking. // In traditional runtime, setValue() and getValue() are always available (although their use is highly discouraged due to perf penalties) /** @param {number} ptr @param {number} value @param {string} type @param {number|boolean=} noSafe */ function setValue(ptr, value, type, noSafe) { type = type || 'i8'; if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit switch(type) { case 'i1': HEAP8[((ptr)>>0)] = value; break; case 'i8': HEAP8[((ptr)>>0)] = value; break; case 'i16': HEAP16[((ptr)>>1)] = value; break; case 'i32': HEAP32[((ptr)>>2)] = value; break; case 'i64': (tempI64 = [value>>>0,(tempDouble=value,(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math.min((+(Math.floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[((ptr)>>2)] = tempI64[0],HEAP32[(((ptr)+(4))>>2)] = tempI64[1]); break; case 'float': HEAPF32[((ptr)>>2)] = value; break; case 'double': HEAPF64[((ptr)>>3)] = value; break; default: abort('invalid type for setValue: ' + type); } } /** @param {number} ptr @param {string} type @param {number|boolean=} noSafe */ function getValue(ptr, type, noSafe) { type = type || 'i8'; if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit switch(type) { case 'i1': return HEAP8[((ptr)>>0)]; case 'i8': return HEAP8[((ptr)>>0)]; case 'i16': return HEAP16[((ptr)>>1)]; case 'i32': return HEAP32[((ptr)>>2)]; case 'i64': return HEAP32[((ptr)>>2)]; case 'float': return HEAPF32[((ptr)>>2)]; case 'double': return HEAPF64[((ptr)>>3)]; default: abort('invalid type for getValue: ' + type); } return null; } // end include: runtime_safe_heap.js // Wasm globals var wasmMemory; //======================================== // Runtime essentials //======================================== // whether we are quitting the application. no code should run after this. // set in exit() and abort() var ABORT = false; // set by exit() and abort(). Passed to 'onExit' handler. // NOTE: This is also used as the process return code code in shell environments // but only when noExitRuntime is false. var EXITSTATUS; /** @type {function(*, string=)} */ function assert(condition, text) { if (!condition) { abort('Assertion failed: ' + text); } } // Returns the C function with a specified identifier (for C++, you need to do manual name mangling) function getCFunc(ident) { var func = Module['_' + ident]; // closure exported function assert(func, 'Cannot call unknown function ' + ident + ', make sure it is exported'); return func; } // C calling interface. /** @param {string|null=} returnType @param {Array=} argTypes @param {Arguments|Array=} args @param {Object=} opts */ function ccall(ident, returnType, argTypes, args, opts) { // For fast lookup of conversion functions var toC = { 'string': function(str) { var ret = 0; if (str !== null && str !== undefined && str !== 0) { // null string // at most 4 bytes per UTF-8 code point, +1 for the trailing '\0' var len = (str.length << 2) + 1; ret = stackAlloc(len); stringToUTF8(str, ret, len); } return ret; }, 'array': function(arr) { var ret = stackAlloc(arr.length); writeArrayToMemory(arr, ret); return ret; } }; function convertReturnValue(ret) { if (returnType === 'string') return UTF8ToString(ret); if (returnType === 'boolean') return Boolean(ret); return ret; } var func = getCFunc(ident); var cArgs = []; var stack = 0; assert(returnType !== 'array', 'Return type should not be "array".'); if (args) { for (var i = 0; i < args.length; i++) { var converter = toC[argTypes[i]]; if (converter) { if (stack === 0) stack = stackSave(); cArgs[i] = converter(args[i]); } else { cArgs[i] = args[i]; } } } var ret = func.apply(null, cArgs); ret = convertReturnValue(ret); if (stack !== 0) stackRestore(stack); return ret; } /** @param {string=} returnType @param {Array=} argTypes @param {Object=} opts */ function cwrap(ident, returnType, argTypes, opts) { return function() { return ccall(ident, returnType, argTypes, arguments, opts); } } // We used to include malloc/free by default in the past. Show a helpful error in // builds with assertions. var ALLOC_NORMAL = 0; // Tries to use _malloc() var ALLOC_STACK = 1; // Lives for the duration of the current function call // allocate(): This is for internal use. You can use it yourself as well, but the interface // is a little tricky (see docs right below). The reason is that it is optimized // for multiple syntaxes to save space in generated code. So you should // normally not use allocate(), and instead allocate memory using _malloc(), // initialize it with setValue(), and so forth. // @slab: An array of data. // @allocator: How to allocate memory, see ALLOC_* /** @type {function((Uint8Array|Array), number)} */ function allocate(slab, allocator) { var ret; assert(typeof allocator === 'number', 'allocate no longer takes a type argument') assert(typeof slab !== 'number', 'allocate no longer takes a number as arg0') if (allocator == ALLOC_STACK) { ret = stackAlloc(slab.length); } else { ret = _malloc(slab.length); } if (slab.subarray || slab.slice) { HEAPU8.set(/** @type {!Uint8Array} */(slab), ret); } else { HEAPU8.set(new Uint8Array(slab), ret); } return ret; } // include: runtime_strings.js // runtime_strings.js: Strings related runtime functions that are part of both MINIMAL_RUNTIME and regular runtime. // Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the given array that contains uint8 values, returns // a copy of that string as a Javascript String object. var UTF8Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf8') : undefined; /** * @param {number} idx * @param {number=} maxBytesToRead * @return {string} */ function UTF8ArrayToString(heap, idx, maxBytesToRead) { var endIdx = idx + maxBytesToRead; var endPtr = idx; // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself. // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage. // (As a tiny code save trick, compare endPtr against endIdx using a negation, so that undefined means Infinity) while (heap[endPtr] && !(endPtr >= endIdx)) ++endPtr; if (endPtr - idx > 16 && heap.subarray && UTF8Decoder) { return UTF8Decoder.decode(heap.subarray(idx, endPtr)); } else { var str = ''; // If building with TextDecoder, we have already computed the string length above, so test loop end condition against that while (idx < endPtr) { // For UTF8 byte structure, see: // http://en.wikipedia.org/wiki/UTF-8#Description // https://www.ietf.org/rfc/rfc2279.txt // https://tools.ietf.org/html/rfc3629 var u0 = heap[idx++]; if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; } var u1 = heap[idx++] & 63; if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; } var u2 = heap[idx++] & 63; if ((u0 & 0xF0) == 0xE0) { u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; } else { if ((u0 & 0xF8) != 0xF0) warnOnce('Invalid UTF-8 leading byte 0x' + u0.toString(16) + ' encountered when deserializing a UTF-8 string in wasm memory to a JS string!'); u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heap[idx++] & 63); } if (u0 < 0x10000) { str += String.fromCharCode(u0); } else { var ch = u0 - 0x10000; str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); } } } return str; } // Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the emscripten HEAP, returns a // copy of that string as a Javascript String object. // maxBytesToRead: an optional length that specifies the maximum number of bytes to read. You can omit // this parameter to scan the string until the first \0 byte. If maxBytesToRead is // passed, and the string at [ptr, ptr+maxBytesToReadr[ contains a null byte in the // middle, then the string will cut short at that byte index (i.e. maxBytesToRead will // not produce a string of exact length [ptr, ptr+maxBytesToRead[) // N.B. mixing frequent uses of UTF8ToString() with and without maxBytesToRead may // throw JS JIT optimizations off, so it is worth to consider consistently using one // style or the other. /** * @param {number} ptr * @param {number=} maxBytesToRead * @return {string} */ function UTF8ToString(ptr, maxBytesToRead) { return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ''; } // Copies the given Javascript String object 'str' to the given byte array at address 'outIdx', // encoded in UTF8 form and null-terminated. The copy will require at most str.length*4+1 bytes of space in the HEAP. // Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write. // Parameters: // str: the Javascript string to copy. // heap: the array to copy to. Each index in this array is assumed to be one 8-byte element. // outIdx: The starting offset in the array to begin the copying. // maxBytesToWrite: The maximum number of bytes this function can write to the array. // This count should include the null terminator, // i.e. if maxBytesToWrite=1, only the null terminator will be written and nothing else. // maxBytesToWrite=0 does not write any bytes to the output, not even the null terminator. // Returns the number of bytes written, EXCLUDING the null terminator. function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { if (!(maxBytesToWrite > 0)) // Parameter maxBytesToWrite is not optional. Negative values, 0, null, undefined and false each don't write out any bytes. return 0; var startIdx = outIdx; var endIdx = outIdx + maxBytesToWrite - 1; // -1 for string null terminator. for (var i = 0; i < str.length; ++i) { // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. // See http://unicode.org/faq/utf_bom.html#utf16-3 // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629 var u = str.charCodeAt(i); // possibly a lead surrogate if (u >= 0xD800 && u <= 0xDFFF) { var u1 = str.charCodeAt(++i); u = 0x10000 + ((u & 0x3FF) << 10) | (u1 & 0x3FF); } if (u <= 0x7F) { if (outIdx >= endIdx) break; heap[outIdx++] = u; } else if (u <= 0x7FF) { if (outIdx + 1 >= endIdx) break; heap[outIdx++] = 0xC0 | (u >> 6); heap[outIdx++] = 0x80 | (u & 63); } else if (u <= 0xFFFF) { if (outIdx + 2 >= endIdx) break; heap[outIdx++] = 0xE0 | (u >> 12); heap[outIdx++] = 0x80 | ((u >> 6) & 63); heap[outIdx++] = 0x80 | (u & 63); } else { if (outIdx + 3 >= endIdx) break; if (u >= 0x200000) warnOnce('Invalid Unicode code point 0x' + u.toString(16) + ' encountered when serializing a JS string to a UTF-8 string in wasm memory! (Valid unicode code points should be in range 0-0x1FFFFF).'); heap[outIdx++] = 0xF0 | (u >> 18); heap[outIdx++] = 0x80 | ((u >> 12) & 63); heap[outIdx++] = 0x80 | ((u >> 6) & 63); heap[outIdx++] = 0x80 | (u & 63); } } // Null-terminate the pointer to the buffer. heap[outIdx] = 0; return outIdx - startIdx; } // Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', // null-terminated and encoded in UTF8 form. The copy will require at most str.length*4+1 bytes of space in the HEAP. // Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write. // Returns the number of bytes written, EXCLUDING the null terminator. function stringToUTF8(str, outPtr, maxBytesToWrite) { assert(typeof maxBytesToWrite == 'number', 'stringToUTF8(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); return stringToUTF8Array(str, HEAPU8,outPtr, maxBytesToWrite); } // Returns the number of bytes the given Javascript string takes if encoded as a UTF8 byte array, EXCLUDING the null terminator byte. function lengthBytesUTF8(str) { var len = 0; for (var i = 0; i < str.length; ++i) { // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8. // See http://unicode.org/faq/utf_bom.html#utf16-3 var u = str.charCodeAt(i); // possibly a lead surrogate if (u >= 0xD800 && u <= 0xDFFF) u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF); if (u <= 0x7F) ++len; else if (u <= 0x7FF) len += 2; else if (u <= 0xFFFF) len += 3; else len += 4; } return len; } // end include: runtime_strings.js // include: runtime_strings_extra.js // runtime_strings_extra.js: Strings related runtime functions that are available only in regular runtime. // Given a pointer 'ptr' to a null-terminated ASCII-encoded string in the emscripten HEAP, returns // a copy of that string as a Javascript String object. function AsciiToString(ptr) { var str = ''; while (1) { var ch = HEAPU8[((ptr++)>>0)]; if (!ch) return str; str += String.fromCharCode(ch); } } // Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', // null-terminated and encoded in ASCII form. The copy will require at most str.length+1 bytes of space in the HEAP. function stringToAscii(str, outPtr) { return writeAsciiToMemory(str, outPtr, false); } // Given a pointer 'ptr' to a null-terminated UTF16LE-encoded string in the emscripten HEAP, returns // a copy of that string as a Javascript String object. var UTF16Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-16le') : undefined; function UTF16ToString(ptr, maxBytesToRead) { assert(ptr % 2 == 0, 'Pointer passed to UTF16ToString must be aligned to two bytes!'); var endPtr = ptr; // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself. // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage. var idx = endPtr >> 1; var maxIdx = idx + maxBytesToRead / 2; // If maxBytesToRead is not passed explicitly, it will be undefined, and this // will always evaluate to true. This saves on code size. while (!(idx >= maxIdx) && HEAPU16[idx]) ++idx; endPtr = idx << 1; if (endPtr - ptr > 32 && UTF16Decoder) { return UTF16Decoder.decode(HEAPU8.subarray(ptr, endPtr)); } else { var str = ''; // If maxBytesToRead is not passed explicitly, it will be undefined, and the for-loop's condition // will always evaluate to true. The loop is then terminated on the first null char. for (var i = 0; !(i >= maxBytesToRead / 2); ++i) { var codeUnit = HEAP16[(((ptr)+(i*2))>>1)]; if (codeUnit == 0) break; // fromCharCode constructs a character from a UTF-16 code unit, so we can pass the UTF16 string right through. str += String.fromCharCode(codeUnit); } return str; } } // Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', // null-terminated and encoded in UTF16 form. The copy will require at most str.length*4+2 bytes of space in the HEAP. // Use the function lengthBytesUTF16() to compute the exact number of bytes (excluding null terminator) that this function will write. // Parameters: // str: the Javascript string to copy. // outPtr: Byte address in Emscripten HEAP where to write the string to. // maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null // terminator, i.e. if maxBytesToWrite=2, only the null terminator will be written and nothing else. // maxBytesToWrite<2 does not write any bytes to the output, not even the null terminator. // Returns the number of bytes written, EXCLUDING the null terminator. function stringToUTF16(str, outPtr, maxBytesToWrite) { assert(outPtr % 2 == 0, 'Pointer passed to stringToUTF16 must be aligned to two bytes!'); assert(typeof maxBytesToWrite == 'number', 'stringToUTF16(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. if (maxBytesToWrite === undefined) { maxBytesToWrite = 0x7FFFFFFF; } if (maxBytesToWrite < 2) return 0; maxBytesToWrite -= 2; // Null terminator. var startPtr = outPtr; var numCharsToWrite = (maxBytesToWrite < str.length*2) ? (maxBytesToWrite / 2) : str.length; for (var i = 0; i < numCharsToWrite; ++i) { // charCodeAt returns a UTF-16 encoded code unit, so it can be directly written to the HEAP. var codeUnit = str.charCodeAt(i); // possibly a lead surrogate HEAP16[((outPtr)>>1)] = codeUnit; outPtr += 2; } // Null-terminate the pointer to the HEAP. HEAP16[((outPtr)>>1)] = 0; return outPtr - startPtr; } // Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte. function lengthBytesUTF16(str) { return str.length*2; } function UTF32ToString(ptr, maxBytesToRead) { assert(ptr % 4 == 0, 'Pointer passed to UTF32ToString must be aligned to four bytes!'); var i = 0; var str = ''; // If maxBytesToRead is not passed explicitly, it will be undefined, and this // will always evaluate to true. This saves on code size. while (!(i >= maxBytesToRead / 4)) { var utf32 = HEAP32[(((ptr)+(i*4))>>2)]; if (utf32 == 0) break; ++i; // Gotcha: fromCharCode constructs a character from a UTF-16 encoded code (pair), not from a Unicode code point! So encode the code point to UTF-16 for constructing. // See http://unicode.org/faq/utf_bom.html#utf16-3 if (utf32 >= 0x10000) { var ch = utf32 - 0x10000; str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); } else { str += String.fromCharCode(utf32); } } return str; } // Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr', // null-terminated and encoded in UTF32 form. The copy will require at most str.length*4+4 bytes of space in the HEAP. // Use the function lengthBytesUTF32() to compute the exact number of bytes (excluding null terminator) that this function will write. // Parameters: // str: the Javascript string to copy. // outPtr: Byte address in Emscripten HEAP where to write the string to. // maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null // terminator, i.e. if maxBytesToWrite=4, only the null terminator will be written and nothing else. // maxBytesToWrite<4 does not write any bytes to the output, not even the null terminator. // Returns the number of bytes written, EXCLUDING the null terminator. function stringToUTF32(str, outPtr, maxBytesToWrite) { assert(outPtr % 4 == 0, 'Pointer passed to stringToUTF32 must be aligned to four bytes!'); assert(typeof maxBytesToWrite == 'number', 'stringToUTF32(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!'); // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed. if (maxBytesToWrite === undefined) { maxBytesToWrite = 0x7FFFFFFF; } if (maxBytesToWrite < 4) return 0; var startPtr = outPtr; var endPtr = startPtr + maxBytesToWrite - 4; for (var i = 0; i < str.length; ++i) { // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap. // See http://unicode.org/faq/utf_bom.html#utf16-3 var codeUnit = str.charCodeAt(i); // possibly a lead surrogate if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) { var trailSurrogate = str.charCodeAt(++i); codeUnit = 0x10000 + ((codeUnit & 0x3FF) << 10) | (trailSurrogate & 0x3FF); } HEAP32[((outPtr)>>2)] = codeUnit; outPtr += 4; if (outPtr + 4 > endPtr) break; } // Null-terminate the pointer to the HEAP. HEAP32[((outPtr)>>2)] = 0; return outPtr - startPtr; } // Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte. function lengthBytesUTF32(str) { var len = 0; for (var i = 0; i < str.length; ++i) { // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap. // See http://unicode.org/faq/utf_bom.html#utf16-3 var codeUnit = str.charCodeAt(i); if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) ++i; // possibly a lead surrogate, so skip over the tail surrogate. len += 4; } return len; } // Allocate heap space for a JS string, and write it there. // It is the responsibility of the caller to free() that memory. function allocateUTF8(str) { var size = lengthBytesUTF8(str) + 1; var ret = _malloc(size); if (ret) stringToUTF8Array(str, HEAP8, ret, size); return ret; } // Allocate stack space for a JS string, and write it there. function allocateUTF8OnStack(str) { var size = lengthBytesUTF8(str) + 1; var ret = stackAlloc(size); stringToUTF8Array(str, HEAP8, ret, size); return ret; } // Deprecated: This function should not be called because it is unsafe and does not provide // a maximum length limit of how many bytes it is allowed to write. Prefer calling the // function stringToUTF8Array() instead, which takes in a maximum length that can be used // to be secure from out of bounds writes. /** @deprecated @param {boolean=} dontAddNull */ function writeStringToMemory(string, buffer, dontAddNull) { warnOnce('writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!'); var /** @type {number} */ lastChar, /** @type {number} */ end; if (dontAddNull) { // stringToUTF8Array always appends null. If we don't want to do that, remember the // character that existed at the location where the null will be placed, and restore // that after the write (below). end = buffer + lengthBytesUTF8(string); lastChar = HEAP8[end]; } stringToUTF8(string, buffer, Infinity); if (dontAddNull) HEAP8[end] = lastChar; // Restore the value under the null character. } function writeArrayToMemory(array, buffer) { assert(array.length >= 0, 'writeArrayToMemory array must have a length (should be an array or typed array)') HEAP8.set(array, buffer); } /** @param {boolean=} dontAddNull */ function writeAsciiToMemory(str, buffer, dontAddNull) { for (var i = 0; i < str.length; ++i) { assert(str.charCodeAt(i) === str.charCodeAt(i)&0xff); HEAP8[((buffer++)>>0)] = str.charCodeAt(i); } // Null-terminate the pointer to the HEAP. if (!dontAddNull) HEAP8[((buffer)>>0)] = 0; } // end include: runtime_strings_extra.js // Memory management function alignUp(x, multiple) { if (x % multiple > 0) { x += multiple - (x % multiple); } return x; } var HEAP, /** @type {ArrayBuffer} */ buffer, /** @type {Int8Array} */ HEAP8, /** @type {Uint8Array} */ HEAPU8, /** @type {Int16Array} */ HEAP16, /** @type {Uint16Array} */ HEAPU16, /** @type {Int32Array} */ HEAP32, /** @type {Uint32Array} */ HEAPU32, /** @type {Float32Array} */ HEAPF32, /** @type {Float64Array} */ HEAPF64; function updateGlobalBufferAndViews(buf) { buffer = buf; Module['HEAP8'] = HEAP8 = new Int8Array(buf); Module['HEAP16'] = HEAP16 = new Int16Array(buf); Module['HEAP32'] = HEAP32 = new Int32Array(buf); Module['HEAPU8'] = HEAPU8 = new Uint8Array(buf); Module['HEAPU16'] = HEAPU16 = new Uint16Array(buf); Module['HEAPU32'] = HEAPU32 = new Uint32Array(buf); Module['HEAPF32'] = HEAPF32 = new Float32Array(buf); Module['HEAPF64'] = HEAPF64 = new Float64Array(buf); } var TOTAL_STACK = 5242880; if (Module['TOTAL_STACK']) assert(TOTAL_STACK === Module['TOTAL_STACK'], 'the stack size can no longer be determined at runtime') var INITIAL_MEMORY = Module['INITIAL_MEMORY'] || 16777216; if (!Object.getOwnPropertyDescriptor(Module, 'INITIAL_MEMORY')) { Object.defineProperty(Module, 'INITIAL_MEMORY', { configurable: true, get: function() { abort('Module.INITIAL_MEMORY has been replaced with plain INITIAL_MEMORY (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)') } }); } assert(INITIAL_MEMORY >= TOTAL_STACK, 'INITIAL_MEMORY should be larger than TOTAL_STACK, was ' + INITIAL_MEMORY + '! (TOTAL_STACK=' + TOTAL_STACK + ')'); // check for full engine support (use string 'subarray' to avoid closure compiler confusion) assert(typeof Int32Array !== 'undefined' && typeof Float64Array !== 'undefined' && Int32Array.prototype.subarray !== undefined && Int32Array.prototype.set !== undefined, 'JS engine does not provide full typed array support'); // If memory is defined in wasm, the user can't provide it. assert(!Module['wasmMemory'], 'Use of `wasmMemory` detected. Use -s IMPORTED_MEMORY to define wasmMemory externally'); assert(INITIAL_MEMORY == 16777216, 'Detected runtime INITIAL_MEMORY setting. Use -s IMPORTED_MEMORY to define wasmMemory dynamically'); // include: runtime_init_table.js // In regular non-RELOCATABLE mode the table is exported // from the wasm module and this will be assigned once // the exports are available. var wasmTable; // end include: runtime_init_table.js // include: runtime_stack_check.js // Initializes the stack cookie. Called at the startup of main and at the startup of each thread in pthreads mode. function writeStackCookie() { var max = _emscripten_stack_get_end(); assert((max & 3) == 0); // The stack grows downwards HEAPU32[(max >> 2)+1] = 0x2135467; HEAPU32[(max >> 2)+2] = 0x89BACDFE; // Also test the global address 0 for integrity. HEAP32[0] = 0x63736d65; /* 'emsc' */ } function checkStackCookie() { if (ABORT) return; var max = _emscripten_stack_get_end(); var cookie1 = HEAPU32[(max >> 2)+1]; var cookie2 = HEAPU32[(max >> 2)+2]; if (cookie1 != 0x2135467 || cookie2 != 0x89BACDFE) { abort('Stack overflow! Stack cookie has been overwritten, expected hex dwords 0x89BACDFE and 0x2135467, but received 0x' + cookie2.toString(16) + ' ' + cookie1.toString(16)); } // Also test the global address 0 for integrity. if (HEAP32[0] !== 0x63736d65 /* 'emsc' */) abort('Runtime error: The application has corrupted its heap memory area (address zero)!'); } // end include: runtime_stack_check.js // include: runtime_assertions.js // Endianness check (note: assumes compiler arch was little-endian) (function() { var h16 = new Int16Array(1); var h8 = new Int8Array(h16.buffer); h16[0] = 0x6373; if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian!'; })(); function abortFnPtrError(ptr, sig) { abort("Invalid function pointer " + ptr + " called with signature '" + sig + "'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this). Build with ASSERTIONS=2 for more info."); } // end include: runtime_assertions.js var __ATPRERUN__ = []; // functions called before the runtime is initialized var __ATINIT__ = []; // functions called during startup var __ATMAIN__ = []; // functions called when main() is to be run var __ATEXIT__ = []; // functions called during shutdown var __ATPOSTRUN__ = []; // functions called after the main() is called var runtimeInitialized = false; var runtimeExited = false; __ATINIT__.push({ func: function() { ___wasm_call_ctors() } }); function preRun() { if (Module['preRun']) { if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; while (Module['preRun'].length) { addOnPreRun(Module['preRun'].shift()); } } callRuntimeCallbacks(__ATPRERUN__); } function initRuntime() { checkStackCookie(); assert(!runtimeInitialized); runtimeInitialized = true; if (!Module["noFSInit"] && !FS.init.initialized) FS.init(); TTY.init(); callRuntimeCallbacks(__ATINIT__); } function preMain() { checkStackCookie(); FS.ignorePermissions = false; callRuntimeCallbacks(__ATMAIN__); } function exitRuntime() { checkStackCookie(); runtimeExited = true; } function postRun() { checkStackCookie(); if (Module['postRun']) { if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']]; while (Module['postRun'].length) { addOnPostRun(Module['postRun'].shift()); } } callRuntimeCallbacks(__ATPOSTRUN__); } function addOnPreRun(cb) { __ATPRERUN__.unshift(cb); } function addOnInit(cb) { __ATINIT__.unshift(cb); } function addOnPreMain(cb) { __ATMAIN__.unshift(cb); } function addOnExit(cb) { } function addOnPostRun(cb) { __ATPOSTRUN__.unshift(cb); } // include: runtime_math.js // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc assert(Math.imul, 'This browser does not support Math.imul(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); assert(Math.fround, 'This browser does not support Math.fround(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); assert(Math.clz32, 'This browser does not support Math.clz32(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); assert(Math.trunc, 'This browser does not support Math.trunc(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill'); // end include: runtime_math.js // A counter of dependencies for calling run(). If we need to // do asynchronous work before running, increment this and // decrement it. Incrementing must happen in a place like // Module.preRun (used by emcc to add file preloading). // Note that you can add dependencies in preRun, even though // it happens right before run - run will be postponed until // the dependencies are met. var runDependencies = 0; var runDependencyWatcher = null; var dependenciesFulfilled = null; // overridden to take different actions when all run dependencies are fulfilled var runDependencyTracking = {}; function getUniqueRunDependency(id) { var orig = id; while (1) { if (!runDependencyTracking[id]) return id; id = orig + Math.random(); } } function addRunDependency(id) { runDependencies++; if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies); } if (id) { assert(!runDependencyTracking[id]); runDependencyTracking[id] = 1; if (runDependencyWatcher === null && typeof setInterval !== 'undefined') { // Check for missing dependencies every few seconds runDependencyWatcher = setInterval(function() { if (ABORT) { clearInterval(runDependencyWatcher); runDependencyWatcher = null; return; } var shown = false; for (var dep in runDependencyTracking) { if (!shown) { shown = true; err('still waiting on run dependencies:'); } err('dependency: ' + dep); } if (shown) { err('(end of list)'); } }, 10000); } } else { err('warning: run dependency added without ID'); } } function removeRunDependency(id) { runDependencies--; if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies); } if (id) { assert(runDependencyTracking[id]); delete runDependencyTracking[id]; } else { err('warning: run dependency removed without ID'); } if (runDependencies == 0) { if (runDependencyWatcher !== null) { clearInterval(runDependencyWatcher); runDependencyWatcher = null; } if (dependenciesFulfilled) { var callback = dependenciesFulfilled; dependenciesFulfilled = null; callback(); // can add another dependenciesFulfilled } } } Module["preloadedImages"] = {}; // maps url to image data Module["preloadedAudios"] = {}; // maps url to audio data /** @param {string|number=} what */ function abort(what) { if (Module['onAbort']) { Module['onAbort'](what); } what += ''; err(what); ABORT = true; EXITSTATUS = 1; var output = 'abort(' + what + ') at ' + stackTrace(); what = output; // Use a wasm runtime error, because a JS error might be seen as a foreign // exception, which means we'd run destructors on it. We need the error to // simply make the program stop. var e = new WebAssembly.RuntimeError(what); // Throw the error whether or not MODULARIZE is set because abort is used // in code paths apart from instantiation where an exception is expected // to be thrown when abort is called. throw e; } // {{MEM_INITIALIZER}} // include: memoryprofiler.js // end include: memoryprofiler.js // include: URIUtils.js function hasPrefix(str, prefix) { return String.prototype.startsWith ? str.startsWith(prefix) : str.indexOf(prefix) === 0; } // Prefix of data URIs emitted by SINGLE_FILE and related options. var dataURIPrefix = 'data:application/octet-stream;base64,'; // Indicates whether filename is a base64 data URI. function isDataURI(filename) { return hasPrefix(filename, dataURIPrefix); } var fileURIPrefix = "file://"; // Indicates whether filename is delivered via file protocol (as opposed to http/https) function isFileURI(filename) { return hasPrefix(filename, fileURIPrefix); } // end include: URIUtils.js function createExportWrapper(name, fixedasm) { return function() { var displayName = name; var asm = fixedasm; if (!fixedasm) { asm = Module['asm']; } assert(runtimeInitialized, 'native function `' + displayName + '` called before runtime initialization'); assert(!runtimeExited, 'native function `' + displayName + '` called after runtime exit (use NO_EXIT_RUNTIME to keep it alive after main() exits)'); if (!asm[name]) { assert(asm[name], 'exported native function `' + displayName + '` not found'); } return asm[name].apply(null, arguments); }; } var wasmBinaryFile = 'LowResNX120.wasm'; if (!isDataURI(wasmBinaryFile)) { wasmBinaryFile = locateFile(wasmBinaryFile); } function getBinary(file) { try { if (file == wasmBinaryFile && wasmBinary) { return new Uint8Array(wasmBinary); } if (readBinary) { return readBinary(file); } else { throw "both async and sync fetching of the wasm failed"; } } catch (err) { abort(err); } } function getBinaryPromise() { // If we don't have the binary yet, try to to load it asynchronously. // Fetch has some additional restrictions over XHR, like it can't be used on a file:// url. // See https://github.com/github/fetch/pull/92#issuecomment-140665932 // Cordova or Electron apps are typically loaded from a file:// url. // So use fetch if it is available and the url is not a file, otherwise fall back to XHR. if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { if (typeof fetch === 'function' && !isFileURI(wasmBinaryFile) ) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function(response) { if (!response['ok']) { throw "failed to load wasm binary file at '" + wasmBinaryFile + "'"; } return response['arrayBuffer'](); }).catch(function () { return getBinary(wasmBinaryFile); }); } else { if (readAsync) { // fetch is not available or url is file => try XHR (readAsync uses XHR internally) return new Promise(function(resolve, reject) { readAsync(wasmBinaryFile, function(response) { resolve(new Uint8Array(/** @type{!ArrayBuffer} */(response))) }, reject) }); } } } // Otherwise, getBinary should be able to get it synchronously return Promise.resolve().then(function() { return getBinary(wasmBinaryFile); }); } // Create the wasm instance. // Receives the wasm imports, returns the exports. function createWasm() { // prepare imports var info = { 'env': asmLibraryArg, 'wasi_snapshot_preview1': asmLibraryArg, }; // Load the wasm module and create an instance of using native support in the JS engine. // handle a generated wasm instance, receiving its exports and // performing other necessary setup /** @param {WebAssembly.Module=} module*/ function receiveInstance(instance, module) { var exports = instance.exports; Module['asm'] = exports; wasmMemory = Module['asm']['memory']; assert(wasmMemory, "memory not found in wasm exports"); // This assertion doesn't hold when emscripten is run in --post-link // mode. // TODO(sbc): Read INITIAL_MEMORY out of the wasm file in post-link mode. //assert(wasmMemory.buffer.byteLength === 16777216); updateGlobalBufferAndViews(wasmMemory.buffer); wasmTable = Module['asm']['__indirect_function_table']; assert(wasmTable, "table not found in wasm exports"); removeRunDependency('wasm-instantiate'); } // we can't run yet (except in a pthread, where we have a custom sync instantiator) addRunDependency('wasm-instantiate'); // Async compilation can be confusing when an error on the page overwrites Module // (for example, if the order of elements is wrong, and the one defining Module is // later), so we save Module and check it later. var trueModule = Module; function receiveInstantiatedSource(output) { // 'output' is a WebAssemblyInstantiatedSource object which has both the module and instance. // receiveInstance() will swap in the exports (to Module.asm) so they can be called assert(Module === trueModule, 'the Module object should not be replaced during async compilation - perhaps the order of HTML elements is wrong?'); trueModule = null; // TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line. // When the regression is fixed, can restore the above USE_PTHREADS-enabled path. receiveInstance(output['instance']); } function instantiateArrayBuffer(receiver) { return getBinaryPromise().then(function(binary) { return WebAssembly.instantiate(binary, info); }).then(receiver, function(reason) { err('failed to asynchronously prepare wasm: ' + reason); // Warn on some common problems. if (isFileURI(wasmBinaryFile)) { err('warning: Loading from a file URI (' + wasmBinaryFile + ') is not supported in most browsers. See https://emscripten.org/docs/getting_started/FAQ.html#how-do-i-run-a-local-webserver-for-testing-why-does-my-program-stall-in-downloading-or-preparing'); } abort(reason); }); } // Prefer streaming instantiation if available. function instantiateAsync() { if (!wasmBinary && typeof WebAssembly.instantiateStreaming === 'function' && !isDataURI(wasmBinaryFile) && // Don't use streaming for file:// delivered objects in a webview, fetch them synchronously. !isFileURI(wasmBinaryFile) && typeof fetch === 'function') { return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function (response) { var result = WebAssembly.instantiateStreaming(response, info); return result.then(receiveInstantiatedSource, function(reason) { // We expect the most common failure cause to be a bad MIME type for the binary, // in which case falling back to ArrayBuffer instantiation should work. err('wasm streaming compile failed: ' + reason); err('falling back to ArrayBuffer instantiation'); return instantiateArrayBuffer(receiveInstantiatedSource); }); }); } else { return instantiateArrayBuffer(receiveInstantiatedSource); } } // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback // to manually instantiate the Wasm module themselves. This allows pages to run the instantiation parallel // to any other async startup actions they are performing. if (Module['instantiateWasm']) { try { var exports = Module['instantiateWasm'](info, receiveInstance); return exports; } catch(e) { err('Module.instantiateWasm callback failed with error: ' + e); return false; } } instantiateAsync(); return {}; // no exports yet; we'll fill them in later } // Globals used by JS i64 conversions (see makeSetValue) var tempDouble; var tempI64; // === Body === var ASM_CONSTS = { 65204: function($0) {var str = UTF8ToString($0) + '\n\n' + 'Abort/Retry/Ignore/AlwaysIgnore? [ariA] :'; var reply = window.prompt(str, "i"); if (reply === null) { reply = "i"; } return allocate(intArrayFromString(reply), 'i8', ALLOC_NORMAL);}, 65429: function($0, $1, $2) {var w = $0; var h = $1; var pixels = $2; if (!Module['SDL2']) Module['SDL2'] = {}; var SDL2 = Module['SDL2']; if (SDL2.ctxCanvas !== Module['canvas']) { SDL2.ctx = Module['createContext'](Module['canvas'], false, true); SDL2.ctxCanvas = Module['canvas']; } if (SDL2.w !== w || SDL2.h !== h || SDL2.imageCtx !== SDL2.ctx) { SDL2.image = SDL2.ctx.createImageData(w, h); SDL2.w = w; SDL2.h = h; SDL2.imageCtx = SDL2.ctx; } var data = SDL2.image.data; var src = pixels >> 2; var dst = 0; var num; if (typeof CanvasPixelArray !== 'undefined' && data instanceof CanvasPixelArray) { num = data.length; while (dst < num) { var val = HEAP32[src]; data[dst ] = val & 0xff; data[dst+1] = (val >> 8) & 0xff; data[dst+2] = (val >> 16) & 0xff; data[dst+3] = 0xff; src++; dst += 4; } } else { if (SDL2.data32Data !== data) { SDL2.data32 = new Int32Array(data.buffer); SDL2.data8 = new Uint8Array(data.buffer); } var data32 = SDL2.data32; num = data32.length; data32.set(HEAP32.subarray(src, src + num)); var data8 = SDL2.data8; var i = 3; var j = i + 4*num; if (num % 8 == 0) { while (i < j) { data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; } } else { while (i < j) { data8[i] = 0xff; i = i + 4 | 0; } } } SDL2.ctx.putImageData(SDL2.image, 0, 0); return 0;}, 66884: function($0, $1, $2, $3, $4) {var w = $0; var h = $1; var hot_x = $2; var hot_y = $3; var pixels = $4; var canvas = document.createElement("canvas"); canvas.width = w; canvas.height = h; var ctx = canvas.getContext("2d"); var image = ctx.createImageData(w, h); var data = image.data; var src = pixels >> 2; var dst = 0; var num; if (typeof CanvasPixelArray !== 'undefined' && data instanceof CanvasPixelArray) { num = data.length; while (dst < num) { var val = HEAP32[src]; data[dst ] = val & 0xff; data[dst+1] = (val >> 8) & 0xff; data[dst+2] = (val >> 16) & 0xff; data[dst+3] = (val >> 24) & 0xff; src++; dst += 4; } } else { var data32 = new Int32Array(data.buffer); num = data32.length; data32.set(HEAP32.subarray(src, src + num)); } ctx.putImageData(image, 0, 0); var url = hot_x === 0 && hot_y === 0 ? "url(" + canvas.toDataURL() + "), auto" : "url(" + canvas.toDataURL() + ") " + hot_x + " " + hot_y + ", auto"; var urlBuf = _malloc(url.length + 1); stringToUTF8(url, urlBuf, url.length + 1); return urlBuf;}, 67873: function($0) {if (Module['canvas']) { Module['canvas'].style['cursor'] = UTF8ToString($0); } return 0;}, 67966: function() {if (Module['canvas']) { Module['canvas'].style['cursor'] = 'none'; }}, 68035: function() {return screen.width;}, 68060: function() {return screen.height;}, 68086: function() {return window.innerWidth;}, 68116: function() {return window.innerHeight;}, 68147: function($0) {if (typeof setWindowTitle !== 'undefined') { setWindowTitle(UTF8ToString($0)); } return 0;}, 68242: function() {if (typeof(AudioContext) !== 'undefined') { return 1; } else if (typeof(webkitAudioContext) !== 'undefined') { return 1; } return 0;}, 68379: function() {if ((typeof(navigator.mediaDevices) !== 'undefined') && (typeof(navigator.mediaDevices.getUserMedia) !== 'undefined')) { return 1; } else if (typeof(navigator.webkitGetUserMedia) !== 'undefined') { return 1; } return 0;}, 68603: function($0) {if(typeof(Module['SDL2']) === 'undefined') { Module['SDL2'] = {}; } var SDL2 = Module['SDL2']; if (!$0) { SDL2.audio = {}; } else { SDL2.capture = {}; } if (!SDL2.audioContext) { if (typeof(AudioContext) !== 'undefined') { SDL2.audioContext = new AudioContext(); } else if (typeof(webkitAudioContext) !== 'undefined') { SDL2.audioContext = new webkitAudioContext(); } if (SDL2.audioContext) { autoResumeAudioContext(SDL2.audioContext); } } return SDL2.audioContext === undefined ? -1 : 0;}, 69096: function() {var SDL2 = Module['SDL2']; return SDL2.audioContext.sampleRate;}, 69164: function($0, $1, $2, $3) {var SDL2 = Module['SDL2']; var have_microphone = function(stream) { if (SDL2.capture.silenceTimer !== undefined) { clearTimeout(SDL2.capture.silenceTimer); SDL2.capture.silenceTimer = undefined; } SDL2.capture.mediaStreamNode = SDL2.audioContext.createMediaStreamSource(stream); SDL2.capture.scriptProcessorNode = SDL2.audioContext.createScriptProcessor($1, $0, 1); SDL2.capture.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) { if ((SDL2 === undefined) || (SDL2.capture === undefined)) { return; } audioProcessingEvent.outputBuffer.getChannelData(0).fill(0.0); SDL2.capture.currentCaptureBuffer = audioProcessingEvent.inputBuffer; dynCall('vi', $2, [$3]); }; SDL2.capture.mediaStreamNode.connect(SDL2.capture.scriptProcessorNode); SDL2.capture.scriptProcessorNode.connect(SDL2.audioContext.destination); SDL2.capture.stream = stream; }; var no_microphone = function(error) { }; SDL2.capture.silenceBuffer = SDL2.audioContext.createBuffer($0, $1, SDL2.audioContext.sampleRate); SDL2.capture.silenceBuffer.getChannelData(0).fill(0.0); var silence_callback = function() { SDL2.capture.currentCaptureBuffer = SDL2.capture.silenceBuffer; dynCall('vi', $2, [$3]); }; SDL2.capture.silenceTimer = setTimeout(silence_callback, ($1 / SDL2.audioContext.sampleRate) * 1000); if ((navigator.mediaDevices !== undefined) && (navigator.mediaDevices.getUserMedia !== undefined)) { navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(have_microphone).catch(no_microphone); } else if (navigator.webkitGetUserMedia !== undefined) { navigator.webkitGetUserMedia({ audio: true, video: false }, have_microphone, no_microphone); }}, 70816: function($0, $1, $2, $3) {var SDL2 = Module['SDL2']; SDL2.audio.scriptProcessorNode = SDL2.audioContext['createScriptProcessor']($1, 0, $0); SDL2.audio.scriptProcessorNode['onaudioprocess'] = function (e) { if ((SDL2 === undefined) || (SDL2.audio === undefined)) { return; } SDL2.audio.currentOutputBuffer = e['outputBuffer']; dynCall('vi', $2, [$3]); }; SDL2.audio.scriptProcessorNode['connect'](SDL2.audioContext['destination']);}, 71226: function($0, $1) {var SDL2 = Module['SDL2']; var numChannels = SDL2.capture.currentCaptureBuffer.numberOfChannels; for (var c = 0; c < numChannels; ++c) { var channelData = SDL2.capture.currentCaptureBuffer.getChannelData(c); if (channelData.length != $1) { throw 'Web Audio capture buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!'; } if (numChannels == 1) { for (var j = 0; j < $1; ++j) { setValue($0 + (j * 4), channelData[j], 'float'); } } else { for (var j = 0; j < $1; ++j) { setValue($0 + (((j * numChannels) + c) * 4), channelData[j], 'float'); } } }}, 71831: function($0, $1) {var SDL2 = Module['SDL2']; var numChannels = SDL2.audio.currentOutputBuffer['numberOfChannels']; for (var c = 0; c < numChannels; ++c) { var channelData = SDL2.audio.currentOutputBuffer['getChannelData'](c); if (channelData.length != $1) { throw 'Web Audio output buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!'; } for (var j = 0; j < $1; ++j) { channelData[j] = HEAPF32[$0 + ((j*numChannels + c) << 2) >> 2]; } }}, 72311: function($0) {var SDL2 = Module['SDL2']; if ($0) { if (SDL2.capture.silenceTimer !== undefined) { clearTimeout(SDL2.capture.silenceTimer); } if (SDL2.capture.stream !== undefined) { var tracks = SDL2.capture.stream.getAudioTracks(); for (var i = 0; i < tracks.length; i++) { SDL2.capture.stream.removeTrack(tracks[i]); } SDL2.capture.stream = undefined; } if (SDL2.capture.scriptProcessorNode !== undefined) { SDL2.capture.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) {}; SDL2.capture.scriptProcessorNode.disconnect(); SDL2.capture.scriptProcessorNode = undefined; } if (SDL2.capture.mediaStreamNode !== undefined) { SDL2.capture.mediaStreamNode.disconnect(); SDL2.capture.mediaStreamNode = undefined; } if (SDL2.capture.silenceBuffer !== undefined) { SDL2.capture.silenceBuffer = undefined } SDL2.capture = undefined; } else { if (SDL2.audio.scriptProcessorNode != undefined) { SDL2.audio.scriptProcessorNode.disconnect(); SDL2.audio.scriptProcessorNode = undefined; } SDL2.audio = undefined; } if ((SDL2.audioContext !== undefined) && (SDL2.audio === undefined) && (SDL2.capture === undefined)) { SDL2.audioContext.close(); SDL2.audioContext = undefined; }} }; function listenOnce(object, event, func) { object.addEventListener(event, func, { 'once': true }); } function autoResumeAudioContext(ctx, elements) { if (!elements) { elements = [document, document.getElementById('canvas')]; } ['keydown', 'mousedown', 'touchstart'].forEach(function(event) { elements.forEach(function(element) { if (element) { listenOnce(element, event, function() { if (ctx.state === 'suspended') ctx.resume(); }); } }); }); } function callRuntimeCallbacks(callbacks) { while(callbacks.length > 0) { var callback = callbacks.shift(); if (typeof callback == 'function') { callback(Module); // Pass the module as the first argument. continue; } var func = callback.func; if (typeof func === 'number') { if (callback.arg === undefined) { wasmTable.get(func)(); } else { wasmTable.get(func)(callback.arg); } } else { func(callback.arg === undefined ? null : callback.arg); } } } function demangle(func) { warnOnce('warning: build with -s DEMANGLE_SUPPORT=1 to link in libcxxabi demangling'); return func; } function demangleAll(text) { var regex = /\b_Z[\w\d_]+/g; return text.replace(regex, function(x) { var y = demangle(x); return x === y ? x : (y + ' [' + x + ']'); }); } function dynCallLegacy(sig, ptr, args) { assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \'' + sig + '\''); if (args && args.length) { // j (64-bit integer) must be passed in as two numbers [low 32, high 32]. assert(args.length === sig.substring(1).replace(/j/g, '--').length); } else { assert(sig.length == 1); } var f = Module["dynCall_" + sig]; return args && args.length ? f.apply(null, [ptr].concat(args)) : f.call(null, ptr); } function dynCall(sig, ptr, args) { // Without WASM_BIGINT support we cannot directly call function with i64 as // part of thier signature, so we rely the dynCall functions generated by // wasm-emscripten-finalize if (sig.indexOf('j') != -1) { return dynCallLegacy(sig, ptr, args); } assert(wasmTable.get(ptr), 'missing table entry in dynCall: ' + ptr); return wasmTable.get(ptr).apply(null, args) } function jsStackTrace() { var error = new Error(); if (!error.stack) { // IE10+ special cases: It does have callstack info, but it is only populated if an Error object is thrown, // so try that as a special-case. try { throw new Error(); } catch(e) { error = e; } if (!error.stack) { return '(no stack trace available)'; } } return error.stack.toString(); } function stackTrace() { var js = jsStackTrace(); if (Module['extraStackTrace']) js += '\n' + Module['extraStackTrace'](); return demangleAll(js); } function ___assert_fail(condition, filename, line, func) { abort('Assertion failed: ' + UTF8ToString(condition) + ', at: ' + [filename ? UTF8ToString(filename) : 'unknown filename', line, func ? UTF8ToString(func) : 'unknown function']); } function setErrNo(value) { HEAP32[((___errno_location())>>2)] = value; return value; } var PATH={splitPath:function(filename) { var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; return splitPathRe.exec(filename).slice(1); },normalizeArray:function(parts, allowAboveRoot) { // if the path tries to go above the root, `up` ends up > 0 var up = 0; for (var i = parts.length - 1; i >= 0; i--) { var last = parts[i]; if (last === '.') { parts.splice(i, 1); } else if (last === '..') { parts.splice(i, 1); up++; } else if (up) { parts.splice(i, 1); up--; } } // if the path is allowed to go above the root, restore leading ..s if (allowAboveRoot) { for (; up; up--) { parts.unshift('..'); } } return parts; },normalize:function(path) { var isAbsolute = path.charAt(0) === '/', trailingSlash = path.substr(-1) === '/'; // Normalize the path path = PATH.normalizeArray(path.split('/').filter(function(p) { return !!p; }), !isAbsolute).join('/'); if (!path && !isAbsolute) { path = '.'; } if (path && trailingSlash) { path += '/'; } return (isAbsolute ? '/' : '') + path; },dirname:function(path) { var result = PATH.splitPath(path), root = result[0], dir = result[1]; if (!root && !dir) { // No dirname whatsoever return '.'; } if (dir) { // It has a dirname, strip trailing slash dir = dir.substr(0, dir.length - 1); } return root + dir; },basename:function(path) { // EMSCRIPTEN return '/'' for '/', not an empty string if (path === '/') return '/'; path = PATH.normalize(path); path = path.replace(/\/$/, ""); var lastSlash = path.lastIndexOf('/'); if (lastSlash === -1) return path; return path.substr(lastSlash+1); },extname:function(path) { return PATH.splitPath(path)[3]; },join:function() { var paths = Array.prototype.slice.call(arguments, 0); return PATH.normalize(paths.join('/')); },join2:function(l, r) { return PATH.normalize(l + '/' + r); }}; function getRandomDevice() { if (typeof crypto === 'object' && typeof crypto['getRandomValues'] === 'function') { // for modern web browsers var randomBuffer = new Uint8Array(1); return function() { crypto.getRandomValues(randomBuffer); return randomBuffer[0]; }; } else if (ENVIRONMENT_IS_NODE) { // for nodejs with or without crypto support included try { var crypto_module = require('crypto'); // nodejs has crypto support return function() { return crypto_module['randomBytes'](1)[0]; }; } catch (e) { // nodejs doesn't have crypto support } } // we couldn't find a proper implementation, as Math.random() is not suitable for /dev/random, see emscripten-core/emscripten/pull/7096 return function() { abort("no cryptographic support found for randomDevice. consider polyfilling it if you want to use something insecure like Math.random(), e.g. put this in a --pre-js: var crypto = { getRandomValues: function(array) { for (var i = 0; i < array.length; i++) array[i] = (Math.random()*256)|0 } };"); }; } var PATH_FS={resolve:function() { var resolvedPath = '', resolvedAbsolute = false; for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path = (i >= 0) ? arguments[i] : FS.cwd(); // Skip empty and invalid entries if (typeof path !== 'string') { throw new TypeError('Arguments to path.resolve must be strings'); } else if (!path) { return ''; // an invalid portion invalidates the whole thing } resolvedPath = path + '/' + resolvedPath; resolvedAbsolute = path.charAt(0) === '/'; } // At this point the path should be resolved to a full absolute path, but // handle relative paths to be safe (might happen when process.cwd() fails) resolvedPath = PATH.normalizeArray(resolvedPath.split('/').filter(function(p) { return !!p; }), !resolvedAbsolute).join('/'); return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; },relative:function(from, to) { from = PATH_FS.resolve(from).substr(1); to = PATH_FS.resolve(to).substr(1); function trim(arr) { var start = 0; for (; start < arr.length; start++) { if (arr[start] !== '') break; } var end = arr.length - 1; for (; end >= 0; end--) { if (arr[end] !== '') break; } if (start > end) return []; return arr.slice(start, end - start + 1); } var fromParts = trim(from.split('/')); var toParts = trim(to.split('/')); var length = Math.min(fromParts.length, toParts.length); var samePartsLength = length; for (var i = 0; i < length; i++) { if (fromParts[i] !== toParts[i]) { samePartsLength = i; break; } } var outputParts = []; for (var i = samePartsLength; i < fromParts.length; i++) { outputParts.push('..'); } outputParts = outputParts.concat(toParts.slice(samePartsLength)); return outputParts.join('/'); }}; var TTY={ttys:[],init:function () { // https://github.com/emscripten-core/emscripten/pull/1555 // if (ENVIRONMENT_IS_NODE) { // // currently, FS.init does not distinguish if process.stdin is a file or TTY // // device, it always assumes it's a TTY device. because of this, we're forcing // // process.stdin to UTF8 encoding to at least make stdin reading compatible // // with text files until FS.init can be refactored. // process['stdin']['setEncoding']('utf8'); // } },shutdown:function() { // https://github.com/emscripten-core/emscripten/pull/1555 // if (ENVIRONMENT_IS_NODE) { // // inolen: any idea as to why node -e 'process.stdin.read()' wouldn't exit immediately (with process.stdin being a tty)? // // isaacs: because now it's reading from the stream, you've expressed interest in it, so that read() kicks off a _read() which creates a ReadReq operation // // inolen: I thought read() in that case was a synchronous operation that just grabbed some amount of buffered data if it exists? // // isaacs: it is. but it also triggers a _read() call, which calls readStart() on the handle // // isaacs: do process.stdin.pause() and i'd think it'd probably close the pending call // process['stdin']['pause'](); // } },register:function(dev, ops) { TTY.ttys[dev] = { input: [], output: [], ops: ops }; FS.registerDevice(dev, TTY.stream_ops); },stream_ops:{open:function(stream) { var tty = TTY.ttys[stream.node.rdev]; if (!tty) { throw new FS.ErrnoError(43); } stream.tty = tty; stream.seekable = false; },close:function(stream) { // flush any pending line data stream.tty.ops.flush(stream.tty); },flush:function(stream) { stream.tty.ops.flush(stream.tty); },read:function(stream, buffer, offset, length, pos /* ignored */) { if (!stream.tty || !stream.tty.ops.get_char) { throw new FS.ErrnoError(60); } var bytesRead = 0; for (var i = 0; i < length; i++) { var result; try { result = stream.tty.ops.get_char(stream.tty); } catch (e) { throw new FS.ErrnoError(29); } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6); } if (result === null || result === undefined) break; bytesRead++; buffer[offset+i] = result; } if (bytesRead) { stream.node.timestamp = Date.now(); } return bytesRead; },write:function(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.put_char) { throw new FS.ErrnoError(60); } try { for (var i = 0; i < length; i++) { stream.tty.ops.put_char(stream.tty, buffer[offset+i]); } } catch (e) { throw new FS.ErrnoError(29); } if (length) { stream.node.timestamp = Date.now(); } return i; }},default_tty_ops:{get_char:function(tty) { if (!tty.input.length) { var result = null; if (ENVIRONMENT_IS_NODE) { // we will read data by chunks of BUFSIZE var BUFSIZE = 256; var buf = Buffer.alloc ? Buffer.alloc(BUFSIZE) : new Buffer(BUFSIZE); var bytesRead = 0; try { bytesRead = nodeFS.readSync(process.stdin.fd, buf, 0, BUFSIZE, null); } catch(e) { // Cross-platform differences: on Windows, reading EOF throws an exception, but on other OSes, // reading EOF returns 0. Uniformize behavior by treating the EOF exception to return 0. if (e.toString().indexOf('EOF') != -1) bytesRead = 0; else throw e; } if (bytesRead > 0) { result = buf.slice(0, bytesRead).toString('utf-8'); } else { result = null; } } else if (typeof window != 'undefined' && typeof window.prompt == 'function') { // Browser. result = window.prompt('Input: '); // returns null on cancel if (result !== null) { result += '\n'; } } else if (typeof readline == 'function') { // Command line. result = readline(); if (result !== null) { result += '\n'; } } if (!result) { return null; } tty.input = intArrayFromString(result, true); } return tty.input.shift(); },put_char:function(tty, val) { if (val === null || val === 10) { out(UTF8ArrayToString(tty.output, 0)); tty.output = []; } else { if (val != 0) tty.output.push(val); // val == 0 would cut text output off in the middle. } },flush:function(tty) { if (tty.output && tty.output.length > 0) { out(UTF8ArrayToString(tty.output, 0)); tty.output = []; } }},default_tty1_ops:{put_char:function(tty, val) { if (val === null || val === 10) { err(UTF8ArrayToString(tty.output, 0)); tty.output = []; } else { if (val != 0) tty.output.push(val); } },flush:function(tty) { if (tty.output && tty.output.length > 0) { err(UTF8ArrayToString(tty.output, 0)); tty.output = []; } }}}; function mmapAlloc(size) { var alignedSize = alignMemory(size, 16384); var ptr = _malloc(alignedSize); while (size < alignedSize) HEAP8[ptr + size++] = 0; return ptr; } var MEMFS={ops_table:null,mount:function(mount) { return MEMFS.createNode(null, '/', 16384 | 511 /* 0777 */, 0); },createNode:function(parent, name, mode, dev) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { // no supported throw new FS.ErrnoError(63); } if (!MEMFS.ops_table) { MEMFS.ops_table = { dir: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, lookup: MEMFS.node_ops.lookup, mknod: MEMFS.node_ops.mknod, rename: MEMFS.node_ops.rename, unlink: MEMFS.node_ops.unlink, rmdir: MEMFS.node_ops.rmdir, readdir: MEMFS.node_ops.readdir, symlink: MEMFS.node_ops.symlink }, stream: { llseek: MEMFS.stream_ops.llseek } }, file: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr }, stream: { llseek: MEMFS.stream_ops.llseek, read: MEMFS.stream_ops.read, write: MEMFS.stream_ops.write, allocate: MEMFS.stream_ops.allocate, mmap: MEMFS.stream_ops.mmap, msync: MEMFS.stream_ops.msync } }, link: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, readlink: MEMFS.node_ops.readlink }, stream: {} }, chrdev: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr }, stream: FS.chrdev_stream_ops } }; } var node = FS.createNode(parent, name, mode, dev); if (FS.isDir(node.mode)) { node.node_ops = MEMFS.ops_table.dir.node; node.stream_ops = MEMFS.ops_table.dir.stream; node.contents = {}; } else if (FS.isFile(node.mode)) { node.node_ops = MEMFS.ops_table.file.node; node.stream_ops = MEMFS.ops_table.file.stream; node.usedBytes = 0; // The actual number of bytes used in the typed array, as opposed to contents.length which gives the whole capacity. // When the byte data of the file is populated, this will point to either a typed array, or a normal JS array. Typed arrays are preferred // for performance, and used by default. However, typed arrays are not resizable like normal JS arrays are, so there is a small disk size // penalty involved for appending file writes that continuously grow a file similar to std::vector capacity vs used -scheme. node.contents = null; } else if (FS.isLink(node.mode)) { node.node_ops = MEMFS.ops_table.link.node; node.stream_ops = MEMFS.ops_table.link.stream; } else if (FS.isChrdev(node.mode)) { node.node_ops = MEMFS.ops_table.chrdev.node; node.stream_ops = MEMFS.ops_table.chrdev.stream; } node.timestamp = Date.now(); // add the new node to the parent if (parent) { parent.contents[name] = node; parent.timestamp = node.timestamp; } return node; },getFileDataAsTypedArray:function(node) { if (!node.contents) return new Uint8Array(0); if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes. return new Uint8Array(node.contents); },expandFileStorage:function(node, newCapacity) { var prevCapacity = node.contents ? node.contents.length : 0; if (prevCapacity >= newCapacity) return; // No need to expand, the storage was already large enough. // Don't expand strictly to the given requested limit if it's only a very small increase, but instead geometrically grow capacity. // For small filesizes (<1MB), perform size*2 geometric increase, but for large sizes, do a much more conservative size*1.125 increase to // avoid overshooting the allocation cap by a very large margin. var CAPACITY_DOUBLING_MAX = 1024 * 1024; newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125)) >>> 0); if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding. var oldContents = node.contents; node.contents = new Uint8Array(newCapacity); // Allocate new storage. if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0); // Copy old data over to the new storage. },resizeFileStorage:function(node, newSize) { if (node.usedBytes == newSize) return; if (newSize == 0) { node.contents = null; // Fully decommit when requesting a resize to zero. node.usedBytes = 0; } else { var oldContents = node.contents; node.contents = new Uint8Array(newSize); // Allocate new storage. if (oldContents) { node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); // Copy old data over to the new storage. } node.usedBytes = newSize; } },node_ops:{getattr:function(node) { var attr = {}; // device numbers reuse inode numbers. attr.dev = FS.isChrdev(node.mode) ? node.id : 1; attr.ino = node.id; attr.mode = node.mode; attr.nlink = 1; attr.uid = 0; attr.gid = 0; attr.rdev = node.rdev; if (FS.isDir(node.mode)) { attr.size = 4096; } else if (FS.isFile(node.mode)) { attr.size = node.usedBytes; } else if (FS.isLink(node.mode)) { attr.size = node.link.length; } else { attr.size = 0; } attr.atime = new Date(node.timestamp); attr.mtime = new Date(node.timestamp); attr.ctime = new Date(node.timestamp); // NOTE: In our implementation, st_blocks = Math.ceil(st_size/st_blksize), // but this is not required by the standard. attr.blksize = 4096; attr.blocks = Math.ceil(attr.size / attr.blksize); return attr; },setattr:function(node, attr) { if (attr.mode !== undefined) { node.mode = attr.mode; } if (attr.timestamp !== undefined) { node.timestamp = attr.timestamp; } if (attr.size !== undefined) { MEMFS.resizeFileStorage(node, attr.size); } },lookup:function(parent, name) { throw FS.genericErrors[44]; },mknod:function(parent, name, mode, dev) { return MEMFS.createNode(parent, name, mode, dev); },rename:function(old_node, new_dir, new_name) { // if we're overwriting a directory at new_name, make sure it's empty. if (FS.isDir(old_node.mode)) { var new_node; try { new_node = FS.lookupNode(new_dir, new_name); } catch (e) { } if (new_node) { for (var i in new_node.contents) { throw new FS.ErrnoError(55); } } } // do the internal rewiring delete old_node.parent.contents[old_node.name]; old_node.parent.timestamp = Date.now() old_node.name = new_name; new_dir.contents[new_name] = old_node; new_dir.timestamp = old_node.parent.timestamp; old_node.parent = new_dir; },unlink:function(parent, name) { delete parent.contents[name]; parent.timestamp = Date.now(); },rmdir:function(parent, name) { var node = FS.lookupNode(parent, name); for (var i in node.contents) { throw new FS.ErrnoError(55); } delete parent.contents[name]; parent.timestamp = Date.now(); },readdir:function(node) { var entries = ['.', '..']; for (var key in node.contents) { if (!node.contents.hasOwnProperty(key)) { continue; } entries.push(key); } return entries; },symlink:function(parent, newname, oldpath) { var node = MEMFS.createNode(parent, newname, 511 /* 0777 */ | 40960, 0); node.link = oldpath; return node; },readlink:function(node) { if (!FS.isLink(node.mode)) { throw new FS.ErrnoError(28); } return node.link; }},stream_ops:{read:function(stream, buffer, offset, length, position) { var contents = stream.node.contents; if (position >= stream.node.usedBytes) return 0; var size = Math.min(stream.node.usedBytes - position, length); assert(size >= 0); if (size > 8 && contents.subarray) { // non-trivial, and typed array buffer.set(contents.subarray(position, position + size), offset); } else { for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i]; } return size; },write:function(stream, buffer, offset, length, position, canOwn) { // The data buffer should be a typed array view assert(!(buffer instanceof ArrayBuffer)); if (!length) return 0; var node = stream.node; node.timestamp = Date.now(); if (buffer.subarray && (!node.contents || node.contents.subarray)) { // This write is from a typed array to a typed array? if (canOwn) { assert(position === 0, 'canOwn must imply no weird position inside the file'); node.contents = buffer.subarray(offset, offset + length); node.usedBytes = length; return length; } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data. node.contents = buffer.slice(offset, offset + length); node.usedBytes = length; return length; } else if (position + length <= node.usedBytes) { // Writing to an already allocated and used subrange of the file? node.contents.set(buffer.subarray(offset, offset + length), position); return length; } } // Appending to an existing file and we need to reallocate, or source data did not come as a typed array. MEMFS.expandFileStorage(node, position+length); if (node.contents.subarray && buffer.subarray) { // Use typed array write which is available. node.contents.set(buffer.subarray(offset, offset + length), position); } else { for (var i = 0; i < length; i++) { node.contents[position + i] = buffer[offset + i]; // Or fall back to manual write if not. } } node.usedBytes = Math.max(node.usedBytes, position + length); return length; },llseek:function(stream, offset, whence) { var position = offset; if (whence === 1) { position += stream.position; } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { position += stream.node.usedBytes; } } if (position < 0) { throw new FS.ErrnoError(28); } return position; },allocate:function(stream, offset, length) { MEMFS.expandFileStorage(stream.node, offset + length); stream.node.usedBytes = Math.max(stream.node.usedBytes, offset + length); },mmap:function(stream, address, length, position, prot, flags) { if (address !== 0) { // We don't currently support location hints for the address of the mapping throw new FS.ErrnoError(28); } if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43); } var ptr; var allocated; var contents = stream.node.contents; // Only make a new copy when MAP_PRIVATE is specified. if (!(flags & 2) && contents.buffer === buffer) { // We can't emulate MAP_SHARED when the file is not backed by the buffer // we're mapping to (e.g. the HEAP buffer). allocated = false; ptr = contents.byteOffset; } else { // Try to avoid unnecessary slices. if (position > 0 || position + length < contents.length) { if (contents.subarray) { contents = contents.subarray(position, position + length); } else { contents = Array.prototype.slice.call(contents, position, position + length); } } allocated = true; ptr = mmapAlloc(length); if (!ptr) { throw new FS.ErrnoError(48); } HEAP8.set(contents, ptr); } return { ptr: ptr, allocated: allocated }; },msync:function(stream, buffer, offset, length, mmapFlags) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43); } if (mmapFlags & 2) { // MAP_PRIVATE calls need not to be synced back to underlying fs return 0; } var bytesWritten = MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false); // should we check if bytesWritten and length are the same? return 0; }}}; var ERRNO_MESSAGES={0:"Success",1:"Arg list too long",2:"Permission denied",3:"Address already in use",4:"Address not available",5:"Address family not supported by protocol family",6:"No more processes",7:"Socket already connected",8:"Bad file number",9:"Trying to read unreadable message",10:"Mount device busy",11:"Operation canceled",12:"No children",13:"Connection aborted",14:"Connection refused",15:"Connection reset by peer",16:"File locking deadlock error",17:"Destination address required",18:"Math arg out of domain of func",19:"Quota exceeded",20:"File exists",21:"Bad address",22:"File too large",23:"Host is unreachable",24:"Identifier removed",25:"Illegal byte sequence",26:"Connection already in progress",27:"Interrupted system call",28:"Invalid argument",29:"I/O error",30:"Socket is already connected",31:"Is a directory",32:"Too many symbolic links",33:"Too many open files",34:"Too many links",35:"Message too long",36:"Multihop attempted",37:"File or path name too long",38:"Network interface is not configured",39:"Connection reset by network",40:"Network is unreachable",41:"Too many open files in system",42:"No buffer space available",43:"No such device",44:"No such file or directory",45:"Exec format error",46:"No record locks available",47:"The link has been severed",48:"Not enough core",49:"No message of desired type",50:"Protocol not available",51:"No space left on device",52:"Function not implemented",53:"Socket is not connected",54:"Not a directory",55:"Directory not empty",56:"State not recoverable",57:"Socket operation on non-socket",59:"Not a typewriter",60:"No such device or address",61:"Value too large for defined data type",62:"Previous owner died",63:"Not super-user",64:"Broken pipe",65:"Protocol error",66:"Unknown protocol",67:"Protocol wrong type for socket",68:"Math result not representable",69:"Read only file system",70:"Illegal seek",71:"No such process",72:"Stale file handle",73:"Connection timed out",74:"Text file busy",75:"Cross-device link",100:"Device not a stream",101:"Bad font file fmt",102:"Invalid slot",103:"Invalid request code",104:"No anode",105:"Block device required",106:"Channel number out of range",107:"Level 3 halted",108:"Level 3 reset",109:"Link number out of range",110:"Protocol driver not attached",111:"No CSI structure available",112:"Level 2 halted",113:"Invalid exchange",114:"Invalid request descriptor",115:"Exchange full",116:"No data (for no delay io)",117:"Timer expired",118:"Out of streams resources",119:"Machine is not on the network",120:"Package not installed",121:"The object is remote",122:"Advertise error",123:"Srmount error",124:"Communication error on send",125:"Cross mount point (not really error)",126:"Given log. name not unique",127:"f.d. invalid for this operation",128:"Remote address changed",129:"Can access a needed shared lib",130:"Accessing a corrupted shared lib",131:".lib section in a.out corrupted",132:"Attempting to link in too many libs",133:"Attempting to exec a shared library",135:"Streams pipe error",136:"Too many users",137:"Socket type not supported",138:"Not supported",139:"Protocol family not supported",140:"Can't send after socket shutdown",141:"Too many references",142:"Host is down",148:"No medium (in tape drive)",156:"Level 2 not synchronized"}; var ERRNO_CODES={EPERM:63,ENOENT:44,ESRCH:71,EINTR:27,EIO:29,ENXIO:60,E2BIG:1,ENOEXEC:45,EBADF:8,ECHILD:12,EAGAIN:6,EWOULDBLOCK:6,ENOMEM:48,EACCES:2,EFAULT:21,ENOTBLK:105,EBUSY:10,EEXIST:20,EXDEV:75,ENODEV:43,ENOTDIR:54,EISDIR:31,EINVAL:28,ENFILE:41,EMFILE:33,ENOTTY:59,ETXTBSY:74,EFBIG:22,ENOSPC:51,ESPIPE:70,EROFS:69,EMLINK:34,EPIPE:64,EDOM:18,ERANGE:68,ENOMSG:49,EIDRM:24,ECHRNG:106,EL2NSYNC:156,EL3HLT:107,EL3RST:108,ELNRNG:109,EUNATCH:110,ENOCSI:111,EL2HLT:112,EDEADLK:16,ENOLCK:46,EBADE:113,EBADR:114,EXFULL:115,ENOANO:104,EBADRQC:103,EBADSLT:102,EDEADLOCK:16,EBFONT:101,ENOSTR:100,ENODATA:116,ETIME:117,ENOSR:118,ENONET:119,ENOPKG:120,EREMOTE:121,ENOLINK:47,EADV:122,ESRMNT:123,ECOMM:124,EPROTO:65,EMULTIHOP:36,EDOTDOT:125,EBADMSG:9,ENOTUNIQ:126,EBADFD:127,EREMCHG:128,ELIBACC:129,ELIBBAD:130,ELIBSCN:131,ELIBMAX:132,ELIBEXEC:133,ENOSYS:52,ENOTEMPTY:55,ENAMETOOLONG:37,ELOOP:32,EOPNOTSUPP:138,EPFNOSUPPORT:139,ECONNRESET:15,ENOBUFS:42,EAFNOSUPPORT:5,EPROTOTYPE:67,ENOTSOCK:57,ENOPROTOOPT:50,ESHUTDOWN:140,ECONNREFUSED:14,EADDRINUSE:3,ECONNABORTED:13,ENETUNREACH:40,ENETDOWN:38,ETIMEDOUT:73,EHOSTDOWN:142,EHOSTUNREACH:23,EINPROGRESS:26,EALREADY:7,EDESTADDRREQ:17,EMSGSIZE:35,EPROTONOSUPPORT:66,ESOCKTNOSUPPORT:137,EADDRNOTAVAIL:4,ENETRESET:39,EISCONN:30,ENOTCONN:53,ETOOMANYREFS:141,EUSERS:136,EDQUOT:19,ESTALE:72,ENOTSUP:138,ENOMEDIUM:148,EILSEQ:25,EOVERFLOW:61,ECANCELED:11,ENOTRECOVERABLE:56,EOWNERDEAD:62,ESTRPIPE:135}; var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,trackingDelegate:{},tracking:{openFlags:{READ:1,WRITE:2}},ErrnoError:null,genericErrors:{},filesystems:null,syncFSRequests:0,lookupPath:function(path, opts) { path = PATH_FS.resolve(FS.cwd(), path); opts = opts || {}; if (!path) return { path: '', node: null }; var defaults = { follow_mount: true, recurse_count: 0 }; for (var key in defaults) { if (opts[key] === undefined) { opts[key] = defaults[key]; } } if (opts.recurse_count > 8) { // max recursive lookup of 8 throw new FS.ErrnoError(32); } // split the path var parts = PATH.normalizeArray(path.split('/').filter(function(p) { return !!p; }), false); // start at the root var current = FS.root; var current_path = '/'; for (var i = 0; i < parts.length; i++) { var islast = (i === parts.length-1); if (islast && opts.parent) { // stop resolving break; } current = FS.lookupNode(current, parts[i]); current_path = PATH.join2(current_path, parts[i]); // jump to the mount's root node if this is a mountpoint if (FS.isMountpoint(current)) { if (!islast || (islast && opts.follow_mount)) { current = current.mounted.root; } } // by default, lookupPath will not follow a symlink if it is the final path component. // setting opts.follow = true will override this behavior. if (!islast || opts.follow) { var count = 0; while (FS.isLink(current.mode)) { var link = FS.readlink(current_path); current_path = PATH_FS.resolve(PATH.dirname(current_path), link); var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count }); current = lookup.node; if (count++ > 40) { // limit max consecutive symlinks to 40 (SYMLOOP_MAX). throw new FS.ErrnoError(32); } } } } return { path: current_path, node: current }; },getPath:function(node) { var path; while (true) { if (FS.isRoot(node)) { var mount = node.mount.mountpoint; if (!path) return mount; return mount[mount.length-1] !== '/' ? mount + '/' + path : mount + path; } path = path ? node.name + '/' + path : node.name; node = node.parent; } },hashName:function(parentid, name) { var hash = 0; for (var i = 0; i < name.length; i++) { hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0; } return ((parentid + hash) >>> 0) % FS.nameTable.length; },hashAddNode:function(node) { var hash = FS.hashName(node.parent.id, node.name); node.name_next = FS.nameTable[hash]; FS.nameTable[hash] = node; },hashRemoveNode:function(node) { var hash = FS.hashName(node.parent.id, node.name); if (FS.nameTable[hash] === node) { FS.nameTable[hash] = node.name_next; } else { var current = FS.nameTable[hash]; while (current) { if (current.name_next === node) { current.name_next = node.name_next; break; } current = current.name_next; } } },lookupNode:function(parent, name) { var errCode = FS.mayLookup(parent); if (errCode) { throw new FS.ErrnoError(errCode, parent); } var hash = FS.hashName(parent.id, name); for (var node = FS.nameTable[hash]; node; node = node.name_next) { var nodeName = node.name; if (node.parent.id === parent.id && nodeName === name) { return node; } } // if we failed to find it in the cache, call into the VFS return FS.lookup(parent, name); },createNode:function(parent, name, mode, rdev) { assert(typeof parent === 'object') var node = new FS.FSNode(parent, name, mode, rdev); FS.hashAddNode(node); return node; },destroyNode:function(node) { FS.hashRemoveNode(node); },isRoot:function(node) { return node === node.parent; },isMountpoint:function(node) { return !!node.mounted; },isFile:function(mode) { return (mode & 61440) === 32768; },isDir:function(mode) { return (mode & 61440) === 16384; },isLink:function(mode) { return (mode & 61440) === 40960; },isChrdev:function(mode) { return (mode & 61440) === 8192; },isBlkdev:function(mode) { return (mode & 61440) === 24576; },isFIFO:function(mode) { return (mode & 61440) === 4096; },isSocket:function(mode) { return (mode & 49152) === 49152; },flagModes:{"r":0,"r+":2,"w":577,"w+":578,"a":1089,"a+":1090},modeStringToFlags:function(str) { var flags = FS.flagModes[str]; if (typeof flags === 'undefined') { throw new Error('Unknown file open mode: ' + str); } return flags; },flagsToPermissionString:function(flag) { var perms = ['r', 'w', 'rw'][flag & 3]; if ((flag & 512)) { perms += 'w'; } return perms; },nodePermissions:function(node, perms) { if (FS.ignorePermissions) { return 0; } // return 0 if any user, group or owner bits are set. if (perms.indexOf('r') !== -1 && !(node.mode & 292)) { return 2; } else if (perms.indexOf('w') !== -1 && !(node.mode & 146)) { return 2; } else if (perms.indexOf('x') !== -1 && !(node.mode & 73)) { return 2; } return 0; },mayLookup:function(dir) { var errCode = FS.nodePermissions(dir, 'x'); if (errCode) return errCode; if (!dir.node_ops.lookup) return 2; return 0; },mayCreate:function(dir, name) { try { var node = FS.lookupNode(dir, name); return 20; } catch (e) { } return FS.nodePermissions(dir, 'wx'); },mayDelete:function(dir, name, isdir) { var node; try { node = FS.lookupNode(dir, name); } catch (e) { return e.errno; } var errCode = FS.nodePermissions(dir, 'wx'); if (errCode) { return errCode; } if (isdir) { if (!FS.isDir(node.mode)) { return 54; } if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { return 10; } } else { if (FS.isDir(node.mode)) { return 31; } } return 0; },mayOpen:function(node, flags) { if (!node) { return 44; } if (FS.isLink(node.mode)) { return 32; } else if (FS.isDir(node.mode)) { if (FS.flagsToPermissionString(flags) !== 'r' || // opening for write (flags & 512)) { // TODO: check for O_SEARCH? (== search for dir only) return 31; } } return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); },MAX_OPEN_FDS:4096,nextfd:function(fd_start, fd_end) { fd_start = fd_start || 0; fd_end = fd_end || FS.MAX_OPEN_FDS; for (var fd = fd_start; fd <= fd_end; fd++) { if (!FS.streams[fd]) { return fd; } } throw new FS.ErrnoError(33); },getStream:function(fd) { return FS.streams[fd]; },createStream:function(stream, fd_start, fd_end) { if (!FS.FSStream) { FS.FSStream = /** @constructor */ function(){}; FS.FSStream.prototype = { object: { get: function() { return this.node; }, set: function(val) { this.node = val; } }, isRead: { get: function() { return (this.flags & 2097155) !== 1; } }, isWrite: { get: function() { return (this.flags & 2097155) !== 0; } }, isAppend: { get: function() { return (this.flags & 1024); } } }; } // clone it, so we can return an instance of FSStream var newStream = new FS.FSStream(); for (var p in stream) { newStream[p] = stream[p]; } stream = newStream; var fd = FS.nextfd(fd_start, fd_end); stream.fd = fd; FS.streams[fd] = stream; return stream; },closeStream:function(fd) { FS.streams[fd] = null; },chrdev_stream_ops:{open:function(stream) { var device = FS.getDevice(stream.node.rdev); // override node's stream ops with the device's stream.stream_ops = device.stream_ops; // forward the open call if (stream.stream_ops.open) { stream.stream_ops.open(stream); } },llseek:function() { throw new FS.ErrnoError(70); }},major:function(dev) { return ((dev) >> 8); },minor:function(dev) { return ((dev) & 0xff); },makedev:function(ma, mi) { return ((ma) << 8 | (mi)); },registerDevice:function(dev, ops) { FS.devices[dev] = { stream_ops: ops }; },getDevice:function(dev) { return FS.devices[dev]; },getMounts:function(mount) { var mounts = []; var check = [mount]; while (check.length) { var m = check.pop(); mounts.push(m); check.push.apply(check, m.mounts); } return mounts; },syncfs:function(populate, callback) { if (typeof(populate) === 'function') { callback = populate; populate = false; } FS.syncFSRequests++; if (FS.syncFSRequests > 1) { err('warning: ' + FS.syncFSRequests + ' FS.syncfs operations in flight at once, probably just doing extra work'); } var mounts = FS.getMounts(FS.root.mount); var completed = 0; function doCallback(errCode) { assert(FS.syncFSRequests > 0); FS.syncFSRequests--; return callback(errCode); } function done(errCode) { if (errCode) { if (!done.errored) { done.errored = true; return doCallback(errCode); } return; } if (++completed >= mounts.length) { doCallback(null); } }; // sync all mounts mounts.forEach(function (mount) { if (!mount.type.syncfs) { return done(null); } mount.type.syncfs(mount, populate, done); }); },mount:function(type, opts, mountpoint) { if (typeof type === 'string') { // The filesystem was not included, and instead we have an error // message stored in the variable. throw type; } var root = mountpoint === '/'; var pseudo = !mountpoint; var node; if (root && FS.root) { throw new FS.ErrnoError(10); } else if (!root && !pseudo) { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); mountpoint = lookup.path; // use the absolute path node = lookup.node; if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10); } if (!FS.isDir(node.mode)) { throw new FS.ErrnoError(54); } } var mount = { type: type, opts: opts, mountpoint: mountpoint, mounts: [] }; // create a root node for the fs var mountRoot = type.mount(mount); mountRoot.mount = mount; mount.root = mountRoot; if (root) { FS.root = mountRoot; } else if (node) { // set as a mountpoint node.mounted = mount; // add the new mount to the current mount's children if (node.mount) { node.mount.mounts.push(mount); } } return mountRoot; },unmount:function (mountpoint) { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); if (!FS.isMountpoint(lookup.node)) { throw new FS.ErrnoError(28); } // destroy the nodes for this mount, and all its child mounts var node = lookup.node; var mount = node.mounted; var mounts = FS.getMounts(mount); Object.keys(FS.nameTable).forEach(function (hash) { var current = FS.nameTable[hash]; while (current) { var next = current.name_next; if (mounts.indexOf(current.mount) !== -1) { FS.destroyNode(current); } current = next; } }); // no longer a mountpoint node.mounted = null; // remove this mount from the child mounts var idx = node.mount.mounts.indexOf(mount); assert(idx !== -1); node.mount.mounts.splice(idx, 1); },lookup:function(parent, name) { return parent.node_ops.lookup(parent, name); },mknod:function(path, mode, dev) { var lookup = FS.lookupPath(path, { parent: true }); var parent = lookup.node; var name = PATH.basename(path); if (!name || name === '.' || name === '..') { throw new FS.ErrnoError(28); } var errCode = FS.mayCreate(parent, name); if (errCode) { throw new FS.ErrnoError(errCode); } if (!parent.node_ops.mknod) { throw new FS.ErrnoError(63); } return parent.node_ops.mknod(parent, name, mode, dev); },create:function(path, mode) { mode = mode !== undefined ? mode : 438 /* 0666 */; mode &= 4095; mode |= 32768; return FS.mknod(path, mode, 0); },mkdir:function(path, mode) { mode = mode !== undefined ? mode : 511 /* 0777 */; mode &= 511 | 512; mode |= 16384; return FS.mknod(path, mode, 0); },mkdirTree:function(path, mode) { var dirs = path.split('/'); var d = ''; for (var i = 0; i < dirs.length; ++i) { if (!dirs[i]) continue; d += '/' + dirs[i]; try { FS.mkdir(d, mode); } catch(e) { if (e.errno != 20) throw e; } } },mkdev:function(path, mode, dev) { if (typeof(dev) === 'undefined') { dev = mode; mode = 438 /* 0666 */; } mode |= 8192; return FS.mknod(path, mode, dev); },symlink:function(oldpath, newpath) { if (!PATH_FS.resolve(oldpath)) { throw new FS.ErrnoError(44); } var lookup = FS.lookupPath(newpath, { parent: true }); var parent = lookup.node; if (!parent) { throw new FS.ErrnoError(44); } var newname = PATH.basename(newpath); var errCode = FS.mayCreate(parent, newname); if (errCode) { throw new FS.ErrnoError(errCode); } if (!parent.node_ops.symlink) { throw new FS.ErrnoError(63); } return parent.node_ops.symlink(parent, newname, oldpath); },rename:function(old_path, new_path) { var old_dirname = PATH.dirname(old_path); var new_dirname = PATH.dirname(new_path); var old_name = PATH.basename(old_path); var new_name = PATH.basename(new_path); // parents must exist var lookup, old_dir, new_dir; // let the errors from non existant directories percolate up lookup = FS.lookupPath(old_path, { parent: true }); old_dir = lookup.node; lookup = FS.lookupPath(new_path, { parent: true }); new_dir = lookup.node; if (!old_dir || !new_dir) throw new FS.ErrnoError(44); // need to be part of the same mount if (old_dir.mount !== new_dir.mount) { throw new FS.ErrnoError(75); } // source must exist var old_node = FS.lookupNode(old_dir, old_name); // old path should not be an ancestor of the new path var relative = PATH_FS.relative(old_path, new_dirname); if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(28); } // new path should not be an ancestor of the old path relative = PATH_FS.relative(new_path, old_dirname); if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(55); } // see if the new path already exists var new_node; try { new_node = FS.lookupNode(new_dir, new_name); } catch (e) { // not fatal } // early out if nothing needs to change if (old_node === new_node) { return; } // we'll need to delete the old entry var isdir = FS.isDir(old_node.mode); var errCode = FS.mayDelete(old_dir, old_name, isdir); if (errCode) { throw new FS.ErrnoError(errCode); } // need delete permissions if we'll be overwriting. // need create permissions if new doesn't already exist. errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name); if (errCode) { throw new FS.ErrnoError(errCode); } if (!old_dir.node_ops.rename) { throw new FS.ErrnoError(63); } if (FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node))) { throw new FS.ErrnoError(10); } // if we are going to change the parent, check write permissions if (new_dir !== old_dir) { errCode = FS.nodePermissions(old_dir, 'w'); if (errCode) { throw new FS.ErrnoError(errCode); } } try { if (FS.trackingDelegate['willMovePath']) { FS.trackingDelegate['willMovePath'](old_path, new_path); } } catch(e) { err("FS.trackingDelegate['willMovePath']('"+old_path+"', '"+new_path+"') threw an exception: " + e.message); } // remove the node from the lookup hash FS.hashRemoveNode(old_node); // do the underlying fs rename try { old_dir.node_ops.rename(old_node, new_dir, new_name); } catch (e) { throw e; } finally { // add the node back to the hash (in case node_ops.rename // changed its name) FS.hashAddNode(old_node); } try { if (FS.trackingDelegate['onMovePath']) FS.trackingDelegate['onMovePath'](old_path, new_path); } catch(e) { err("FS.trackingDelegate['onMovePath']('"+old_path+"', '"+new_path+"') threw an exception: " + e.message); } },rmdir:function(path) { var lookup = FS.lookupPath(path, { parent: true }); var parent = lookup.node; var name = PATH.basename(path); var node = FS.lookupNode(parent, name); var errCode = FS.mayDelete(parent, name, true); if (errCode) { throw new FS.ErrnoError(errCode); } if (!parent.node_ops.rmdir) { throw new FS.ErrnoError(63); } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10); } try { if (FS.trackingDelegate['willDeletePath']) { FS.trackingDelegate['willDeletePath'](path); } } catch(e) { err("FS.trackingDelegate['willDeletePath']('"+path+"') threw an exception: " + e.message); } parent.node_ops.rmdir(parent, name); FS.destroyNode(node); try { if (FS.trackingDelegate['onDeletePath']) FS.trackingDelegate['onDeletePath'](path); } catch(e) { err("FS.trackingDelegate['onDeletePath']('"+path+"') threw an exception: " + e.message); } },readdir:function(path) { var lookup = FS.lookupPath(path, { follow: true }); var node = lookup.node; if (!node.node_ops.readdir) { throw new FS.ErrnoError(54); } return node.node_ops.readdir(node); },unlink:function(path) { var lookup = FS.lookupPath(path, { parent: true }); var parent = lookup.node; var name = PATH.basename(path); var node = FS.lookupNode(parent, name); var errCode = FS.mayDelete(parent, name, false); if (errCode) { // According to POSIX, we should map EISDIR to EPERM, but // we instead do what Linux does (and we must, as we use // the musl linux libc). throw new FS.ErrnoError(errCode); } if (!parent.node_ops.unlink) { throw new FS.ErrnoError(63); } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10); } try { if (FS.trackingDelegate['willDeletePath']) { FS.trackingDelegate['willDeletePath'](path); } } catch(e) { err("FS.trackingDelegate['willDeletePath']('"+path+"') threw an exception: " + e.message); } parent.node_ops.unlink(parent, name); FS.destroyNode(node); try { if (FS.trackingDelegate['onDeletePath']) FS.trackingDelegate['onDeletePath'](path); } catch(e) { err("FS.trackingDelegate['onDeletePath']('"+path+"') threw an exception: " + e.message); } },readlink:function(path) { var lookup = FS.lookupPath(path); var link = lookup.node; if (!link) { throw new FS.ErrnoError(44); } if (!link.node_ops.readlink) { throw new FS.ErrnoError(28); } return PATH_FS.resolve(FS.getPath(link.parent), link.node_ops.readlink(link)); },stat:function(path, dontFollow) { var lookup = FS.lookupPath(path, { follow: !dontFollow }); var node = lookup.node; if (!node) { throw new FS.ErrnoError(44); } if (!node.node_ops.getattr) { throw new FS.ErrnoError(63); } return node.node_ops.getattr(node); },lstat:function(path) { return FS.stat(path, true); },chmod:function(path, mode, dontFollow) { var node; if (typeof path === 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }); node = lookup.node; } else { node = path; } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63); } node.node_ops.setattr(node, { mode: (mode & 4095) | (node.mode & ~4095), timestamp: Date.now() }); },lchmod:function(path, mode) { FS.chmod(path, mode, true); },fchmod:function(fd, mode) { var stream = FS.getStream(fd); if (!stream) { throw new FS.ErrnoError(8); } FS.chmod(stream.node, mode); },chown:function(path, uid, gid, dontFollow) { var node; if (typeof path === 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }); node = lookup.node; } else { node = path; } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63); } node.node_ops.setattr(node, { timestamp: Date.now() // we ignore the uid / gid for now }); },lchown:function(path, uid, gid) { FS.chown(path, uid, gid, true); },fchown:function(fd, uid, gid) { var stream = FS.getStream(fd); if (!stream) { throw new FS.ErrnoError(8); } FS.chown(stream.node, uid, gid); },truncate:function(path, len) { if (len < 0) { throw new FS.ErrnoError(28); } var node; if (typeof path === 'string') { var lookup = FS.lookupPath(path, { follow: true }); node = lookup.node; } else { node = path; } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63); } if (FS.isDir(node.mode)) { throw new FS.ErrnoError(31); } if (!FS.isFile(node.mode)) { throw new FS.ErrnoError(28); } var errCode = FS.nodePermissions(node, 'w'); if (errCode) { throw new FS.ErrnoError(errCode); } node.node_ops.setattr(node, { size: len, timestamp: Date.now() }); },ftruncate:function(fd, len) { var stream = FS.getStream(fd); if (!stream) { throw new FS.ErrnoError(8); } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(28); } FS.truncate(stream.node, len); },utime:function(path, atime, mtime) { var lookup = FS.lookupPath(path, { follow: true }); var node = lookup.node; node.node_ops.setattr(node, { timestamp: Math.max(atime, mtime) }); },open:function(path, flags, mode, fd_start, fd_end) { if (path === "") { throw new FS.ErrnoError(44); } flags = typeof flags === 'string' ? FS.modeStringToFlags(flags) : flags; mode = typeof mode === 'undefined' ? 438 /* 0666 */ : mode; if ((flags & 64)) { mode = (mode & 4095) | 32768; } else { mode = 0; } var node; if (typeof path === 'object') { node = path; } else { path = PATH.normalize(path); try { var lookup = FS.lookupPath(path, { follow: !(flags & 131072) }); node = lookup.node; } catch (e) { // ignore } } // perhaps we need to create the node var created = false; if ((flags & 64)) { if (node) { // if O_CREAT and O_EXCL are set, error out if the node already exists if ((flags & 128)) { throw new FS.ErrnoError(20); } } else { // node doesn't exist, try to create it node = FS.mknod(path, mode, 0); created = true; } } if (!node) { throw new FS.ErrnoError(44); } // can't truncate a device if (FS.isChrdev(node.mode)) { flags &= ~512; } // if asked only for a directory, then this must be one if ((flags & 65536) && !FS.isDir(node.mode)) { throw new FS.ErrnoError(54); } // check permissions, if this is not a file we just created now (it is ok to // create and write to a file with read-only permissions; it is read-only // for later use) if (!created) { var errCode = FS.mayOpen(node, flags); if (errCode) { throw new FS.ErrnoError(errCode); } } // do truncation if necessary if ((flags & 512)) { FS.truncate(node, 0); } // we've already handled these, don't pass down to the underlying vfs flags &= ~(128 | 512 | 131072); // register the stream with the filesystem var stream = FS.createStream({ node: node, path: FS.getPath(node), // we want the absolute path to the node flags: flags, seekable: true, position: 0, stream_ops: node.stream_ops, // used by the file family libc calls (fopen, fwrite, ferror, etc.) ungotten: [], error: false }, fd_start, fd_end); // call the new stream's open function if (stream.stream_ops.open) { stream.stream_ops.open(stream); } if (Module['logReadFiles'] && !(flags & 1)) { if (!FS.readFiles) FS.readFiles = {}; if (!(path in FS.readFiles)) { FS.readFiles[path] = 1; err("FS.trackingDelegate error on read file: " + path); } } try { if (FS.trackingDelegate['onOpenFile']) { var trackingFlags = 0; if ((flags & 2097155) !== 1) { trackingFlags |= FS.tracking.openFlags.READ; } if ((flags & 2097155) !== 0) { trackingFlags |= FS.tracking.openFlags.WRITE; } FS.trackingDelegate['onOpenFile'](path, trackingFlags); } } catch(e) { err("FS.trackingDelegate['onOpenFile']('"+path+"', flags) threw an exception: " + e.message); } return stream; },close:function(stream) { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8); } if (stream.getdents) stream.getdents = null; // free readdir state try { if (stream.stream_ops.close) { stream.stream_ops.close(stream); } } catch (e) { throw e; } finally { FS.closeStream(stream.fd); } stream.fd = null; },isClosed:function(stream) { return stream.fd === null; },llseek:function(stream, offset, whence) { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8); } if (!stream.seekable || !stream.stream_ops.llseek) { throw new FS.ErrnoError(70); } if (whence != 0 && whence != 1 && whence != 2) { throw new FS.ErrnoError(28); } stream.position = stream.stream_ops.llseek(stream, offset, whence); stream.ungotten = []; return stream.position; },read:function(stream, buffer, offset, length, position) { if (length < 0 || position < 0) { throw new FS.ErrnoError(28); } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8); } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(8); } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31); } if (!stream.stream_ops.read) { throw new FS.ErrnoError(28); } var seeking = typeof position !== 'undefined'; if (!seeking) { position = stream.position; } else if (!stream.seekable) { throw new FS.ErrnoError(70); } var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position); if (!seeking) stream.position += bytesRead; return bytesRead; },write:function(stream, buffer, offset, length, position, canOwn) { if (length < 0 || position < 0) { throw new FS.ErrnoError(28); } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8); } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8); } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31); } if (!stream.stream_ops.write) { throw new FS.ErrnoError(28); } if (stream.seekable && stream.flags & 1024) { // seek to the end before writing in append mode FS.llseek(stream, 0, 2); } var seeking = typeof position !== 'undefined'; if (!seeking) { position = stream.position; } else if (!stream.seekable) { throw new FS.ErrnoError(70); } var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn); if (!seeking) stream.position += bytesWritten; try { if (stream.path && FS.trackingDelegate['onWriteToFile']) FS.trackingDelegate['onWriteToFile'](stream.path); } catch(e) { err("FS.trackingDelegate['onWriteToFile']('"+stream.path+"') threw an exception: " + e.message); } return bytesWritten; },allocate:function(stream, offset, length) { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8); } if (offset < 0 || length <= 0) { throw new FS.ErrnoError(28); } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8); } if (!FS.isFile(stream.node.mode) && !FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(43); } if (!stream.stream_ops.allocate) { throw new FS.ErrnoError(138); } stream.stream_ops.allocate(stream, offset, length); },mmap:function(stream, address, length, position, prot, flags) { // User requests writing to file (prot & PROT_WRITE != 0). // Checking if we have permissions to write to the file unless // MAP_PRIVATE flag is set. According to POSIX spec it is possible // to write to file opened in read-only mode with MAP_PRIVATE flag, // as all modifications will be visible only in the memory of // the current process. if ((prot & 2) !== 0 && (flags & 2) === 0 && (stream.flags & 2097155) !== 2) { throw new FS.ErrnoError(2); } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(2); } if (!stream.stream_ops.mmap) { throw new FS.ErrnoError(43); } return stream.stream_ops.mmap(stream, address, length, position, prot, flags); },msync:function(stream, buffer, offset, length, mmapFlags) { if (!stream || !stream.stream_ops.msync) { return 0; } return stream.stream_ops.msync(stream, buffer, offset, length, mmapFlags); },munmap:function(stream) { return 0; },ioctl:function(stream, cmd, arg) { if (!stream.stream_ops.ioctl) { throw new FS.ErrnoError(59); } return stream.stream_ops.ioctl(stream, cmd, arg); },readFile:function(path, opts) { opts = opts || {}; opts.flags = opts.flags || 0; opts.encoding = opts.encoding || 'binary'; if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { throw new Error('Invalid encoding type "' + opts.encoding + '"'); } var ret; var stream = FS.open(path, opts.flags); var stat = FS.stat(path); var length = stat.size; var buf = new Uint8Array(length); FS.read(stream, buf, 0, length, 0); if (opts.encoding === 'utf8') { ret = UTF8ArrayToString(buf, 0); } else if (opts.encoding === 'binary') { ret = buf; } FS.close(stream); return ret; },writeFile:function(path, data, opts) { opts = opts || {}; opts.flags = opts.flags || 577; var stream = FS.open(path, opts.flags, opts.mode); if (typeof data === 'string') { var buf = new Uint8Array(lengthBytesUTF8(data)+1); var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length); FS.write(stream, buf, 0, actualNumBytes, undefined, opts.canOwn); } else if (ArrayBuffer.isView(data)) { FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn); } else { throw new Error('Unsupported data type'); } FS.close(stream); },cwd:function() { return FS.currentPath; },chdir:function(path) { var lookup = FS.lookupPath(path, { follow: true }); if (lookup.node === null) { throw new FS.ErrnoError(44); } if (!FS.isDir(lookup.node.mode)) { throw new FS.ErrnoError(54); } var errCode = FS.nodePermissions(lookup.node, 'x'); if (errCode) { throw new FS.ErrnoError(errCode); } FS.currentPath = lookup.path; },createDefaultDirectories:function() { FS.mkdir('/tmp'); FS.mkdir('/home'); FS.mkdir('/home/web_user'); },createDefaultDevices:function() { // create /dev FS.mkdir('/dev'); // setup /dev/null FS.registerDevice(FS.makedev(1, 3), { read: function() { return 0; }, write: function(stream, buffer, offset, length, pos) { return length; } }); FS.mkdev('/dev/null', FS.makedev(1, 3)); // setup /dev/tty and /dev/tty1 // stderr needs to print output using err() rather than out() // so we register a second tty just for it. TTY.register(FS.makedev(5, 0), TTY.default_tty_ops); TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops); FS.mkdev('/dev/tty', FS.makedev(5, 0)); FS.mkdev('/dev/tty1', FS.makedev(6, 0)); // setup /dev/[u]random var random_device = getRandomDevice(); FS.createDevice('/dev', 'random', random_device); FS.createDevice('/dev', 'urandom', random_device); // we're not going to emulate the actual shm device, // just create the tmp dirs that reside in it commonly FS.mkdir('/dev/shm'); FS.mkdir('/dev/shm/tmp'); },createSpecialDirectories:function() { // create /proc/self/fd which allows /proc/self/fd/6 => readlink gives the // name of the stream for fd 6 (see test_unistd_ttyname) FS.mkdir('/proc'); var proc_self = FS.mkdir('/proc/self'); FS.mkdir('/proc/self/fd'); FS.mount({ mount: function() { var node = FS.createNode(proc_self, 'fd', 16384 | 511 /* 0777 */, 73); node.node_ops = { lookup: function(parent, name) { var fd = +name; var stream = FS.getStream(fd); if (!stream) throw new FS.ErrnoError(8); var ret = { parent: null, mount: { mountpoint: 'fake' }, node_ops: { readlink: function() { return stream.path } } }; ret.parent = ret; // make it look like a simple root node return ret; } }; return node; } }, {}, '/proc/self/fd'); },createStandardStreams:function() { // TODO deprecate the old functionality of a single // input / output callback and that utilizes FS.createDevice // and instead require a unique set of stream ops // by default, we symlink the standard streams to the // default tty devices. however, if the standard streams // have been overwritten we create a unique device for // them instead. if (Module['stdin']) { FS.createDevice('/dev', 'stdin', Module['stdin']); } else { FS.symlink('/dev/tty', '/dev/stdin'); } if (Module['stdout']) { FS.createDevice('/dev', 'stdout', null, Module['stdout']); } else { FS.symlink('/dev/tty', '/dev/stdout'); } if (Module['stderr']) { FS.createDevice('/dev', 'stderr', null, Module['stderr']); } else { FS.symlink('/dev/tty1', '/dev/stderr'); } // open default streams for the stdin, stdout and stderr devices var stdin = FS.open('/dev/stdin', 0); var stdout = FS.open('/dev/stdout', 1); var stderr = FS.open('/dev/stderr', 1); assert(stdin.fd === 0, 'invalid handle for stdin (' + stdin.fd + ')'); assert(stdout.fd === 1, 'invalid handle for stdout (' + stdout.fd + ')'); assert(stderr.fd === 2, 'invalid handle for stderr (' + stderr.fd + ')'); },ensureErrnoError:function() { if (FS.ErrnoError) return; FS.ErrnoError = /** @this{Object} */ function ErrnoError(errno, node) { this.node = node; this.setErrno = /** @this{Object} */ function(errno) { this.errno = errno; for (var key in ERRNO_CODES) { if (ERRNO_CODES[key] === errno) { this.code = key; break; } } }; this.setErrno(errno); this.message = ERRNO_MESSAGES[errno]; // Try to get a maximally helpful stack trace. On Node.js, getting Error.stack // now ensures it shows what we want. if (this.stack) { // Define the stack property for Node.js 4, which otherwise errors on the next line. Object.defineProperty(this, "stack", { value: (new Error).stack, writable: true }); this.stack = demangleAll(this.stack); } }; FS.ErrnoError.prototype = new Error(); FS.ErrnoError.prototype.constructor = FS.ErrnoError; // Some errors may happen quite a bit, to avoid overhead we reuse them (and suffer a lack of stack info) [44].forEach(function(code) { FS.genericErrors[code] = new FS.ErrnoError(code); FS.genericErrors[code].stack = ''; }); },staticInit:function() { FS.ensureErrnoError(); FS.nameTable = new Array(4096); FS.mount(MEMFS, {}, '/'); FS.createDefaultDirectories(); FS.createDefaultDevices(); FS.createSpecialDirectories(); FS.filesystems = { 'MEMFS': MEMFS, }; },init:function(input, output, error) { assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)'); FS.init.initialized = true; FS.ensureErrnoError(); // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here Module['stdin'] = input || Module['stdin']; Module['stdout'] = output || Module['stdout']; Module['stderr'] = error || Module['stderr']; FS.createStandardStreams(); },quit:function() { FS.init.initialized = false; // force-flush all streams, so we get musl std streams printed out var fflush = Module['_fflush']; if (fflush) fflush(0); // close all of our streams for (var i = 0; i < FS.streams.length; i++) { var stream = FS.streams[i]; if (!stream) { continue; } FS.close(stream); } },getMode:function(canRead, canWrite) { var mode = 0; if (canRead) mode |= 292 | 73; if (canWrite) mode |= 146; return mode; },findObject:function(path, dontResolveLastLink) { var ret = FS.analyzePath(path, dontResolveLastLink); if (ret.exists) { return ret.object; } else { return null; } },analyzePath:function(path, dontResolveLastLink) { // operate from within the context of the symlink's target try { var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); path = lookup.path; } catch (e) { } var ret = { isRoot: false, exists: false, error: 0, name: null, path: null, object: null, parentExists: false, parentPath: null, parentObject: null }; try { var lookup = FS.lookupPath(path, { parent: true }); ret.parentExists = true; ret.parentPath = lookup.path; ret.parentObject = lookup.node; ret.name = PATH.basename(path); lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); ret.exists = true; ret.path = lookup.path; ret.object = lookup.node; ret.name = lookup.node.name; ret.isRoot = lookup.path === '/'; } catch (e) { ret.error = e.errno; }; return ret; },createPath:function(parent, path, canRead, canWrite) { parent = typeof parent === 'string' ? parent : FS.getPath(parent); var parts = path.split('/').reverse(); while (parts.length) { var part = parts.pop(); if (!part) continue; var current = PATH.join2(parent, part); try { FS.mkdir(current); } catch (e) { // ignore EEXIST } parent = current; } return current; },createFile:function(parent, name, properties, canRead, canWrite) { var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); var mode = FS.getMode(canRead, canWrite); return FS.create(path, mode); },createDataFile:function(parent, name, data, canRead, canWrite, canOwn) { var path = name ? PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name) : parent; var mode = FS.getMode(canRead, canWrite); var node = FS.create(path, mode); if (data) { if (typeof data === 'string') { var arr = new Array(data.length); for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i); data = arr; } // make sure we can write to the file FS.chmod(node, mode | 146); var stream = FS.open(node, 577); FS.write(stream, data, 0, data.length, 0, canOwn); FS.close(stream); FS.chmod(node, mode); } return node; },createDevice:function(parent, name, input, output) { var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); var mode = FS.getMode(!!input, !!output); if (!FS.createDevice.major) FS.createDevice.major = 64; var dev = FS.makedev(FS.createDevice.major++, 0); // Create a fake device that a set of stream ops to emulate // the old behavior. FS.registerDevice(dev, { open: function(stream) { stream.seekable = false; }, close: function(stream) { // flush any pending line data if (output && output.buffer && output.buffer.length) { output(10); } }, read: function(stream, buffer, offset, length, pos /* ignored */) { var bytesRead = 0; for (var i = 0; i < length; i++) { var result; try { result = input(); } catch (e) { throw new FS.ErrnoError(29); } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6); } if (result === null || result === undefined) break; bytesRead++; buffer[offset+i] = result; } if (bytesRead) { stream.node.timestamp = Date.now(); } return bytesRead; }, write: function(stream, buffer, offset, length, pos) { for (var i = 0; i < length; i++) { try { output(buffer[offset+i]); } catch (e) { throw new FS.ErrnoError(29); } } if (length) { stream.node.timestamp = Date.now(); } return i; } }); return FS.mkdev(path, mode, dev); },forceLoadFile:function(obj) { if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; if (typeof XMLHttpRequest !== 'undefined') { throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); } else if (read_) { // Command-line. try { // WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as // read() will try to parse UTF8. obj.contents = intArrayFromString(read_(obj.url), true); obj.usedBytes = obj.contents.length; } catch (e) { throw new FS.ErrnoError(29); } } else { throw new Error('Cannot load without read() or XMLHttpRequest.'); } },createLazyFile:function(parent, name, url, canRead, canWrite) { // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse. /** @constructor */ function LazyUint8Array() { this.lengthKnown = false; this.chunks = []; // Loaded chunks. Index is the chunk number } LazyUint8Array.prototype.get = /** @this{Object} */ function LazyUint8Array_get(idx) { if (idx > this.length-1 || idx < 0) { return undefined; } var chunkOffset = idx % this.chunkSize; var chunkNum = (idx / this.chunkSize)|0; return this.getter(chunkNum)[chunkOffset]; }; LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter(getter) { this.getter = getter; }; LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() { // Find length var xhr = new XMLHttpRequest(); xhr.open('HEAD', url, false); xhr.send(null); if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); var datalength = Number(xhr.getResponseHeader("Content-length")); var header; var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes"; var usesGzip = (header = xhr.getResponseHeader("Content-Encoding")) && header === "gzip"; var chunkSize = 1024*1024; // Chunk size in bytes if (!hasByteServing) chunkSize = datalength; // Function to get a range from the remote URL. var doXHR = (function(from, to) { if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!"); if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!"); // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to); // Some hints to the browser that we want binary data. if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer'; if (xhr.overrideMimeType) { xhr.overrideMimeType('text/plain; charset=x-user-defined'); } xhr.send(null); if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); if (xhr.response !== undefined) { return new Uint8Array(/** @type{Array} */(xhr.response || [])); } else { return intArrayFromString(xhr.responseText || '', true); } }); var lazyArray = this; lazyArray.setDataGetter(function(chunkNum) { var start = chunkNum * chunkSize; var end = (chunkNum+1) * chunkSize - 1; // including this byte end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block if (typeof(lazyArray.chunks[chunkNum]) === "undefined") { lazyArray.chunks[chunkNum] = doXHR(start, end); } if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!"); return lazyArray.chunks[chunkNum]; }); if (usesGzip || !datalength) { // if the server uses gzip or doesn't supply the length, we have to download the whole file to get the (uncompressed) length chunkSize = datalength = 1; // this will force getter(0)/doXHR do download the whole file datalength = this.getter(0).length; chunkSize = datalength; out("LazyFiles on gzip forces download of the whole file when length is accessed"); } this._length = datalength; this._chunkSize = chunkSize; this.lengthKnown = true; }; if (typeof XMLHttpRequest !== 'undefined') { if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc'; var lazyArray = new LazyUint8Array(); Object.defineProperties(lazyArray, { length: { get: /** @this{Object} */ function() { if(!this.lengthKnown) { this.cacheLength(); } return this._length; } }, chunkSize: { get: /** @this{Object} */ function() { if(!this.lengthKnown) { this.cacheLength(); } return this._chunkSize; } } }); var properties = { isDevice: false, contents: lazyArray }; } else { var properties = { isDevice: false, url: url }; } var node = FS.createFile(parent, name, properties, canRead, canWrite); // This is a total hack, but I want to get this lazy file code out of the // core of MEMFS. If we want to keep this lazy file concept I feel it should // be its own thin LAZYFS proxying calls to MEMFS. if (properties.contents) { node.contents = properties.contents; } else if (properties.url) { node.contents = null; node.url = properties.url; } // Add a function that defers querying the file size until it is asked the first time. Object.defineProperties(node, { usedBytes: { get: /** @this {FSNode} */ function() { return this.contents.length; } } }); // override each stream op with one that tries to force load the lazy file first var stream_ops = {}; var keys = Object.keys(node.stream_ops); keys.forEach(function(key) { var fn = node.stream_ops[key]; stream_ops[key] = function forceLoadLazyFile() { FS.forceLoadFile(node); return fn.apply(null, arguments); }; }); // use a custom read function stream_ops.read = function stream_ops_read(stream, buffer, offset, length, position) { FS.forceLoadFile(node); var contents = stream.node.contents; if (position >= contents.length) return 0; var size = Math.min(contents.length - position, length); assert(size >= 0); if (contents.slice) { // normal array for (var i = 0; i < size; i++) { buffer[offset + i] = contents[position + i]; } } else { for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR buffer[offset + i] = contents.get(position + i); } } return size; }; node.stream_ops = stream_ops; return node; },createPreloadedFile:function(parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) { Browser.init(); // XXX perhaps this method should move onto Browser? // TODO we should allow people to just pass in a complete filename instead // of parent and name being that we just join them anyways var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent; var dep = getUniqueRunDependency('cp ' + fullname); // might have several active requests for the same fullname function processData(byteArray) { function finish(byteArray) { if (preFinish) preFinish(); if (!dontCreateFile) { FS.createDataFile(parent, name, byteArray, canRead, canWrite, canOwn); } if (onload) onload(); removeRunDependency(dep); } var handled = false; Module['preloadPlugins'].forEach(function(plugin) { if (handled) return; if (plugin['canHandle'](fullname)) { plugin['handle'](byteArray, fullname, finish, function() { if (onerror) onerror(); removeRunDependency(dep); }); handled = true; } }); if (!handled) finish(byteArray); } addRunDependency(dep); if (typeof url == 'string') { Browser.asyncLoad(url, function(byteArray) { processData(byteArray); }, onerror); } else { processData(url); } },indexedDB:function() { return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; },DB_NAME:function() { return 'EM_FS_' + window.location.pathname; },DB_VERSION:20,DB_STORE_NAME:"FILE_DATA",saveFilesToDB:function(paths, onload, onerror) { onload = onload || function(){}; onerror = onerror || function(){}; var indexedDB = FS.indexedDB(); try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION); } catch (e) { return onerror(e); } openRequest.onupgradeneeded = function openRequest_onupgradeneeded() { out('creating db'); var db = openRequest.result; db.createObjectStore(FS.DB_STORE_NAME); }; openRequest.onsuccess = function openRequest_onsuccess() { var db = openRequest.result; var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite'); var files = transaction.objectStore(FS.DB_STORE_NAME); var ok = 0, fail = 0, total = paths.length; function finish() { if (fail == 0) onload(); else onerror(); } paths.forEach(function(path) { var putRequest = files.put(FS.analyzePath(path).object.contents, path); putRequest.onsuccess = function putRequest_onsuccess() { ok++; if (ok + fail == total) finish() }; putRequest.onerror = function putRequest_onerror() { fail++; if (ok + fail == total) finish() }; }); transaction.onerror = onerror; }; openRequest.onerror = onerror; },loadFilesFromDB:function(paths, onload, onerror) { onload = onload || function(){}; onerror = onerror || function(){}; var indexedDB = FS.indexedDB(); try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION); } catch (e) { return onerror(e); } openRequest.onupgradeneeded = onerror; // no database to load from openRequest.onsuccess = function openRequest_onsuccess() { var db = openRequest.result; try { var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly'); } catch(e) { onerror(e); return; } var files = transaction.objectStore(FS.DB_STORE_NAME); var ok = 0, fail = 0, total = paths.length; function finish() { if (fail == 0) onload(); else onerror(); } paths.forEach(function(path) { var getRequest = files.get(path); getRequest.onsuccess = function getRequest_onsuccess() { if (FS.analyzePath(path).exists) { FS.unlink(path); } FS.createDataFile(PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true); ok++; if (ok + fail == total) finish(); }; getRequest.onerror = function getRequest_onerror() { fail++; if (ok + fail == total) finish() }; }); transaction.onerror = onerror; }; openRequest.onerror = onerror; },absolutePath:function() { abort('FS.absolutePath has been removed; use PATH_FS.resolve instead'); },createFolder:function() { abort('FS.createFolder has been removed; use FS.mkdir instead'); },createLink:function() { abort('FS.createLink has been removed; use FS.symlink instead'); },joinPath:function() { abort('FS.joinPath has been removed; use PATH.join instead'); },mmapAlloc:function() { abort('FS.mmapAlloc has been replaced by the top level function mmapAlloc'); },standardizePath:function() { abort('FS.standardizePath has been removed; use PATH.normalize instead'); }}; var SYSCALLS={mappings:{},DEFAULT_POLLMASK:5,umask:511,calculateAt:function(dirfd, path, allowEmpty) { if (path[0] === '/') { return path; } // relative path var dir; if (dirfd === -100) { dir = FS.cwd(); } else { var dirstream = FS.getStream(dirfd); if (!dirstream) throw new FS.ErrnoError(8); dir = dirstream.path; } if (path.length == 0) { if (!allowEmpty) { throw new FS.ErrnoError(44);; } return dir; } return PATH.join2(dir, path); },doStat:function(func, path, buf) { try { var stat = func(path); } catch (e) { if (e && e.node && PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node))) { // an error occurred while trying to look up the path; we should just report ENOTDIR return -54; } throw e; } HEAP32[((buf)>>2)] = stat.dev; HEAP32[(((buf)+(4))>>2)] = 0; HEAP32[(((buf)+(8))>>2)] = stat.ino; HEAP32[(((buf)+(12))>>2)] = stat.mode; HEAP32[(((buf)+(16))>>2)] = stat.nlink; HEAP32[(((buf)+(20))>>2)] = stat.uid; HEAP32[(((buf)+(24))>>2)] = stat.gid; HEAP32[(((buf)+(28))>>2)] = stat.rdev; HEAP32[(((buf)+(32))>>2)] = 0; (tempI64 = [stat.size>>>0,(tempDouble=stat.size,(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math.min((+(Math.floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[(((buf)+(40))>>2)] = tempI64[0],HEAP32[(((buf)+(44))>>2)] = tempI64[1]); HEAP32[(((buf)+(48))>>2)] = 4096; HEAP32[(((buf)+(52))>>2)] = stat.blocks; HEAP32[(((buf)+(56))>>2)] = (stat.atime.getTime() / 1000)|0; HEAP32[(((buf)+(60))>>2)] = 0; HEAP32[(((buf)+(64))>>2)] = (stat.mtime.getTime() / 1000)|0; HEAP32[(((buf)+(68))>>2)] = 0; HEAP32[(((buf)+(72))>>2)] = (stat.ctime.getTime() / 1000)|0; HEAP32[(((buf)+(76))>>2)] = 0; (tempI64 = [stat.ino>>>0,(tempDouble=stat.ino,(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math.min((+(Math.floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[(((buf)+(80))>>2)] = tempI64[0],HEAP32[(((buf)+(84))>>2)] = tempI64[1]); return 0; },doMsync:function(addr, stream, len, flags, offset) { var buffer = HEAPU8.slice(addr, addr + len); FS.msync(stream, buffer, offset, len, flags); },doMkdir:function(path, mode) { // remove a trailing slash, if one - /a/b/ has basename of '', but // we want to create b in the context of this function path = PATH.normalize(path); if (path[path.length-1] === '/') path = path.substr(0, path.length-1); FS.mkdir(path, mode, 0); return 0; },doMknod:function(path, mode, dev) { // we don't want this in the JS API as it uses mknod to create all nodes. switch (mode & 61440) { case 32768: case 8192: case 24576: case 4096: case 49152: break; default: return -28; } FS.mknod(path, mode, dev); return 0; },doReadlink:function(path, buf, bufsize) { if (bufsize <= 0) return -28; var ret = FS.readlink(path); var len = Math.min(bufsize, lengthBytesUTF8(ret)); var endChar = HEAP8[buf+len]; stringToUTF8(ret, buf, bufsize+1); // readlink is one of the rare functions that write out a C string, but does never append a null to the output buffer(!) // stringToUTF8() always appends a null byte, so restore the character under the null byte after the write. HEAP8[buf+len] = endChar; return len; },doAccess:function(path, amode) { if (amode & ~7) { // need a valid mode return -28; } var node; var lookup = FS.lookupPath(path, { follow: true }); node = lookup.node; if (!node) { return -44; } var perms = ''; if (amode & 4) perms += 'r'; if (amode & 2) perms += 'w'; if (amode & 1) perms += 'x'; if (perms /* otherwise, they've just passed F_OK */ && FS.nodePermissions(node, perms)) { return -2; } return 0; },doDup:function(path, flags, suggestFD) { var suggest = FS.getStream(suggestFD); if (suggest) FS.close(suggest); return FS.open(path, flags, 0, suggestFD, suggestFD).fd; },doReadv:function(stream, iov, iovcnt, offset) { var ret = 0; for (var i = 0; i < iovcnt; i++) { var ptr = HEAP32[(((iov)+(i*8))>>2)]; var len = HEAP32[(((iov)+(i*8 + 4))>>2)]; var curr = FS.read(stream, HEAP8,ptr, len, offset); if (curr < 0) return -1; ret += curr; if (curr < len) break; // nothing more to read } return ret; },doWritev:function(stream, iov, iovcnt, offset) { var ret = 0; for (var i = 0; i < iovcnt; i++) { var ptr = HEAP32[(((iov)+(i*8))>>2)]; var len = HEAP32[(((iov)+(i*8 + 4))>>2)]; var curr = FS.write(stream, HEAP8,ptr, len, offset); if (curr < 0) return -1; ret += curr; } return ret; },varargs:undefined,get:function() { assert(SYSCALLS.varargs != undefined); SYSCALLS.varargs += 4; var ret = HEAP32[(((SYSCALLS.varargs)-(4))>>2)]; return ret; },getStr:function(ptr) { var ret = UTF8ToString(ptr); return ret; },getStreamFromFD:function(fd) { var stream = FS.getStream(fd); if (!stream) throw new FS.ErrnoError(8); return stream; },get64:function(low, high) { if (low >= 0) assert(high === 0); else assert(high === -1); return low; }}; function ___sys_fcntl64(fd, cmd, varargs) {SYSCALLS.varargs = varargs; try { var stream = SYSCALLS.getStreamFromFD(fd); switch (cmd) { case 0: { var arg = SYSCALLS.get(); if (arg < 0) { return -28; } var newStream; newStream = FS.open(stream.path, stream.flags, 0, arg); return newStream.fd; } case 1: case 2: return 0; // FD_CLOEXEC makes no sense for a single process. case 3: return stream.flags; case 4: { var arg = SYSCALLS.get(); stream.flags |= arg; return 0; } case 12: /* case 12: Currently in musl F_GETLK64 has same value as F_GETLK, so omitted to avoid duplicate case blocks. If that changes, uncomment this */ { var arg = SYSCALLS.get(); var offset = 0; // We're always unlocked. HEAP16[(((arg)+(offset))>>1)] = 2; return 0; } case 13: case 14: /* case 13: Currently in musl F_SETLK64 has same value as F_SETLK, so omitted to avoid duplicate case blocks. If that changes, uncomment this */ /* case 14: Currently in musl F_SETLKW64 has same value as F_SETLKW, so omitted to avoid duplicate case blocks. If that changes, uncomment this */ return 0; // Pretend that the locking is successful. case 16: case 8: return -28; // These are for sockets. We don't have them fully implemented yet. case 9: // musl trusts getown return values, due to a bug where they must be, as they overlap with errors. just return -1 here, so fnctl() returns that, and we set errno ourselves. setErrNo(28); return -1; default: { return -28; } } } catch (e) { if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); return -e.errno; } } function ___sys_ioctl(fd, op, varargs) {SYSCALLS.varargs = varargs; try { var stream = SYSCALLS.getStreamFromFD(fd); switch (op) { case 21509: case 21505: { if (!stream.tty) return -59; return 0; } case 21510: case 21511: case 21512: case 21506: case 21507: case 21508: { if (!stream.tty) return -59; return 0; // no-op, not actually adjusting terminal settings } case 21519: { if (!stream.tty) return -59; var argp = SYSCALLS.get(); HEAP32[((argp)>>2)] = 0; return 0; } case 21520: { if (!stream.tty) return -59; return -28; // not supported } case 21531: { var argp = SYSCALLS.get(); return FS.ioctl(stream, op, argp); } case 21523: { // TODO: in theory we should write to the winsize struct that gets // passed in, but for now musl doesn't read anything on it if (!stream.tty) return -59; return 0; } case 21524: { // TODO: technically, this ioctl call should change the window size. // but, since emscripten doesn't have any concept of a terminal window // yet, we'll just silently throw it away as we do TIOCGWINSZ if (!stream.tty) return -59; return 0; } default: abort('bad ioctl syscall ' + op); } } catch (e) { if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); return -e.errno; } } function ___sys_open(path, flags, varargs) {SYSCALLS.varargs = varargs; try { var pathname = SYSCALLS.getStr(path); var mode = varargs ? SYSCALLS.get() : 0; var stream = FS.open(pathname, flags, mode); return stream.fd; } catch (e) { if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); return -e.errno; } } var _emscripten_get_now;if (ENVIRONMENT_IS_NODE) { _emscripten_get_now = function() { var t = process['hrtime'](); return t[0] * 1e3 + t[1] / 1e6; }; } else if (typeof dateNow !== 'undefined') { _emscripten_get_now = dateNow; } else _emscripten_get_now = function() { return performance.now(); } ; var _emscripten_get_now_is_monotonic=true;; function _clock_gettime(clk_id, tp) { // int clock_gettime(clockid_t clk_id, struct timespec *tp); var now; if (clk_id === 0) { now = Date.now(); } else if ((clk_id === 1 || clk_id === 4) && _emscripten_get_now_is_monotonic) { now = _emscripten_get_now(); } else { setErrNo(28); return -1; } HEAP32[((tp)>>2)] = (now/1000)|0; // seconds HEAP32[(((tp)+(4))>>2)] = ((now % 1000)*1000*1000)|0; // nanoseconds return 0; } function _dlclose(handle) { abort("To use dlopen, you need to use Emscripten's linking support, see https://github.com/emscripten-core/emscripten/wiki/Linking"); } function _emscripten_set_main_loop_timing(mode, value) { Browser.mainLoop.timingMode = mode; Browser.mainLoop.timingValue = value; if (!Browser.mainLoop.func) { console.error('emscripten_set_main_loop_timing: Cannot set timing mode for main loop since a main loop does not exist! Call emscripten_set_main_loop first to set one up.'); return 1; // Return non-zero on failure, can't set timing mode when there is no main loop. } if (mode == 0 /*EM_TIMING_SETTIMEOUT*/) { Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_setTimeout() { var timeUntilNextTick = Math.max(0, Browser.mainLoop.tickStartTime + value - _emscripten_get_now())|0; setTimeout(Browser.mainLoop.runner, timeUntilNextTick); // doing this each time means that on exception, we stop }; Browser.mainLoop.method = 'timeout'; } else if (mode == 1 /*EM_TIMING_RAF*/) { Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_rAF() { Browser.requestAnimationFrame(Browser.mainLoop.runner); }; Browser.mainLoop.method = 'rAF'; } else if (mode == 2 /*EM_TIMING_SETIMMEDIATE*/) { if (typeof setImmediate === 'undefined') { // Emulate setImmediate. (note: not a complete polyfill, we don't emulate clearImmediate() to keep code size to minimum, since not needed) var setImmediates = []; var emscriptenMainLoopMessageId = 'setimmediate'; var Browser_setImmediate_messageHandler = function(event) { // When called in current thread or Worker, the main loop ID is structured slightly different to accommodate for --proxy-to-worker runtime listening to Worker events, // so check for both cases. if (event.data === emscriptenMainLoopMessageId || event.data.target === emscriptenMainLoopMessageId) { event.stopPropagation(); setImmediates.shift()(); } } addEventListener("message", Browser_setImmediate_messageHandler, true); setImmediate = /** @type{function(function(): ?, ...?): number} */(function Browser_emulated_setImmediate(func) { setImmediates.push(func); if (ENVIRONMENT_IS_WORKER) { if (Module['setImmediates'] === undefined) Module['setImmediates'] = []; Module['setImmediates'].push(func); postMessage({target: emscriptenMainLoopMessageId}); // In --proxy-to-worker, route the message via proxyClient.js } else postMessage(emscriptenMainLoopMessageId, "*"); // On the main thread, can just send the message to itself. }) } Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_setImmediate() { setImmediate(Browser.mainLoop.runner); }; Browser.mainLoop.method = 'immediate'; } return 0; } function setMainLoop(browserIterationFunc, fps, simulateInfiniteLoop, arg, noSetTiming) { noExitRuntime = true; assert(!Browser.mainLoop.func, 'emscripten_set_main_loop: there can only be one main loop function at once: call emscripten_cancel_main_loop to cancel the previous one before setting a new one with different parameters.'); Browser.mainLoop.func = browserIterationFunc; Browser.mainLoop.arg = arg; var thisMainLoopId = Browser.mainLoop.currentlyRunningMainloop; Browser.mainLoop.runner = function Browser_mainLoop_runner() { if (ABORT) return; if (Browser.mainLoop.queue.length > 0) { var start = Date.now(); var blocker = Browser.mainLoop.queue.shift(); blocker.func(blocker.arg); if (Browser.mainLoop.remainingBlockers) { var remaining = Browser.mainLoop.remainingBlockers; var next = remaining%1 == 0 ? remaining-1 : Math.floor(remaining); if (blocker.counted) { Browser.mainLoop.remainingBlockers = next; } else { // not counted, but move the progress along a tiny bit next = next + 0.5; // do not steal all the next one's progress Browser.mainLoop.remainingBlockers = (8*remaining + next)/9; } } console.log('main loop blocker "' + blocker.name + '" took ' + (Date.now() - start) + ' ms'); //, left: ' + Browser.mainLoop.remainingBlockers); Browser.mainLoop.updateStatus(); // catches pause/resume main loop from blocker execution if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return; setTimeout(Browser.mainLoop.runner, 0); return; } // catch pauses from non-main loop sources if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return; // Implement very basic swap interval control Browser.mainLoop.currentFrameNumber = Browser.mainLoop.currentFrameNumber + 1 | 0; if (Browser.mainLoop.timingMode == 1/*EM_TIMING_RAF*/ && Browser.mainLoop.timingValue > 1 && Browser.mainLoop.currentFrameNumber % Browser.mainLoop.timingValue != 0) { // Not the scheduled time to render this frame - skip. Browser.mainLoop.scheduler(); return; } else if (Browser.mainLoop.timingMode == 0/*EM_TIMING_SETTIMEOUT*/) { Browser.mainLoop.tickStartTime = _emscripten_get_now(); } // Signal GL rendering layer that processing of a new frame is about to start. This helps it optimize // VBO double-buffering and reduce GPU stalls. if (Browser.mainLoop.method === 'timeout' && Module.ctx) { warnOnce('Looks like you are rendering without using requestAnimationFrame for the main loop. You should use 0 for the frame rate in emscripten_set_main_loop in order to use requestAnimationFrame, as that can greatly improve your frame rates!'); Browser.mainLoop.method = ''; // just warn once per call to set main loop } Browser.mainLoop.runIter(browserIterationFunc); checkStackCookie(); // catch pauses from the main loop itself if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return; // Queue new audio data. This is important to be right after the main loop invocation, so that we will immediately be able // to queue the newest produced audio samples. // TODO: Consider adding pre- and post- rAF callbacks so that GL.newRenderingFrameStarted() and SDL.audio.queueNewAudioData() // do not need to be hardcoded into this function, but can be more generic. if (typeof SDL === 'object' && SDL.audio && SDL.audio.queueNewAudioData) SDL.audio.queueNewAudioData(); Browser.mainLoop.scheduler(); } if (!noSetTiming) { if (fps && fps > 0) _emscripten_set_main_loop_timing(0/*EM_TIMING_SETTIMEOUT*/, 1000.0 / fps); else _emscripten_set_main_loop_timing(1/*EM_TIMING_RAF*/, 1); // Do rAF by rendering each frame (no decimating) Browser.mainLoop.scheduler(); } if (simulateInfiniteLoop) { throw 'unwind'; } } var Browser={mainLoop:{scheduler:null,method:"",currentlyRunningMainloop:0,func:null,arg:0,timingMode:0,timingValue:0,currentFrameNumber:0,queue:[],pause:function() { Browser.mainLoop.scheduler = null; // Incrementing this signals the previous main loop that it's now become old, and it must return. Browser.mainLoop.currentlyRunningMainloop++; },resume:function() { Browser.mainLoop.currentlyRunningMainloop++; var timingMode = Browser.mainLoop.timingMode; var timingValue = Browser.mainLoop.timingValue; var func = Browser.mainLoop.func; Browser.mainLoop.func = null; // do not set timing and call scheduler, we will do it on the next lines setMainLoop(func, 0, false, Browser.mainLoop.arg, true); _emscripten_set_main_loop_timing(timingMode, timingValue); Browser.mainLoop.scheduler(); },updateStatus:function() { if (Module['setStatus']) { var message = Module['statusMessage'] || 'Please wait...'; var remaining = Browser.mainLoop.remainingBlockers; var expected = Browser.mainLoop.expectedBlockers; if (remaining) { if (remaining < expected) { Module['setStatus'](message + ' (' + (expected - remaining) + '/' + expected + ')'); } else { Module['setStatus'](message); } } else { Module['setStatus'](''); } } },runIter:function(func) { if (ABORT) return; if (Module['preMainLoop']) { var preRet = Module['preMainLoop'](); if (preRet === false) { return; // |return false| skips a frame } } try { func(); } catch (e) { if (e instanceof ExitStatus) { return; } else if (e == 'unwind') { return; } else { if (e && typeof e === 'object' && e.stack) err('exception thrown: ' + [e, e.stack]); throw e; } } if (Module['postMainLoop']) Module['postMainLoop'](); }},isFullscreen:false,pointerLock:false,moduleContextCreatedCallbacks:[],workers:[],init:function() { if (!Module["preloadPlugins"]) Module["preloadPlugins"] = []; // needs to exist even in workers if (Browser.initted) return; Browser.initted = true; try { new Blob(); Browser.hasBlobConstructor = true; } catch(e) { Browser.hasBlobConstructor = false; console.log("warning: no blob constructor, cannot create blobs with mimetypes"); } Browser.BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : (typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : (!Browser.hasBlobConstructor ? console.log("warning: no BlobBuilder") : null)); Browser.URLObject = typeof window != "undefined" ? (window.URL ? window.URL : window.webkitURL) : undefined; if (!Module.noImageDecoding && typeof Browser.URLObject === 'undefined') { console.log("warning: Browser does not support creating object URLs. Built-in browser image decoding will not be available."); Module.noImageDecoding = true; } // Support for plugins that can process preloaded files. You can add more of these to // your app by creating and appending to Module.preloadPlugins. // // Each plugin is asked if it can handle a file based on the file's name. If it can, // it is given the file's raw data. When it is done, it calls a callback with the file's // (possibly modified) data. For example, a plugin might decompress a file, or it // might create some side data structure for use later (like an Image element, etc.). var imagePlugin = {}; imagePlugin['canHandle'] = function imagePlugin_canHandle(name) { return !Module.noImageDecoding && /\.(jpg|jpeg|png|bmp)$/i.test(name); }; imagePlugin['handle'] = function imagePlugin_handle(byteArray, name, onload, onerror) { var b = null; if (Browser.hasBlobConstructor) { try { b = new Blob([byteArray], { type: Browser.getMimetype(name) }); if (b.size !== byteArray.length) { // Safari bug #118630 // Safari's Blob can only take an ArrayBuffer b = new Blob([(new Uint8Array(byteArray)).buffer], { type: Browser.getMimetype(name) }); } } catch(e) { warnOnce('Blob constructor present but fails: ' + e + '; falling back to blob builder'); } } if (!b) { var bb = new Browser.BlobBuilder(); bb.append((new Uint8Array(byteArray)).buffer); // we need to pass a buffer, and must copy the array to get the right data range b = bb.getBlob(); } var url = Browser.URLObject.createObjectURL(b); assert(typeof url == 'string', 'createObjectURL must return a url as a string'); var img = new Image(); img.onload = function img_onload() { assert(img.complete, 'Image ' + name + ' could not be decoded'); var canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; var ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); Module["preloadedImages"][name] = canvas; Browser.URLObject.revokeObjectURL(url); if (onload) onload(byteArray); }; img.onerror = function img_onerror(event) { console.log('Image ' + url + ' could not be decoded'); if (onerror) onerror(); }; img.src = url; }; Module['preloadPlugins'].push(imagePlugin); var audioPlugin = {}; audioPlugin['canHandle'] = function audioPlugin_canHandle(name) { return !Module.noAudioDecoding && name.substr(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 }; }; audioPlugin['handle'] = function audioPlugin_handle(byteArray, name, onload, onerror) { var done = false; function finish(audio) { if (done) return; done = true; Module["preloadedAudios"][name] = audio; if (onload) onload(byteArray); } function fail() { if (done) return; done = true; Module["preloadedAudios"][name] = new Audio(); // empty shim if (onerror) onerror(); } if (Browser.hasBlobConstructor) { try { var b = new Blob([byteArray], { type: Browser.getMimetype(name) }); } catch(e) { return fail(); } var url = Browser.URLObject.createObjectURL(b); // XXX we never revoke this! assert(typeof url == 'string', 'createObjectURL must return a url as a string'); var audio = new Audio(); audio.addEventListener('canplaythrough', function() { finish(audio) }, false); // use addEventListener due to chromium bug 124926 audio.onerror = function audio_onerror(event) { if (done) return; console.log('warning: browser could not fully decode audio ' + name + ', trying slower base64 approach'); function encode64(data) { var BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; var PAD = '='; var ret = ''; var leftchar = 0; var leftbits = 0; for (var i = 0; i < data.length; i++) { leftchar = (leftchar << 8) | data[i]; leftbits += 8; while (leftbits >= 6) { var curr = (leftchar >> (leftbits-6)) & 0x3f; leftbits -= 6; ret += BASE[curr]; } } if (leftbits == 2) { ret += BASE[(leftchar&3) << 4]; ret += PAD + PAD; } else if (leftbits == 4) { ret += BASE[(leftchar&0xf) << 2]; ret += PAD; } return ret; } audio.src = 'data:audio/x-' + name.substr(-3) + ';base64,' + encode64(byteArray); finish(audio); // we don't wait for confirmation this worked - but it's worth trying }; audio.src = url; // workaround for chrome bug 124926 - we do not always get oncanplaythrough or onerror Browser.safeSetTimeout(function() { finish(audio); // try to use it even though it is not necessarily ready to play }, 10000); } else { return fail(); } }; Module['preloadPlugins'].push(audioPlugin); // Canvas event setup function pointerLockChange() { Browser.pointerLock = document['pointerLockElement'] === Module['canvas'] || document['mozPointerLockElement'] === Module['canvas'] || document['webkitPointerLockElement'] === Module['canvas'] || document['msPointerLockElement'] === Module['canvas']; } var canvas = Module['canvas']; if (canvas) { // forced aspect ratio can be enabled by defining 'forcedAspectRatio' on Module // Module['forcedAspectRatio'] = 4 / 3; canvas.requestPointerLock = canvas['requestPointerLock'] || canvas['mozRequestPointerLock'] || canvas['webkitRequestPointerLock'] || canvas['msRequestPointerLock'] || function(){}; canvas.exitPointerLock = document['exitPointerLock'] || document['mozExitPointerLock'] || document['webkitExitPointerLock'] || document['msExitPointerLock'] || function(){}; // no-op if function does not exist canvas.exitPointerLock = canvas.exitPointerLock.bind(document); document.addEventListener('pointerlockchange', pointerLockChange, false); document.addEventListener('mozpointerlockchange', pointerLockChange, false); document.addEventListener('webkitpointerlockchange', pointerLockChange, false); document.addEventListener('mspointerlockchange', pointerLockChange, false); if (Module['elementPointerLock']) { canvas.addEventListener("click", function(ev) { if (!Browser.pointerLock && Module['canvas'].requestPointerLock) { Module['canvas'].requestPointerLock(); ev.preventDefault(); } }, false); } } },createContext:function(canvas, useWebGL, setInModule, webGLContextAttributes) { if (useWebGL && Module.ctx && canvas == Module.canvas) return Module.ctx; // no need to recreate GL context if it's already been created for this canvas. var ctx; var contextHandle; if (useWebGL) { // For GLES2/desktop GL compatibility, adjust a few defaults to be different to WebGL defaults, so that they align better with the desktop defaults. var contextAttributes = { antialias: false, alpha: false, majorVersion: 1, }; if (webGLContextAttributes) { for (var attribute in webGLContextAttributes) { contextAttributes[attribute] = webGLContextAttributes[attribute]; } } // This check of existence of GL is here to satisfy Closure compiler, which yells if variable GL is referenced below but GL object is not // actually compiled in because application is not doing any GL operations. TODO: Ideally if GL is not being used, this function // Browser.createContext() should not even be emitted. if (typeof GL !== 'undefined') { contextHandle = GL.createContext(canvas, contextAttributes); if (contextHandle) { ctx = GL.getContext(contextHandle).GLctx; } } } else { ctx = canvas.getContext('2d'); } if (!ctx) return null; if (setInModule) { if (!useWebGL) assert(typeof GLctx === 'undefined', 'cannot set in module if GLctx is used, but we are a non-GL context that would replace it'); Module.ctx = ctx; if (useWebGL) GL.makeContextCurrent(contextHandle); Module.useWebGL = useWebGL; Browser.moduleContextCreatedCallbacks.forEach(function(callback) { callback() }); Browser.init(); } return ctx; },destroyContext:function(canvas, useWebGL, setInModule) {},fullscreenHandlersInstalled:false,lockPointer:undefined,resizeCanvas:undefined,requestFullscreen:function(lockPointer, resizeCanvas) { Browser.lockPointer = lockPointer; Browser.resizeCanvas = resizeCanvas; if (typeof Browser.lockPointer === 'undefined') Browser.lockPointer = true; if (typeof Browser.resizeCanvas === 'undefined') Browser.resizeCanvas = false; var canvas = Module['canvas']; function fullscreenChange() { Browser.isFullscreen = false; var canvasContainer = canvas.parentNode; if ((document['fullscreenElement'] || document['mozFullScreenElement'] || document['msFullscreenElement'] || document['webkitFullscreenElement'] || document['webkitCurrentFullScreenElement']) === canvasContainer) { canvas.exitFullscreen = Browser.exitFullscreen; if (Browser.lockPointer) canvas.requestPointerLock(); Browser.isFullscreen = true; if (Browser.resizeCanvas) { Browser.setFullscreenCanvasSize(); } else { Browser.updateCanvasDimensions(canvas); } } else { // remove the full screen specific parent of the canvas again to restore the HTML structure from before going full screen canvasContainer.parentNode.insertBefore(canvas, canvasContainer); canvasContainer.parentNode.removeChild(canvasContainer); if (Browser.resizeCanvas) { Browser.setWindowedCanvasSize(); } else { Browser.updateCanvasDimensions(canvas); } } if (Module['onFullScreen']) Module['onFullScreen'](Browser.isFullscreen); if (Module['onFullscreen']) Module['onFullscreen'](Browser.isFullscreen); } if (!Browser.fullscreenHandlersInstalled) { Browser.fullscreenHandlersInstalled = true; document.addEventListener('fullscreenchange', fullscreenChange, false); document.addEventListener('mozfullscreenchange', fullscreenChange, false); document.addEventListener('webkitfullscreenchange', fullscreenChange, false); document.addEventListener('MSFullscreenChange', fullscreenChange, false); } // create a new parent to ensure the canvas has no siblings. this allows browsers to optimize full screen performance when its parent is the full screen root var canvasContainer = document.createElement("div"); canvas.parentNode.insertBefore(canvasContainer, canvas); canvasContainer.appendChild(canvas); // use parent of canvas as full screen root to allow aspect ratio correction (Firefox stretches the root to screen size) canvasContainer.requestFullscreen = canvasContainer['requestFullscreen'] || canvasContainer['mozRequestFullScreen'] || canvasContainer['msRequestFullscreen'] || (canvasContainer['webkitRequestFullscreen'] ? function() { canvasContainer['webkitRequestFullscreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null) || (canvasContainer['webkitRequestFullScreen'] ? function() { canvasContainer['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null); canvasContainer.requestFullscreen(); },requestFullScreen:function() { abort('Module.requestFullScreen has been replaced by Module.requestFullscreen (without a capital S)'); },exitFullscreen:function() { // This is workaround for chrome. Trying to exit from fullscreen // not in fullscreen state will cause "TypeError: Document not active" // in chrome. See https://github.com/emscripten-core/emscripten/pull/8236 if (!Browser.isFullscreen) { return false; } var CFS = document['exitFullscreen'] || document['cancelFullScreen'] || document['mozCancelFullScreen'] || document['msExitFullscreen'] || document['webkitCancelFullScreen'] || (function() {}); CFS.apply(document, []); return true; },nextRAF:0,fakeRequestAnimationFrame:function(func) { // try to keep 60fps between calls to here var now = Date.now(); if (Browser.nextRAF === 0) { Browser.nextRAF = now + 1000/60; } else { while (now + 2 >= Browser.nextRAF) { // fudge a little, to avoid timer jitter causing us to do lots of delay:0 Browser.nextRAF += 1000/60; } } var delay = Math.max(Browser.nextRAF - now, 0); setTimeout(func, delay); },requestAnimationFrame:function(func) { if (typeof requestAnimationFrame === 'function') { requestAnimationFrame(func); return; } var RAF = Browser.fakeRequestAnimationFrame; RAF(func); },safeRequestAnimationFrame:function(func) { return Browser.requestAnimationFrame(function() { if (ABORT) return; func(); }); },safeSetTimeout:function(func, timeout) { noExitRuntime = true; return setTimeout(function() { if (ABORT) return; func(); }, timeout); },getMimetype:function(name) { return { 'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', 'png': 'image/png', 'bmp': 'image/bmp', 'ogg': 'audio/ogg', 'wav': 'audio/wav', 'mp3': 'audio/mpeg' }[name.substr(name.lastIndexOf('.')+1)]; },getUserMedia:function(func) { if(!window.getUserMedia) { window.getUserMedia = navigator['getUserMedia'] || navigator['mozGetUserMedia']; } window.getUserMedia(func); },getMovementX:function(event) { return event['movementX'] || event['mozMovementX'] || event['webkitMovementX'] || 0; },getMovementY:function(event) { return event['movementY'] || event['mozMovementY'] || event['webkitMovementY'] || 0; },getMouseWheelDelta:function(event) { var delta = 0; switch (event.type) { case 'DOMMouseScroll': // 3 lines make up a step delta = event.detail / 3; break; case 'mousewheel': // 120 units make up a step delta = event.wheelDelta / 120; break; case 'wheel': delta = event.deltaY switch(event.deltaMode) { case 0: // DOM_DELTA_PIXEL: 100 pixels make up a step delta /= 100; break; case 1: // DOM_DELTA_LINE: 3 lines make up a step delta /= 3; break; case 2: // DOM_DELTA_PAGE: A page makes up 80 steps delta *= 80; break; default: throw 'unrecognized mouse wheel delta mode: ' + event.deltaMode; } break; default: throw 'unrecognized mouse wheel event: ' + event.type; } return delta; },mouseX:0,mouseY:0,mouseMovementX:0,mouseMovementY:0,touches:{},lastTouches:{},calculateMouseEvent:function(event) { // event should be mousemove, mousedown or mouseup if (Browser.pointerLock) { // When the pointer is locked, calculate the coordinates // based on the movement of the mouse. // Workaround for Firefox bug 764498 if (event.type != 'mousemove' && ('mozMovementX' in event)) { Browser.mouseMovementX = Browser.mouseMovementY = 0; } else { Browser.mouseMovementX = Browser.getMovementX(event); Browser.mouseMovementY = Browser.getMovementY(event); } // check if SDL is available if (typeof SDL != "undefined") { Browser.mouseX = SDL.mouseX + Browser.mouseMovementX; Browser.mouseY = SDL.mouseY + Browser.mouseMovementY; } else { // just add the mouse delta to the current absolut mouse position // FIXME: ideally this should be clamped against the canvas size and zero Browser.mouseX += Browser.mouseMovementX; Browser.mouseY += Browser.mouseMovementY; } } else { // Otherwise, calculate the movement based on the changes // in the coordinates. var rect = Module["canvas"].getBoundingClientRect(); var cw = Module["canvas"].width; var ch = Module["canvas"].height; // Neither .scrollX or .pageXOffset are defined in a spec, but // we prefer .scrollX because it is currently in a spec draft. // (see: http://www.w3.org/TR/2013/WD-cssom-view-20131217/) var scrollX = ((typeof window.scrollX !== 'undefined') ? window.scrollX : window.pageXOffset); var scrollY = ((typeof window.scrollY !== 'undefined') ? window.scrollY : window.pageYOffset); // If this assert lands, it's likely because the browser doesn't support scrollX or pageXOffset // and we have no viable fallback. assert((typeof scrollX !== 'undefined') && (typeof scrollY !== 'undefined'), 'Unable to retrieve scroll position, mouse positions likely broken.'); if (event.type === 'touchstart' || event.type === 'touchend' || event.type === 'touchmove') { var touch = event.touch; if (touch === undefined) { return; // the "touch" property is only defined in SDL } var adjustedX = touch.pageX - (scrollX + rect.left); var adjustedY = touch.pageY - (scrollY + rect.top); adjustedX = adjustedX * (cw / rect.width); adjustedY = adjustedY * (ch / rect.height); var coords = { x: adjustedX, y: adjustedY }; if (event.type === 'touchstart') { Browser.lastTouches[touch.identifier] = coords; Browser.touches[touch.identifier] = coords; } else if (event.type === 'touchend' || event.type === 'touchmove') { var last = Browser.touches[touch.identifier]; if (!last) last = coords; Browser.lastTouches[touch.identifier] = last; Browser.touches[touch.identifier] = coords; } return; } var x = event.pageX - (scrollX + rect.left); var y = event.pageY - (scrollY + rect.top); // the canvas might be CSS-scaled compared to its backbuffer; // SDL-using content will want mouse coordinates in terms // of backbuffer units. x = x * (cw / rect.width); y = y * (ch / rect.height); Browser.mouseMovementX = x - Browser.mouseX; Browser.mouseMovementY = y - Browser.mouseY; Browser.mouseX = x; Browser.mouseY = y; } },asyncLoad:function(url, onload, onerror, noRunDep) { var dep = !noRunDep ? getUniqueRunDependency('al ' + url) : ''; readAsync(url, function(arrayBuffer) { assert(arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).'); onload(new Uint8Array(arrayBuffer)); if (dep) removeRunDependency(dep); }, function(event) { if (onerror) { onerror(); } else { throw 'Loading data file "' + url + '" failed.'; } }); if (dep) addRunDependency(dep); },resizeListeners:[],updateResizeListeners:function() { var canvas = Module['canvas']; Browser.resizeListeners.forEach(function(listener) { listener(canvas.width, canvas.height); }); },setCanvasSize:function(width, height, noUpdates) { var canvas = Module['canvas']; Browser.updateCanvasDimensions(canvas, width, height); if (!noUpdates) Browser.updateResizeListeners(); },windowedWidth:0,windowedHeight:0,setFullscreenCanvasSize:function() { // check if SDL is available if (typeof SDL != "undefined") { var flags = HEAPU32[((SDL.screen)>>2)]; flags = flags | 0x00800000; // set SDL_FULLSCREEN flag HEAP32[((SDL.screen)>>2)] = flags } Browser.updateCanvasDimensions(Module['canvas']); Browser.updateResizeListeners(); },setWindowedCanvasSize:function() { // check if SDL is available if (typeof SDL != "undefined") { var flags = HEAPU32[((SDL.screen)>>2)]; flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag HEAP32[((SDL.screen)>>2)] = flags } Browser.updateCanvasDimensions(Module['canvas']); Browser.updateResizeListeners(); },updateCanvasDimensions:function(canvas, wNative, hNative) { if (wNative && hNative) { canvas.widthNative = wNative; canvas.heightNative = hNative; } else { wNative = canvas.widthNative; hNative = canvas.heightNative; } var w = wNative; var h = hNative; if (Module['forcedAspectRatio'] && Module['forcedAspectRatio'] > 0) { if (w/h < Module['forcedAspectRatio']) { w = Math.round(h * Module['forcedAspectRatio']); } else { h = Math.round(w / Module['forcedAspectRatio']); } } if (((document['fullscreenElement'] || document['mozFullScreenElement'] || document['msFullscreenElement'] || document['webkitFullscreenElement'] || document['webkitCurrentFullScreenElement']) === canvas.parentNode) && (typeof screen != 'undefined')) { var factor = Math.min(screen.width / w, screen.height / h); w = Math.round(w * factor); h = Math.round(h * factor); } if (Browser.resizeCanvas) { if (canvas.width != w) canvas.width = w; if (canvas.height != h) canvas.height = h; if (typeof canvas.style != 'undefined') { canvas.style.removeProperty( "width"); canvas.style.removeProperty("height"); } } else { if (canvas.width != wNative) canvas.width = wNative; if (canvas.height != hNative) canvas.height = hNative; if (typeof canvas.style != 'undefined') { if (w != wNative || h != hNative) { canvas.style.setProperty( "width", w + "px", "important"); canvas.style.setProperty("height", h + "px", "important"); } else { canvas.style.removeProperty( "width"); canvas.style.removeProperty("height"); } } } },wgetRequests:{},nextWgetRequestHandle:0,getNextWgetRequestHandle:function() { var handle = Browser.nextWgetRequestHandle; Browser.nextWgetRequestHandle++; return handle; }}; var EGL={errorCode:12288,defaultDisplayInitialized:false,currentContext:0,currentReadSurface:0,currentDrawSurface:0,contextAttributes:{alpha:false,depth:false,stencil:false,antialias:false},stringCache:{},setErrorCode:function(code) { EGL.errorCode = code; },chooseConfig:function(display, attribList, config, config_size, numConfigs) { if (display != 62000 /* Magic ID for Emscripten 'default display' */) { EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); return 0; } if (attribList) { // read attribList if it is non-null for(;;) { var param = HEAP32[((attribList)>>2)]; if (param == 0x3021 /*EGL_ALPHA_SIZE*/) { var alphaSize = HEAP32[(((attribList)+(4))>>2)]; EGL.contextAttributes.alpha = (alphaSize > 0); } else if (param == 0x3025 /*EGL_DEPTH_SIZE*/) { var depthSize = HEAP32[(((attribList)+(4))>>2)]; EGL.contextAttributes.depth = (depthSize > 0); } else if (param == 0x3026 /*EGL_STENCIL_SIZE*/) { var stencilSize = HEAP32[(((attribList)+(4))>>2)]; EGL.contextAttributes.stencil = (stencilSize > 0); } else if (param == 0x3031 /*EGL_SAMPLES*/) { var samples = HEAP32[(((attribList)+(4))>>2)]; EGL.contextAttributes.antialias = (samples > 0); } else if (param == 0x3032 /*EGL_SAMPLE_BUFFERS*/) { var samples = HEAP32[(((attribList)+(4))>>2)]; EGL.contextAttributes.antialias = (samples == 1); } else if (param == 0x3100 /*EGL_CONTEXT_PRIORITY_LEVEL_IMG*/) { var requestedPriority = HEAP32[(((attribList)+(4))>>2)]; EGL.contextAttributes.lowLatency = (requestedPriority != 0x3103 /*EGL_CONTEXT_PRIORITY_LOW_IMG*/); } else if (param == 0x3038 /*EGL_NONE*/) { break; } attribList += 8; } } if ((!config || !config_size) && !numConfigs) { EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */); return 0; } if (numConfigs) { HEAP32[((numConfigs)>>2)] = 1; // Total number of supported configs: 1. } if (config && config_size > 0) { HEAP32[((config)>>2)] = 62002; } EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); return 1; }}; function _eglBindAPI(api) { if (api == 0x30A0 /* EGL_OPENGL_ES_API */) { EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); return 1; } else { // if (api == 0x30A1 /* EGL_OPENVG_API */ || api == 0x30A2 /* EGL_OPENGL_API */) { EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */); return 0; } } function _eglChooseConfig(display, attrib_list, configs, config_size, numConfigs) { return EGL.chooseConfig(display, attrib_list, configs, config_size, numConfigs); } function __webgl_enable_ANGLE_instanced_arrays(ctx) { // Extension available in WebGL 1 from Firefox 26 and Google Chrome 30 onwards. Core feature in WebGL 2. var ext = ctx.getExtension('ANGLE_instanced_arrays'); if (ext) { ctx['vertexAttribDivisor'] = function(index, divisor) { ext['vertexAttribDivisorANGLE'](index, divisor); }; ctx['drawArraysInstanced'] = function(mode, first, count, primcount) { ext['drawArraysInstancedANGLE'](mode, first, count, primcount); }; ctx['drawElementsInstanced'] = function(mode, count, type, indices, primcount) { ext['drawElementsInstancedANGLE'](mode, count, type, indices, primcount); }; return 1; } } function __webgl_enable_OES_vertex_array_object(ctx) { // Extension available in WebGL 1 from Firefox 25 and WebKit 536.28/desktop Safari 6.0.3 onwards. Core feature in WebGL 2. var ext = ctx.getExtension('OES_vertex_array_object'); if (ext) { ctx['createVertexArray'] = function() { return ext['createVertexArrayOES'](); }; ctx['deleteVertexArray'] = function(vao) { ext['deleteVertexArrayOES'](vao); }; ctx['bindVertexArray'] = function(vao) { ext['bindVertexArrayOES'](vao); }; ctx['isVertexArray'] = function(vao) { return ext['isVertexArrayOES'](vao); }; return 1; } } function __webgl_enable_WEBGL_draw_buffers(ctx) { // Extension available in WebGL 1 from Firefox 28 onwards. Core feature in WebGL 2. var ext = ctx.getExtension('WEBGL_draw_buffers'); if (ext) { ctx['drawBuffers'] = function(n, bufs) { ext['drawBuffersWEBGL'](n, bufs); }; return 1; } } function __webgl_enable_WEBGL_multi_draw(ctx) { // Closure is expected to be allowed to minify the '.multiDrawWebgl' property, so not accessing it quoted. return !!(ctx.multiDrawWebgl = ctx.getExtension('WEBGL_multi_draw')); } var GL={counter:1,buffers:[],programs:[],framebuffers:[],renderbuffers:[],textures:[],uniforms:[],shaders:[],vaos:[],contexts:[],offscreenCanvases:{},timerQueriesEXT:[],programInfos:{},stringCache:{},unpackAlignment:4,recordError:function recordError(errorCode) { if (!GL.lastError) { GL.lastError = errorCode; } },getNewId:function(table) { var ret = GL.counter++; for (var i = table.length; i < ret; i++) { table[i] = null; } return ret; },getSource:function(shader, count, string, length) { var source = ''; for (var i = 0; i < count; ++i) { var len = length ? HEAP32[(((length)+(i*4))>>2)] : -1; source += UTF8ToString(HEAP32[(((string)+(i*4))>>2)], len < 0 ? undefined : len); } return source; },createContext:function(canvas, webGLContextAttributes) { var ctx = (canvas.getContext("webgl", webGLContextAttributes) // https://caniuse.com/#feat=webgl ); if (!ctx) return 0; var handle = GL.registerContext(ctx, webGLContextAttributes); return handle; },registerContext:function(ctx, webGLContextAttributes) { // without pthreads a context is just an integer ID var handle = GL.getNewId(GL.contexts); var context = { handle: handle, attributes: webGLContextAttributes, version: webGLContextAttributes.majorVersion, GLctx: ctx }; // Store the created context object so that we can access the context given a canvas without having to pass the parameters again. if (ctx.canvas) ctx.canvas.GLctxObject = context; GL.contexts[handle] = context; if (typeof webGLContextAttributes.enableExtensionsByDefault === 'undefined' || webGLContextAttributes.enableExtensionsByDefault) { GL.initExtensions(context); } return handle; },makeContextCurrent:function(contextHandle) { GL.currentContext = GL.contexts[contextHandle]; // Active Emscripten GL layer context object. Module.ctx = GLctx = GL.currentContext && GL.currentContext.GLctx; // Active WebGL context object. return !(contextHandle && !GLctx); },getContext:function(contextHandle) { return GL.contexts[contextHandle]; },deleteContext:function(contextHandle) { if (GL.currentContext === GL.contexts[contextHandle]) GL.currentContext = null; if (typeof JSEvents === 'object') JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas); // Release all JS event handlers on the DOM element that the GL context is associated with since the context is now deleted. if (GL.contexts[contextHandle] && GL.contexts[contextHandle].GLctx.canvas) GL.contexts[contextHandle].GLctx.canvas.GLctxObject = undefined; // Make sure the canvas object no longer refers to the context object so there are no GC surprises. GL.contexts[contextHandle] = null; },initExtensions:function(context) { // If this function is called without a specific context object, init the extensions of the currently active context. if (!context) context = GL.currentContext; if (context.initExtensionsDone) return; context.initExtensionsDone = true; var GLctx = context.GLctx; // Detect the presence of a few extensions manually, this GL interop layer itself will need to know if they exist. // Extensions that are only available in WebGL 1 (the calls will be no-ops if called on a WebGL 2 context active) __webgl_enable_ANGLE_instanced_arrays(GLctx); __webgl_enable_OES_vertex_array_object(GLctx); __webgl_enable_WEBGL_draw_buffers(GLctx); GLctx.disjointTimerQueryExt = GLctx.getExtension("EXT_disjoint_timer_query"); __webgl_enable_WEBGL_multi_draw(GLctx); // .getSupportedExtensions() can return null if context is lost, so coerce to empty array. var exts = GLctx.getSupportedExtensions() || []; exts.forEach(function(ext) { // WEBGL_lose_context, WEBGL_debug_renderer_info and WEBGL_debug_shaders are not enabled by default. if (ext.indexOf('lose_context') < 0 && ext.indexOf('debug') < 0) { // Call .getExtension() to enable that extension permanently. GLctx.getExtension(ext); } }); },populateUniformTable:function(program) { var p = GL.programs[program]; var ptable = GL.programInfos[program] = { uniforms: {}, maxUniformLength: 0, // This is eagerly computed below, since we already enumerate all uniforms anyway. maxAttributeLength: -1, // This is lazily computed and cached, computed when/if first asked, "-1" meaning not computed yet. maxUniformBlockNameLength: -1 // Lazily computed as well }; var utable = ptable.uniforms; // A program's uniform table maps the string name of an uniform to an integer location of that uniform. // The global GL.uniforms map maps integer locations to WebGLUniformLocations. var numUniforms = GLctx.getProgramParameter(p, 0x8B86/*GL_ACTIVE_UNIFORMS*/); for (var i = 0; i < numUniforms; ++i) { var u = GLctx.getActiveUniform(p, i); var name = u.name; ptable.maxUniformLength = Math.max(ptable.maxUniformLength, name.length+1); // If we are dealing with an array, e.g. vec4 foo[3], strip off the array index part to canonicalize that "foo", "foo[]", // and "foo[0]" will mean the same. Loop below will populate foo[1] and foo[2]. if (name.slice(-1) == ']') { name = name.slice(0, name.lastIndexOf('[')); } // Optimize memory usage slightly: If we have an array of uniforms, e.g. 'vec3 colors[3];', then // only store the string 'colors' in utable, and 'colors[0]', 'colors[1]' and 'colors[2]' will be parsed as 'colors'+i. // Note that for the GL.uniforms table, we still need to fetch the all WebGLUniformLocations for all the indices. var loc = GLctx.getUniformLocation(p, name); if (loc) { var id = GL.getNewId(GL.uniforms); utable[name] = [u.size, id]; GL.uniforms[id] = loc; for (var j = 1; j < u.size; ++j) { var n = name + '['+j+']'; loc = GLctx.getUniformLocation(p, n); id = GL.getNewId(GL.uniforms); GL.uniforms[id] = loc; } } } }}; function _eglCreateContext(display, config, hmm, contextAttribs) { if (display != 62000 /* Magic ID for Emscripten 'default display' */) { EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); return 0; } // EGL 1.4 spec says default EGL_CONTEXT_CLIENT_VERSION is GLES1, but this is not supported by Emscripten. // So user must pass EGL_CONTEXT_CLIENT_VERSION == 2 to initialize EGL. var glesContextVersion = 1; for(;;) { var param = HEAP32[((contextAttribs)>>2)]; if (param == 0x3098 /*EGL_CONTEXT_CLIENT_VERSION*/) { glesContextVersion = HEAP32[(((contextAttribs)+(4))>>2)]; } else if (param == 0x3038 /*EGL_NONE*/) { break; } else { /* EGL1.4 specifies only EGL_CONTEXT_CLIENT_VERSION as supported attribute */ EGL.setErrorCode(0x3004 /*EGL_BAD_ATTRIBUTE*/); return 0; } contextAttribs += 8; } if (glesContextVersion != 2) { EGL.setErrorCode(0x3005 /* EGL_BAD_CONFIG */); return 0; /* EGL_NO_CONTEXT */ } EGL.contextAttributes.majorVersion = glesContextVersion - 1; // WebGL 1 is GLES 2, WebGL2 is GLES3 EGL.contextAttributes.minorVersion = 0; EGL.context = GL.createContext(Module['canvas'], EGL.contextAttributes); if (EGL.context != 0) { EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); // Run callbacks so that GL emulation works GL.makeContextCurrent(EGL.context); Module.useWebGL = true; Browser.moduleContextCreatedCallbacks.forEach(function(callback) { callback() }); // Note: This function only creates a context, but it shall not make it active. GL.makeContextCurrent(null); return 62004; // Magic ID for Emscripten EGLContext } else { EGL.setErrorCode(0x3009 /* EGL_BAD_MATCH */); // By the EGL 1.4 spec, an implementation that does not support GLES2 (WebGL in this case), this error code is set. return 0; /* EGL_NO_CONTEXT */ } } function _eglCreateWindowSurface(display, config, win, attrib_list) { if (display != 62000 /* Magic ID for Emscripten 'default display' */) { EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); return 0; } if (config != 62002 /* Magic ID for the only EGLConfig supported by Emscripten */) { EGL.setErrorCode(0x3005 /* EGL_BAD_CONFIG */); return 0; } // TODO: Examine attrib_list! Parameters that can be present there are: // - EGL_RENDER_BUFFER (must be EGL_BACK_BUFFER) // - EGL_VG_COLORSPACE (can't be set) // - EGL_VG_ALPHA_FORMAT (can't be set) EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); return 62006; /* Magic ID for Emscripten 'default surface' */ } function _eglDestroyContext(display, context) { if (display != 62000 /* Magic ID for Emscripten 'default display' */) { EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); return 0; } if (context != 62004 /* Magic ID for Emscripten EGLContext */) { EGL.setErrorCode(0x3006 /* EGL_BAD_CONTEXT */); return 0; } GL.deleteContext(EGL.context); EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); if (EGL.currentContext == context) { EGL.currentContext = 0; } return 1 /* EGL_TRUE */; } function _eglDestroySurface(display, surface) { if (display != 62000 /* Magic ID for Emscripten 'default display' */) { EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); return 0; } if (surface != 62006 /* Magic ID for the only EGLSurface supported by Emscripten */) { EGL.setErrorCode(0x300D /* EGL_BAD_SURFACE */); return 1; } if (EGL.currentReadSurface == surface) { EGL.currentReadSurface = 0; } if (EGL.currentDrawSurface == surface) { EGL.currentDrawSurface = 0; } EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); return 1; /* Magic ID for Emscripten 'default surface' */ } function _eglGetConfigAttrib(display, config, attribute, value) { if (display != 62000 /* Magic ID for Emscripten 'default display' */) { EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); return 0; } if (config != 62002 /* Magic ID for the only EGLConfig supported by Emscripten */) { EGL.setErrorCode(0x3005 /* EGL_BAD_CONFIG */); return 0; } if (!value) { EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */); return 0; } EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); switch(attribute) { case 0x3020: // EGL_BUFFER_SIZE HEAP32[((value)>>2)] = EGL.contextAttributes.alpha ? 32 : 24; return 1; case 0x3021: // EGL_ALPHA_SIZE HEAP32[((value)>>2)] = EGL.contextAttributes.alpha ? 8 : 0; return 1; case 0x3022: // EGL_BLUE_SIZE HEAP32[((value)>>2)] = 8; return 1; case 0x3023: // EGL_GREEN_SIZE HEAP32[((value)>>2)] = 8; return 1; case 0x3024: // EGL_RED_SIZE HEAP32[((value)>>2)] = 8; return 1; case 0x3025: // EGL_DEPTH_SIZE HEAP32[((value)>>2)] = EGL.contextAttributes.depth ? 24 : 0; return 1; case 0x3026: // EGL_STENCIL_SIZE HEAP32[((value)>>2)] = EGL.contextAttributes.stencil ? 8 : 0; return 1; case 0x3027: // EGL_CONFIG_CAVEAT // We can return here one of EGL_NONE (0x3038), EGL_SLOW_CONFIG (0x3050) or EGL_NON_CONFORMANT_CONFIG (0x3051). HEAP32[((value)>>2)] = 0x3038; return 1; case 0x3028: // EGL_CONFIG_ID HEAP32[((value)>>2)] = 62002; return 1; case 0x3029: // EGL_LEVEL HEAP32[((value)>>2)] = 0; return 1; case 0x302A: // EGL_MAX_PBUFFER_HEIGHT HEAP32[((value)>>2)] = 4096; return 1; case 0x302B: // EGL_MAX_PBUFFER_PIXELS HEAP32[((value)>>2)] = 16777216; return 1; case 0x302C: // EGL_MAX_PBUFFER_WIDTH HEAP32[((value)>>2)] = 4096; return 1; case 0x302D: // EGL_NATIVE_RENDERABLE HEAP32[((value)>>2)] = 0; return 1; case 0x302E: // EGL_NATIVE_VISUAL_ID HEAP32[((value)>>2)] = 0; return 1; case 0x302F: // EGL_NATIVE_VISUAL_TYPE HEAP32[((value)>>2)] = 0x3038; return 1; case 0x3031: // EGL_SAMPLES HEAP32[((value)>>2)] = EGL.contextAttributes.antialias ? 4 : 0; return 1; case 0x3032: // EGL_SAMPLE_BUFFERS HEAP32[((value)>>2)] = EGL.contextAttributes.antialias ? 1 : 0; return 1; case 0x3033: // EGL_SURFACE_TYPE HEAP32[((value)>>2)] = 0x4; return 1; case 0x3034: // EGL_TRANSPARENT_TYPE // If this returns EGL_TRANSPARENT_RGB (0x3052), transparency is used through color-keying. No such thing applies to Emscripten canvas. HEAP32[((value)>>2)] = 0x3038; return 1; case 0x3035: // EGL_TRANSPARENT_BLUE_VALUE case 0x3036: // EGL_TRANSPARENT_GREEN_VALUE case 0x3037: // EGL_TRANSPARENT_RED_VALUE // "If EGL_TRANSPARENT_TYPE is EGL_NONE, then the values for EGL_TRANSPARENT_RED_VALUE, EGL_TRANSPARENT_GREEN_VALUE, and EGL_TRANSPARENT_BLUE_VALUE are undefined." HEAP32[((value)>>2)] = -1; return 1; case 0x3039: // EGL_BIND_TO_TEXTURE_RGB case 0x303A: // EGL_BIND_TO_TEXTURE_RGBA HEAP32[((value)>>2)] = 0; return 1; case 0x303B: // EGL_MIN_SWAP_INTERVAL HEAP32[((value)>>2)] = 0; return 1; case 0x303C: // EGL_MAX_SWAP_INTERVAL HEAP32[((value)>>2)] = 1; return 1; case 0x303D: // EGL_LUMINANCE_SIZE case 0x303E: // EGL_ALPHA_MASK_SIZE HEAP32[((value)>>2)] = 0; return 1; case 0x303F: // EGL_COLOR_BUFFER_TYPE // EGL has two types of buffers: EGL_RGB_BUFFER and EGL_LUMINANCE_BUFFER. HEAP32[((value)>>2)] = 0x308E; return 1; case 0x3040: // EGL_RENDERABLE_TYPE // A bit combination of EGL_OPENGL_ES_BIT,EGL_OPENVG_BIT,EGL_OPENGL_ES2_BIT and EGL_OPENGL_BIT. HEAP32[((value)>>2)] = 0x4; return 1; case 0x3042: // EGL_CONFORMANT // "EGL_CONFORMANT is a mask indicating if a client API context created with respect to the corresponding EGLConfig will pass the required conformance tests for that API." HEAP32[((value)>>2)] = 0; return 1; default: EGL.setErrorCode(0x3004 /* EGL_BAD_ATTRIBUTE */); return 0; } } function _eglGetDisplay(nativeDisplayType) { EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); // Note: As a 'conformant' implementation of EGL, we would prefer to init here only if the user // calls this function with EGL_DEFAULT_DISPLAY. Other display IDs would be preferred to be unsupported // and EGL_NO_DISPLAY returned. Uncomment the following code lines to do this. // Instead, an alternative route has been preferred, namely that the Emscripten EGL implementation // "emulates" X11, and eglGetDisplay is expected to accept/receive a pointer to an X11 Display object. // Therefore, be lax and allow anything to be passed in, and return the magic handle to our default EGLDisplay object. // if (nativeDisplayType == 0 /* EGL_DEFAULT_DISPLAY */) { return 62000; // Magic ID for Emscripten 'default display' // } // else // return 0; // EGL_NO_DISPLAY } function _eglGetError() { return EGL.errorCode; } function _eglInitialize(display, majorVersion, minorVersion) { if (display == 62000 /* Magic ID for Emscripten 'default display' */) { if (majorVersion) { HEAP32[((majorVersion)>>2)] = 1; // Advertise EGL Major version: '1' } if (minorVersion) { HEAP32[((minorVersion)>>2)] = 4; // Advertise EGL Minor version: '4' } EGL.defaultDisplayInitialized = true; EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); return 1; } else { EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); return 0; } } function _eglMakeCurrent(display, draw, read, context) { if (display != 62000 /* Magic ID for Emscripten 'default display' */) { EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); return 0 /* EGL_FALSE */; } //\todo An EGL_NOT_INITIALIZED error is generated if EGL is not initialized for dpy. if (context != 0 && context != 62004 /* Magic ID for Emscripten EGLContext */) { EGL.setErrorCode(0x3006 /* EGL_BAD_CONTEXT */); return 0; } if ((read != 0 && read != 62006) || (draw != 0 && draw != 62006 /* Magic ID for Emscripten 'default surface' */)) { EGL.setErrorCode(0x300D /* EGL_BAD_SURFACE */); return 0; } GL.makeContextCurrent(context ? EGL.context : null); EGL.currentContext = context; EGL.currentDrawSurface = draw; EGL.currentReadSurface = read; EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); return 1 /* EGL_TRUE */; } function _eglQueryString(display, name) { if (display != 62000 /* Magic ID for Emscripten 'default display' */) { EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); return 0; } //\todo An EGL_NOT_INITIALIZED error is generated if EGL is not initialized for dpy. EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); if (EGL.stringCache[name]) return EGL.stringCache[name]; var ret; switch(name) { case 0x3053 /* EGL_VENDOR */: ret = allocateUTF8("Emscripten"); break; case 0x3054 /* EGL_VERSION */: ret = allocateUTF8("1.4 Emscripten EGL"); break; case 0x3055 /* EGL_EXTENSIONS */: ret = allocateUTF8(""); break; // Currently not supporting any EGL extensions. case 0x308D /* EGL_CLIENT_APIS */: ret = allocateUTF8("OpenGL_ES"); break; default: EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */); return 0; } EGL.stringCache[name] = ret; return ret; } function _eglSwapBuffers() { if (!EGL.defaultDisplayInitialized) { EGL.setErrorCode(0x3001 /* EGL_NOT_INITIALIZED */); } else if (!Module.ctx) { EGL.setErrorCode(0x3002 /* EGL_BAD_ACCESS */); } else if (Module.ctx.isContextLost()) { EGL.setErrorCode(0x300E /* EGL_CONTEXT_LOST */); } else { // According to documentation this does an implicit flush. // Due to discussion at https://github.com/emscripten-core/emscripten/pull/1871 // the flush was removed since this _may_ result in slowing code down. //_glFlush(); EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); return 1 /* EGL_TRUE */; } return 0 /* EGL_FALSE */; } function _eglSwapInterval(display, interval) { if (display != 62000 /* Magic ID for Emscripten 'default display' */) { EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); return 0; } if (interval == 0) _emscripten_set_main_loop_timing(0/*EM_TIMING_SETTIMEOUT*/, 0); else _emscripten_set_main_loop_timing(1/*EM_TIMING_RAF*/, interval); EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); return 1; } function _eglTerminate(display) { if (display != 62000 /* Magic ID for Emscripten 'default display' */) { EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */); return 0; } EGL.currentContext = 0; EGL.currentReadSurface = 0; EGL.currentDrawSurface = 0; EGL.defaultDisplayInitialized = false; EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); return 1; } function _eglWaitClient() { EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); return 1; } function _eglWaitGL( ) { return _eglWaitClient(); } function _eglWaitNative(nativeEngineId) { EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); return 1; } function _emscripten_asm_const_int(code, sigPtr, argbuf) { var args = readAsmConstArgs(sigPtr, argbuf); return ASM_CONSTS[code].apply(null, args); } function _emscripten_async_wget(url, file, onload, onerror) { noExitRuntime = true; var _url = UTF8ToString(url); var _file = UTF8ToString(file); _file = PATH_FS.resolve(_file); function doCallback(callback) { if (callback) { var stack = stackSave(); wasmTable.get(callback)(allocate(intArrayFromString(_file), ALLOC_STACK)); stackRestore(stack); } } var destinationDirectory = PATH.dirname(_file); FS.createPreloadedFile( destinationDirectory, PATH.basename(_file), _url, true, true, function() { doCallback(onload); }, function() { doCallback(onerror); }, false, // dontCreateFile false, // canOwn function() { // preFinish // if a file exists there, we overwrite it try { FS.unlink(_file); } catch (e) {} // if the destination directory does not yet exist, create it FS.mkdirTree(destinationDirectory); } ); } var JSEvents={inEventHandler:0,removeAllEventListeners:function() { for(var i = JSEvents.eventHandlers.length-1; i >= 0; --i) { JSEvents._removeHandler(i); } JSEvents.eventHandlers = []; JSEvents.deferredCalls = []; },registerRemoveEventListeners:function() { if (!JSEvents.removeEventListenersRegistered) { __ATEXIT__.push(JSEvents.removeAllEventListeners); JSEvents.removeEventListenersRegistered = true; } },deferredCalls:[],deferCall:function(targetFunction, precedence, argsList) { function arraysHaveEqualContent(arrA, arrB) { if (arrA.length != arrB.length) return false; for(var i in arrA) { if (arrA[i] != arrB[i]) return false; } return true; } // Test if the given call was already queued, and if so, don't add it again. for(var i in JSEvents.deferredCalls) { var call = JSEvents.deferredCalls[i]; if (call.targetFunction == targetFunction && arraysHaveEqualContent(call.argsList, argsList)) { return; } } JSEvents.deferredCalls.push({ targetFunction: targetFunction, precedence: precedence, argsList: argsList }); JSEvents.deferredCalls.sort(function(x,y) { return x.precedence < y.precedence; }); },removeDeferredCalls:function(targetFunction) { for(var i = 0; i < JSEvents.deferredCalls.length; ++i) { if (JSEvents.deferredCalls[i].targetFunction == targetFunction) { JSEvents.deferredCalls.splice(i, 1); --i; } } },canPerformEventHandlerRequests:function() { return JSEvents.inEventHandler && JSEvents.currentEventHandler.allowsDeferredCalls; },runDeferredCalls:function() { if (!JSEvents.canPerformEventHandlerRequests()) { return; } for(var i = 0; i < JSEvents.deferredCalls.length; ++i) { var call = JSEvents.deferredCalls[i]; JSEvents.deferredCalls.splice(i, 1); --i; call.targetFunction.apply(null, call.argsList); } },eventHandlers:[],removeAllHandlersOnTarget:function(target, eventTypeString) { for(var i = 0; i < JSEvents.eventHandlers.length; ++i) { if (JSEvents.eventHandlers[i].target == target && (!eventTypeString || eventTypeString == JSEvents.eventHandlers[i].eventTypeString)) { JSEvents._removeHandler(i--); } } },_removeHandler:function(i) { var h = JSEvents.eventHandlers[i]; h.target.removeEventListener(h.eventTypeString, h.eventListenerFunc, h.useCapture); JSEvents.eventHandlers.splice(i, 1); },registerOrRemoveHandler:function(eventHandler) { var jsEventHandler = function jsEventHandler(event) { // Increment nesting count for the event handler. ++JSEvents.inEventHandler; JSEvents.currentEventHandler = eventHandler; // Process any old deferred calls the user has placed. JSEvents.runDeferredCalls(); // Process the actual event, calls back to user C code handler. eventHandler.handlerFunc(event); // Process any new deferred calls that were placed right now from this event handler. JSEvents.runDeferredCalls(); // Out of event handler - restore nesting count. --JSEvents.inEventHandler; }; if (eventHandler.callbackfunc) { eventHandler.eventListenerFunc = jsEventHandler; eventHandler.target.addEventListener(eventHandler.eventTypeString, jsEventHandler, eventHandler.useCapture); JSEvents.eventHandlers.push(eventHandler); JSEvents.registerRemoveEventListeners(); } else { for(var i = 0; i < JSEvents.eventHandlers.length; ++i) { if (JSEvents.eventHandlers[i].target == eventHandler.target && JSEvents.eventHandlers[i].eventTypeString == eventHandler.eventTypeString) { JSEvents._removeHandler(i--); } } } },getNodeNameForTarget:function(target) { if (!target) return ''; if (target == window) return '#window'; if (target == screen) return '#screen'; return (target && target.nodeName) ? target.nodeName : ''; },fullscreenEnabled:function() { return document.fullscreenEnabled // Safari 13.0.3 on macOS Catalina 10.15.1 still ships with prefixed webkitFullscreenEnabled. // TODO: If Safari at some point ships with unprefixed version, update the version check above. || document.webkitFullscreenEnabled ; }}; var currentFullscreenStrategy={}; function maybeCStringToJsString(cString) { // "cString > 2" checks if the input is a number, and isn't of the special // values we accept here, EMSCRIPTEN_EVENT_TARGET_* (which map to 0, 1, 2). // In other words, if cString > 2 then it's a pointer to a valid place in // memory, and points to a C string. return cString > 2 ? UTF8ToString(cString) : cString; } var specialHTMLTargets=[0, typeof document !== 'undefined' ? document : 0, typeof window !== 'undefined' ? window : 0]; function findEventTarget(target) { target = maybeCStringToJsString(target); var domElement = specialHTMLTargets[target] || (typeof document !== 'undefined' ? document.querySelector(target) : undefined); return domElement; } function findCanvasEventTarget(target) { return findEventTarget(target); } function _emscripten_get_canvas_element_size(target, width, height) { var canvas = findCanvasEventTarget(target); if (!canvas) return -4; HEAP32[((width)>>2)] = canvas.width; HEAP32[((height)>>2)] = canvas.height; } function getCanvasElementSize(target) { var stackTop = stackSave(); var w = stackAlloc(8); var h = w + 4; var targetInt = stackAlloc(target.id.length+1); stringToUTF8(target.id, targetInt, target.id.length+1); var ret = _emscripten_get_canvas_element_size(targetInt, w, h); var size = [HEAP32[((w)>>2)], HEAP32[((h)>>2)]]; stackRestore(stackTop); return size; } function _emscripten_set_canvas_element_size(target, width, height) { var canvas = findCanvasEventTarget(target); if (!canvas) return -4; canvas.width = width; canvas.height = height; return 0; } function setCanvasElementSize(target, width, height) { if (!target.controlTransferredOffscreen) { target.width = width; target.height = height; } else { // This function is being called from high-level JavaScript code instead of asm.js/Wasm, // and it needs to synchronously proxy over to another thread, so marshal the string onto the heap to do the call. var stackTop = stackSave(); var targetInt = stackAlloc(target.id.length+1); stringToUTF8(target.id, targetInt, target.id.length+1); _emscripten_set_canvas_element_size(targetInt, width, height); stackRestore(stackTop); } } function registerRestoreOldStyle(canvas) { var canvasSize = getCanvasElementSize(canvas); var oldWidth = canvasSize[0]; var oldHeight = canvasSize[1]; var oldCssWidth = canvas.style.width; var oldCssHeight = canvas.style.height; var oldBackgroundColor = canvas.style.backgroundColor; // Chrome reads color from here. var oldDocumentBackgroundColor = document.body.style.backgroundColor; // IE11 reads color from here. // Firefox always has black background color. var oldPaddingLeft = canvas.style.paddingLeft; // Chrome, FF, Safari var oldPaddingRight = canvas.style.paddingRight; var oldPaddingTop = canvas.style.paddingTop; var oldPaddingBottom = canvas.style.paddingBottom; var oldMarginLeft = canvas.style.marginLeft; // IE11 var oldMarginRight = canvas.style.marginRight; var oldMarginTop = canvas.style.marginTop; var oldMarginBottom = canvas.style.marginBottom; var oldDocumentBodyMargin = document.body.style.margin; var oldDocumentOverflow = document.documentElement.style.overflow; // Chrome, Firefox var oldDocumentScroll = document.body.scroll; // IE var oldImageRendering = canvas.style.imageRendering; function restoreOldStyle() { var fullscreenElement = document.fullscreenElement || document.webkitFullscreenElement || document.msFullscreenElement ; if (!fullscreenElement) { document.removeEventListener('fullscreenchange', restoreOldStyle); // Unprefixed Fullscreen API shipped in Chromium 71 (https://bugs.chromium.org/p/chromium/issues/detail?id=383813) // As of Safari 13.0.3 on macOS Catalina 10.15.1 still ships with prefixed webkitfullscreenchange. TODO: revisit this check once Safari ships unprefixed version. document.removeEventListener('webkitfullscreenchange', restoreOldStyle); setCanvasElementSize(canvas, oldWidth, oldHeight); canvas.style.width = oldCssWidth; canvas.style.height = oldCssHeight; canvas.style.backgroundColor = oldBackgroundColor; // Chrome // IE11 hack: assigning 'undefined' or an empty string to document.body.style.backgroundColor has no effect, so first assign back the default color // before setting the undefined value. Setting undefined value is also important, or otherwise we would later treat that as something that the user // had explicitly set so subsequent fullscreen transitions would not set background color properly. if (!oldDocumentBackgroundColor) document.body.style.backgroundColor = 'white'; document.body.style.backgroundColor = oldDocumentBackgroundColor; // IE11 canvas.style.paddingLeft = oldPaddingLeft; // Chrome, FF, Safari canvas.style.paddingRight = oldPaddingRight; canvas.style.paddingTop = oldPaddingTop; canvas.style.paddingBottom = oldPaddingBottom; canvas.style.marginLeft = oldMarginLeft; // IE11 canvas.style.marginRight = oldMarginRight; canvas.style.marginTop = oldMarginTop; canvas.style.marginBottom = oldMarginBottom; document.body.style.margin = oldDocumentBodyMargin; document.documentElement.style.overflow = oldDocumentOverflow; // Chrome, Firefox document.body.scroll = oldDocumentScroll; // IE canvas.style.imageRendering = oldImageRendering; if (canvas.GLctxObject) canvas.GLctxObject.GLctx.viewport(0, 0, oldWidth, oldHeight); if (currentFullscreenStrategy.canvasResizedCallback) { wasmTable.get(currentFullscreenStrategy.canvasResizedCallback)(37, 0, currentFullscreenStrategy.canvasResizedCallbackUserData); } } } document.addEventListener('fullscreenchange', restoreOldStyle); // Unprefixed Fullscreen API shipped in Chromium 71 (https://bugs.chromium.org/p/chromium/issues/detail?id=383813) // As of Safari 13.0.3 on macOS Catalina 10.15.1 still ships with prefixed webkitfullscreenchange. TODO: revisit this check once Safari ships unprefixed version. document.addEventListener('webkitfullscreenchange', restoreOldStyle); return restoreOldStyle; } function setLetterbox(element, topBottom, leftRight) { // Cannot use margin to specify letterboxes in FF or Chrome, since those ignore margins in fullscreen mode. element.style.paddingLeft = element.style.paddingRight = leftRight + 'px'; element.style.paddingTop = element.style.paddingBottom = topBottom + 'px'; } function getBoundingClientRect(e) { return specialHTMLTargets.indexOf(e) < 0 ? e.getBoundingClientRect() : {'left':0,'top':0}; } function _JSEvents_resizeCanvasForFullscreen(target, strategy) { var restoreOldStyle = registerRestoreOldStyle(target); var cssWidth = strategy.softFullscreen ? innerWidth : screen.width; var cssHeight = strategy.softFullscreen ? innerHeight : screen.height; var rect = getBoundingClientRect(target); var windowedCssWidth = rect.width; var windowedCssHeight = rect.height; var canvasSize = getCanvasElementSize(target); var windowedRttWidth = canvasSize[0]; var windowedRttHeight = canvasSize[1]; if (strategy.scaleMode == 3) { setLetterbox(target, (cssHeight - windowedCssHeight) / 2, (cssWidth - windowedCssWidth) / 2); cssWidth = windowedCssWidth; cssHeight = windowedCssHeight; } else if (strategy.scaleMode == 2) { if (cssWidth*windowedRttHeight < windowedRttWidth*cssHeight) { var desiredCssHeight = windowedRttHeight * cssWidth / windowedRttWidth; setLetterbox(target, (cssHeight - desiredCssHeight) / 2, 0); cssHeight = desiredCssHeight; } else { var desiredCssWidth = windowedRttWidth * cssHeight / windowedRttHeight; setLetterbox(target, 0, (cssWidth - desiredCssWidth) / 2); cssWidth = desiredCssWidth; } } // If we are adding padding, must choose a background color or otherwise Chrome will give the // padding a default white color. Do it only if user has not customized their own background color. if (!target.style.backgroundColor) target.style.backgroundColor = 'black'; // IE11 does the same, but requires the color to be set in the document body. if (!document.body.style.backgroundColor) document.body.style.backgroundColor = 'black'; // IE11 // Firefox always shows black letterboxes independent of style color. target.style.width = cssWidth + 'px'; target.style.height = cssHeight + 'px'; if (strategy.filteringMode == 1) { target.style.imageRendering = 'optimizeSpeed'; target.style.imageRendering = '-moz-crisp-edges'; target.style.imageRendering = '-o-crisp-edges'; target.style.imageRendering = '-webkit-optimize-contrast'; target.style.imageRendering = 'optimize-contrast'; target.style.imageRendering = 'crisp-edges'; target.style.imageRendering = 'pixelated'; } var dpiScale = (strategy.canvasResolutionScaleMode == 2) ? devicePixelRatio : 1; if (strategy.canvasResolutionScaleMode != 0) { var newWidth = (cssWidth * dpiScale)|0; var newHeight = (cssHeight * dpiScale)|0; setCanvasElementSize(target, newWidth, newHeight); if (target.GLctxObject) target.GLctxObject.GLctx.viewport(0, 0, newWidth, newHeight); } return restoreOldStyle; } function _JSEvents_requestFullscreen(target, strategy) { // EMSCRIPTEN_FULLSCREEN_SCALE_DEFAULT + EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE is a mode where no extra logic is performed to the DOM elements. if (strategy.scaleMode != 0 || strategy.canvasResolutionScaleMode != 0) { _JSEvents_resizeCanvasForFullscreen(target, strategy); } if (target.requestFullscreen) { target.requestFullscreen(); } else if (target.webkitRequestFullscreen) { target.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); } else { return JSEvents.fullscreenEnabled() ? -3 : -1; } currentFullscreenStrategy = strategy; if (strategy.canvasResizedCallback) { wasmTable.get(strategy.canvasResizedCallback)(37, 0, strategy.canvasResizedCallbackUserData); } return 0; } function _emscripten_exit_fullscreen() { if (!JSEvents.fullscreenEnabled()) return -1; // Make sure no queued up calls will fire after this. JSEvents.removeDeferredCalls(_JSEvents_requestFullscreen); var d = specialHTMLTargets[1]; if (d.exitFullscreen) { d.fullscreenElement && d.exitFullscreen(); } else if (d.webkitExitFullscreen) { d.webkitFullscreenElement && d.webkitExitFullscreen(); } else { return -1; } return 0; } function requestPointerLock(target) { if (target.requestPointerLock) { target.requestPointerLock(); } else if (target.msRequestPointerLock) { target.msRequestPointerLock(); } else { // document.body is known to accept pointer lock, so use that to differentiate if the user passed a bad element, // or if the whole browser just doesn't support the feature. if (document.body.requestPointerLock || document.body.msRequestPointerLock ) { return -3; } else { return -1; } } return 0; } function _emscripten_exit_pointerlock() { // Make sure no queued up calls will fire after this. JSEvents.removeDeferredCalls(requestPointerLock); if (document.exitPointerLock) { document.exitPointerLock(); } else if (document.msExitPointerLock) { document.msExitPointerLock(); } else { return -1; } return 0; } function _emscripten_get_device_pixel_ratio() { return (typeof devicePixelRatio === 'number' && devicePixelRatio) || 1.0; } function _emscripten_get_element_css_size(target, width, height) { target = findEventTarget(target); if (!target) return -4; var rect = getBoundingClientRect(target); HEAPF64[((width)>>3)] = rect.width; HEAPF64[((height)>>3)] = rect.height; return 0; } function fillGamepadEventData(eventStruct, e) { HEAPF64[((eventStruct)>>3)] = e.timestamp; for(var i = 0; i < e.axes.length; ++i) { HEAPF64[(((eventStruct+i*8)+(16))>>3)] = e.axes[i]; } for(var i = 0; i < e.buttons.length; ++i) { if (typeof(e.buttons[i]) === 'object') { HEAPF64[(((eventStruct+i*8)+(528))>>3)] = e.buttons[i].value; } else { HEAPF64[(((eventStruct+i*8)+(528))>>3)] = e.buttons[i]; } } for(var i = 0; i < e.buttons.length; ++i) { if (typeof(e.buttons[i]) === 'object') { HEAP32[(((eventStruct+i*4)+(1040))>>2)] = e.buttons[i].pressed; } else { // Assigning a boolean to HEAP32, that's ok, but Closure would like to warn about it: /** @suppress {checkTypes} */ HEAP32[(((eventStruct+i*4)+(1040))>>2)] = e.buttons[i] == 1; } } HEAP32[(((eventStruct)+(1296))>>2)] = e.connected; HEAP32[(((eventStruct)+(1300))>>2)] = e.index; HEAP32[(((eventStruct)+(8))>>2)] = e.axes.length; HEAP32[(((eventStruct)+(12))>>2)] = e.buttons.length; stringToUTF8(e.id, eventStruct + 1304, 64); stringToUTF8(e.mapping, eventStruct + 1368, 64); } function _emscripten_get_gamepad_status(index, gamepadState) { if (!JSEvents.lastGamepadState) throw 'emscripten_get_gamepad_status() can only be called after having first called emscripten_sample_gamepad_data() and that function has returned EMSCRIPTEN_RESULT_SUCCESS!'; // INVALID_PARAM is returned on a Gamepad index that never was there. if (index < 0 || index >= JSEvents.lastGamepadState.length) return -5; // NO_DATA is returned on a Gamepad index that was removed. // For previously disconnected gamepads there should be an empty slot (null/undefined/false) at the index. // This is because gamepads must keep their original position in the array. // For example, removing the first of two gamepads produces [null/undefined/false, gamepad]. if (!JSEvents.lastGamepadState[index]) return -7; fillGamepadEventData(gamepadState, JSEvents.lastGamepadState[index]); return 0; } function _emscripten_get_num_gamepads() { if (!JSEvents.lastGamepadState) throw 'emscripten_get_num_gamepads() can only be called after having first called emscripten_sample_gamepad_data() and that function has returned EMSCRIPTEN_RESULT_SUCCESS!'; // N.B. Do not call emscripten_get_num_gamepads() unless having first called emscripten_sample_gamepad_data(), and that has returned EMSCRIPTEN_RESULT_SUCCESS. // Otherwise the following line will throw an exception. return JSEvents.lastGamepadState.length; } function _emscripten_glActiveTexture(x0) { GLctx['activeTexture'](x0) } function _emscripten_glAttachShader(program, shader) { GLctx.attachShader(GL.programs[program], GL.shaders[shader]); } function _emscripten_glBeginQueryEXT(target, id) { GLctx.disjointTimerQueryExt['beginQueryEXT'](target, GL.timerQueriesEXT[id]); } function _emscripten_glBindAttribLocation(program, index, name) { GLctx.bindAttribLocation(GL.programs[program], index, UTF8ToString(name)); } function _emscripten_glBindBuffer(target, buffer) { GLctx.bindBuffer(target, GL.buffers[buffer]); } function _emscripten_glBindFramebuffer(target, framebuffer) { GLctx.bindFramebuffer(target, GL.framebuffers[framebuffer]); } function _emscripten_glBindRenderbuffer(target, renderbuffer) { GLctx.bindRenderbuffer(target, GL.renderbuffers[renderbuffer]); } function _emscripten_glBindTexture(target, texture) { GLctx.bindTexture(target, GL.textures[texture]); } function _emscripten_glBindVertexArrayOES(vao) { GLctx['bindVertexArray'](GL.vaos[vao]); } function _emscripten_glBlendColor(x0, x1, x2, x3) { GLctx['blendColor'](x0, x1, x2, x3) } function _emscripten_glBlendEquation(x0) { GLctx['blendEquation'](x0) } function _emscripten_glBlendEquationSeparate(x0, x1) { GLctx['blendEquationSeparate'](x0, x1) } function _emscripten_glBlendFunc(x0, x1) { GLctx['blendFunc'](x0, x1) } function _emscripten_glBlendFuncSeparate(x0, x1, x2, x3) { GLctx['blendFuncSeparate'](x0, x1, x2, x3) } function _emscripten_glBufferData(target, size, data, usage) { // N.b. here first form specifies a heap subarray, second form an integer size, so the ?: code here is polymorphic. It is advised to avoid // randomly mixing both uses in calling code, to avoid any potential JS engine JIT issues. GLctx.bufferData(target, data ? HEAPU8.subarray(data, data+size) : size, usage); } function _emscripten_glBufferSubData(target, offset, size, data) { GLctx.bufferSubData(target, offset, HEAPU8.subarray(data, data+size)); } function _emscripten_glCheckFramebufferStatus(x0) { return GLctx['checkFramebufferStatus'](x0) } function _emscripten_glClear(x0) { GLctx['clear'](x0) } function _emscripten_glClearColor(x0, x1, x2, x3) { GLctx['clearColor'](x0, x1, x2, x3) } function _emscripten_glClearDepthf(x0) { GLctx['clearDepth'](x0) } function _emscripten_glClearStencil(x0) { GLctx['clearStencil'](x0) } function _emscripten_glColorMask(red, green, blue, alpha) { GLctx.colorMask(!!red, !!green, !!blue, !!alpha); } function _emscripten_glCompileShader(shader) { GLctx.compileShader(GL.shaders[shader]); } function _emscripten_glCompressedTexImage2D(target, level, internalFormat, width, height, border, imageSize, data) { GLctx['compressedTexImage2D'](target, level, internalFormat, width, height, border, data ? HEAPU8.subarray((data), (data+imageSize)) : null); } function _emscripten_glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data) { GLctx['compressedTexSubImage2D'](target, level, xoffset, yoffset, width, height, format, data ? HEAPU8.subarray((data), (data+imageSize)) : null); } function _emscripten_glCopyTexImage2D(x0, x1, x2, x3, x4, x5, x6, x7) { GLctx['copyTexImage2D'](x0, x1, x2, x3, x4, x5, x6, x7) } function _emscripten_glCopyTexSubImage2D(x0, x1, x2, x3, x4, x5, x6, x7) { GLctx['copyTexSubImage2D'](x0, x1, x2, x3, x4, x5, x6, x7) } function _emscripten_glCreateProgram() { var id = GL.getNewId(GL.programs); var program = GLctx.createProgram(); program.name = id; GL.programs[id] = program; return id; } function _emscripten_glCreateShader(shaderType) { var id = GL.getNewId(GL.shaders); GL.shaders[id] = GLctx.createShader(shaderType); return id; } function _emscripten_glCullFace(x0) { GLctx['cullFace'](x0) } function _emscripten_glDeleteBuffers(n, buffers) { for (var i = 0; i < n; i++) { var id = HEAP32[(((buffers)+(i*4))>>2)]; var buffer = GL.buffers[id]; // From spec: "glDeleteBuffers silently ignores 0's and names that do not // correspond to existing buffer objects." if (!buffer) continue; GLctx.deleteBuffer(buffer); buffer.name = 0; GL.buffers[id] = null; } } function _emscripten_glDeleteFramebuffers(n, framebuffers) { for (var i = 0; i < n; ++i) { var id = HEAP32[(((framebuffers)+(i*4))>>2)]; var framebuffer = GL.framebuffers[id]; if (!framebuffer) continue; // GL spec: "glDeleteFramebuffers silently ignores 0s and names that do not correspond to existing framebuffer objects". GLctx.deleteFramebuffer(framebuffer); framebuffer.name = 0; GL.framebuffers[id] = null; } } function _emscripten_glDeleteProgram(id) { if (!id) return; var program = GL.programs[id]; if (!program) { // glDeleteProgram actually signals an error when deleting a nonexisting object, unlike some other GL delete functions. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } GLctx.deleteProgram(program); program.name = 0; GL.programs[id] = null; GL.programInfos[id] = null; } function _emscripten_glDeleteQueriesEXT(n, ids) { for (var i = 0; i < n; i++) { var id = HEAP32[(((ids)+(i*4))>>2)]; var query = GL.timerQueriesEXT[id]; if (!query) continue; // GL spec: "unused names in ids are ignored, as is the name zero." GLctx.disjointTimerQueryExt['deleteQueryEXT'](query); GL.timerQueriesEXT[id] = null; } } function _emscripten_glDeleteRenderbuffers(n, renderbuffers) { for (var i = 0; i < n; i++) { var id = HEAP32[(((renderbuffers)+(i*4))>>2)]; var renderbuffer = GL.renderbuffers[id]; if (!renderbuffer) continue; // GL spec: "glDeleteRenderbuffers silently ignores 0s and names that do not correspond to existing renderbuffer objects". GLctx.deleteRenderbuffer(renderbuffer); renderbuffer.name = 0; GL.renderbuffers[id] = null; } } function _emscripten_glDeleteShader(id) { if (!id) return; var shader = GL.shaders[id]; if (!shader) { // glDeleteShader actually signals an error when deleting a nonexisting object, unlike some other GL delete functions. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } GLctx.deleteShader(shader); GL.shaders[id] = null; } function _emscripten_glDeleteTextures(n, textures) { for (var i = 0; i < n; i++) { var id = HEAP32[(((textures)+(i*4))>>2)]; var texture = GL.textures[id]; if (!texture) continue; // GL spec: "glDeleteTextures silently ignores 0s and names that do not correspond to existing textures". GLctx.deleteTexture(texture); texture.name = 0; GL.textures[id] = null; } } function _emscripten_glDeleteVertexArraysOES(n, vaos) { for (var i = 0; i < n; i++) { var id = HEAP32[(((vaos)+(i*4))>>2)]; GLctx['deleteVertexArray'](GL.vaos[id]); GL.vaos[id] = null; } } function _emscripten_glDepthFunc(x0) { GLctx['depthFunc'](x0) } function _emscripten_glDepthMask(flag) { GLctx.depthMask(!!flag); } function _emscripten_glDepthRangef(x0, x1) { GLctx['depthRange'](x0, x1) } function _emscripten_glDetachShader(program, shader) { GLctx.detachShader(GL.programs[program], GL.shaders[shader]); } function _emscripten_glDisable(x0) { GLctx['disable'](x0) } function _emscripten_glDisableVertexAttribArray(index) { GLctx.disableVertexAttribArray(index); } function _emscripten_glDrawArrays(mode, first, count) { GLctx.drawArrays(mode, first, count); } function _emscripten_glDrawArraysInstancedANGLE(mode, first, count, primcount) { GLctx['drawArraysInstanced'](mode, first, count, primcount); } var tempFixedLengthArray=[]; function _emscripten_glDrawBuffersWEBGL(n, bufs) { var bufArray = tempFixedLengthArray[n]; for (var i = 0; i < n; i++) { bufArray[i] = HEAP32[(((bufs)+(i*4))>>2)]; } GLctx['drawBuffers'](bufArray); } function _emscripten_glDrawElements(mode, count, type, indices) { GLctx.drawElements(mode, count, type, indices); } function _emscripten_glDrawElementsInstancedANGLE(mode, count, type, indices, primcount) { GLctx['drawElementsInstanced'](mode, count, type, indices, primcount); } function _emscripten_glEnable(x0) { GLctx['enable'](x0) } function _emscripten_glEnableVertexAttribArray(index) { GLctx.enableVertexAttribArray(index); } function _emscripten_glEndQueryEXT(target) { GLctx.disjointTimerQueryExt['endQueryEXT'](target); } function _emscripten_glFinish() { GLctx['finish']() } function _emscripten_glFlush() { GLctx['flush']() } function _emscripten_glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer) { GLctx.framebufferRenderbuffer(target, attachment, renderbuffertarget, GL.renderbuffers[renderbuffer]); } function _emscripten_glFramebufferTexture2D(target, attachment, textarget, texture, level) { GLctx.framebufferTexture2D(target, attachment, textarget, GL.textures[texture], level); } function _emscripten_glFrontFace(x0) { GLctx['frontFace'](x0) } function __glGenObject(n, buffers, createFunction, objectTable ) { for (var i = 0; i < n; i++) { var buffer = GLctx[createFunction](); var id = buffer && GL.getNewId(objectTable); if (buffer) { buffer.name = id; objectTable[id] = buffer; } else { GL.recordError(0x502 /* GL_INVALID_OPERATION */); } HEAP32[(((buffers)+(i*4))>>2)] = id; } } function _emscripten_glGenBuffers(n, buffers) { __glGenObject(n, buffers, 'createBuffer', GL.buffers ); } function _emscripten_glGenFramebuffers(n, ids) { __glGenObject(n, ids, 'createFramebuffer', GL.framebuffers ); } function _emscripten_glGenQueriesEXT(n, ids) { for (var i = 0; i < n; i++) { var query = GLctx.disjointTimerQueryExt['createQueryEXT'](); if (!query) { GL.recordError(0x502 /* GL_INVALID_OPERATION */); while(i < n) HEAP32[(((ids)+(i++*4))>>2)] = 0; return; } var id = GL.getNewId(GL.timerQueriesEXT); query.name = id; GL.timerQueriesEXT[id] = query; HEAP32[(((ids)+(i*4))>>2)] = id; } } function _emscripten_glGenRenderbuffers(n, renderbuffers) { __glGenObject(n, renderbuffers, 'createRenderbuffer', GL.renderbuffers ); } function _emscripten_glGenTextures(n, textures) { __glGenObject(n, textures, 'createTexture', GL.textures ); } function _emscripten_glGenVertexArraysOES(n, arrays) { __glGenObject(n, arrays, 'createVertexArray', GL.vaos ); } function _emscripten_glGenerateMipmap(x0) { GLctx['generateMipmap'](x0) } function __glGetActiveAttribOrUniform(funcName, program, index, bufSize, length, size, type, name) { program = GL.programs[program]; var info = GLctx[funcName](program, index); if (info) { // If an error occurs, nothing will be written to length, size and type and name. var numBytesWrittenExclNull = name && stringToUTF8(info.name, name, bufSize); if (length) HEAP32[((length)>>2)] = numBytesWrittenExclNull; if (size) HEAP32[((size)>>2)] = info.size; if (type) HEAP32[((type)>>2)] = info.type; } } function _emscripten_glGetActiveAttrib(program, index, bufSize, length, size, type, name) { __glGetActiveAttribOrUniform('getActiveAttrib', program, index, bufSize, length, size, type, name); } function _emscripten_glGetActiveUniform(program, index, bufSize, length, size, type, name) { __glGetActiveAttribOrUniform('getActiveUniform', program, index, bufSize, length, size, type, name); } function _emscripten_glGetAttachedShaders(program, maxCount, count, shaders) { var result = GLctx.getAttachedShaders(GL.programs[program]); var len = result.length; if (len > maxCount) { len = maxCount; } HEAP32[((count)>>2)] = len; for (var i = 0; i < len; ++i) { var id = GL.shaders.indexOf(result[i]); HEAP32[(((shaders)+(i*4))>>2)] = id; } } function _emscripten_glGetAttribLocation(program, name) { return GLctx.getAttribLocation(GL.programs[program], UTF8ToString(name)); } function readI53FromI64(ptr) { return HEAPU32[ptr>>2] + HEAP32[ptr+4>>2] * 4294967296; } function readI53FromU64(ptr) { return HEAPU32[ptr>>2] + HEAPU32[ptr+4>>2] * 4294967296; } function writeI53ToI64(ptr, num) { HEAPU32[ptr>>2] = num; HEAPU32[ptr+4>>2] = (num - HEAPU32[ptr>>2])/4294967296; var deserialized = (num >= 0) ? readI53FromU64(ptr) : readI53FromI64(ptr); if (deserialized != num) warnOnce('writeI53ToI64() out of range: serialized JS Number ' + num + ' to Wasm heap as bytes lo=0x' + HEAPU32[ptr>>2].toString(16) + ', hi=0x' + HEAPU32[ptr+4>>2].toString(16) + ', which deserializes back to ' + deserialized + ' instead!'); } function emscriptenWebGLGet(name_, p, type) { // Guard against user passing a null pointer. // Note that GLES2 spec does not say anything about how passing a null pointer should be treated. // Testing on desktop core GL 3, the application crashes on glGetIntegerv to a null pointer, but // better to report an error instead of doing anything random. if (!p) { GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } var ret = undefined; switch(name_) { // Handle a few trivial GLES values case 0x8DFA: // GL_SHADER_COMPILER ret = 1; break; case 0x8DF8: // GL_SHADER_BINARY_FORMATS if (type != 0 && type != 1) { GL.recordError(0x500); // GL_INVALID_ENUM } return; // Do not write anything to the out pointer, since no binary formats are supported. case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS ret = 0; break; case 0x86A2: // GL_NUM_COMPRESSED_TEXTURE_FORMATS // WebGL doesn't have GL_NUM_COMPRESSED_TEXTURE_FORMATS (it's obsolete since GL_COMPRESSED_TEXTURE_FORMATS returns a JS array that can be queried for length), // so implement it ourselves to allow C++ GLES2 code get the length. var formats = GLctx.getParameter(0x86A3 /*GL_COMPRESSED_TEXTURE_FORMATS*/); ret = formats ? formats.length : 0; break; } if (ret === undefined) { var result = GLctx.getParameter(name_); switch (typeof(result)) { case "number": ret = result; break; case "boolean": ret = result ? 1 : 0; break; case "string": GL.recordError(0x500); // GL_INVALID_ENUM return; case "object": if (result === null) { // null is a valid result for some (e.g., which buffer is bound - perhaps nothing is bound), but otherwise // can mean an invalid name_, which we need to report as an error switch(name_) { case 0x8894: // ARRAY_BUFFER_BINDING case 0x8B8D: // CURRENT_PROGRAM case 0x8895: // ELEMENT_ARRAY_BUFFER_BINDING case 0x8CA6: // FRAMEBUFFER_BINDING or DRAW_FRAMEBUFFER_BINDING case 0x8CA7: // RENDERBUFFER_BINDING case 0x8069: // TEXTURE_BINDING_2D case 0x85B5: // WebGL 2 GL_VERTEX_ARRAY_BINDING, or WebGL 1 extension OES_vertex_array_object GL_VERTEX_ARRAY_BINDING_OES case 0x8514: { // TEXTURE_BINDING_CUBE_MAP ret = 0; break; } default: { GL.recordError(0x500); // GL_INVALID_ENUM return; } } } else if (result instanceof Float32Array || result instanceof Uint32Array || result instanceof Int32Array || result instanceof Array) { for (var i = 0; i < result.length; ++i) { switch (type) { case 0: HEAP32[(((p)+(i*4))>>2)] = result[i]; break; case 2: HEAPF32[(((p)+(i*4))>>2)] = result[i]; break; case 4: HEAP8[(((p)+(i))>>0)] = result[i] ? 1 : 0; break; } } return; } else { try { ret = result.name | 0; } catch(e) { GL.recordError(0x500); // GL_INVALID_ENUM err('GL_INVALID_ENUM in glGet' + type + 'v: Unknown object returned from WebGL getParameter(' + name_ + ')! (error: ' + e + ')'); return; } } break; default: GL.recordError(0x500); // GL_INVALID_ENUM err('GL_INVALID_ENUM in glGet' + type + 'v: Native code calling glGet' + type + 'v(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!'); return; } } switch (type) { case 1: writeI53ToI64(p, ret); break; case 0: HEAP32[((p)>>2)] = ret; break; case 2: HEAPF32[((p)>>2)] = ret; break; case 4: HEAP8[((p)>>0)] = ret ? 1 : 0; break; } } function _emscripten_glGetBooleanv(name_, p) { emscriptenWebGLGet(name_, p, 4); } function _emscripten_glGetBufferParameteriv(target, value, data) { if (!data) { // GLES2 specification does not specify how to behave if data is a null pointer. Since calling this function does not make sense // if data == null, issue a GL error to notify user about it. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } HEAP32[((data)>>2)] = GLctx.getBufferParameter(target, value); } function _emscripten_glGetError() { var error = GLctx.getError() || GL.lastError; GL.lastError = 0/*GL_NO_ERROR*/; return error; } function _emscripten_glGetFloatv(name_, p) { emscriptenWebGLGet(name_, p, 2); } function _emscripten_glGetFramebufferAttachmentParameteriv(target, attachment, pname, params) { var result = GLctx.getFramebufferAttachmentParameter(target, attachment, pname); if (result instanceof WebGLRenderbuffer || result instanceof WebGLTexture) { result = result.name | 0; } HEAP32[((params)>>2)] = result; } function _emscripten_glGetIntegerv(name_, p) { emscriptenWebGLGet(name_, p, 0); } function _emscripten_glGetProgramInfoLog(program, maxLength, length, infoLog) { var log = GLctx.getProgramInfoLog(GL.programs[program]); if (log === null) log = '(unknown error)'; var numBytesWrittenExclNull = (maxLength > 0 && infoLog) ? stringToUTF8(log, infoLog, maxLength) : 0; if (length) HEAP32[((length)>>2)] = numBytesWrittenExclNull; } function _emscripten_glGetProgramiv(program, pname, p) { if (!p) { // GLES2 specification does not specify how to behave if p is a null pointer. Since calling this function does not make sense // if p == null, issue a GL error to notify user about it. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } if (program >= GL.counter) { GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } var ptable = GL.programInfos[program]; if (!ptable) { GL.recordError(0x502 /* GL_INVALID_OPERATION */); return; } if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH var log = GLctx.getProgramInfoLog(GL.programs[program]); if (log === null) log = '(unknown error)'; HEAP32[((p)>>2)] = log.length + 1; } else if (pname == 0x8B87 /* GL_ACTIVE_UNIFORM_MAX_LENGTH */) { HEAP32[((p)>>2)] = ptable.maxUniformLength; } else if (pname == 0x8B8A /* GL_ACTIVE_ATTRIBUTE_MAX_LENGTH */) { if (ptable.maxAttributeLength == -1) { program = GL.programs[program]; var numAttribs = GLctx.getProgramParameter(program, 0x8B89/*GL_ACTIVE_ATTRIBUTES*/); ptable.maxAttributeLength = 0; // Spec says if there are no active attribs, 0 must be returned. for (var i = 0; i < numAttribs; ++i) { var activeAttrib = GLctx.getActiveAttrib(program, i); ptable.maxAttributeLength = Math.max(ptable.maxAttributeLength, activeAttrib.name.length+1); } } HEAP32[((p)>>2)] = ptable.maxAttributeLength; } else if (pname == 0x8A35 /* GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH */) { if (ptable.maxUniformBlockNameLength == -1) { program = GL.programs[program]; var numBlocks = GLctx.getProgramParameter(program, 0x8A36/*GL_ACTIVE_UNIFORM_BLOCKS*/); ptable.maxUniformBlockNameLength = 0; for (var i = 0; i < numBlocks; ++i) { var activeBlockName = GLctx.getActiveUniformBlockName(program, i); ptable.maxUniformBlockNameLength = Math.max(ptable.maxUniformBlockNameLength, activeBlockName.length+1); } } HEAP32[((p)>>2)] = ptable.maxUniformBlockNameLength; } else { HEAP32[((p)>>2)] = GLctx.getProgramParameter(GL.programs[program], pname); } } function _emscripten_glGetQueryObjecti64vEXT(id, pname, params) { if (!params) { // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense // if p == null, issue a GL error to notify user about it. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } var query = GL.timerQueriesEXT[id]; var param = GLctx.disjointTimerQueryExt['getQueryObjectEXT'](query, pname); var ret; if (typeof param == 'boolean') { ret = param ? 1 : 0; } else { ret = param; } writeI53ToI64(params, ret); } function _emscripten_glGetQueryObjectivEXT(id, pname, params) { if (!params) { // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense // if p == null, issue a GL error to notify user about it. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } var query = GL.timerQueriesEXT[id]; var param = GLctx.disjointTimerQueryExt['getQueryObjectEXT'](query, pname); var ret; if (typeof param == 'boolean') { ret = param ? 1 : 0; } else { ret = param; } HEAP32[((params)>>2)] = ret; } function _emscripten_glGetQueryObjectui64vEXT(id, pname, params) { if (!params) { // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense // if p == null, issue a GL error to notify user about it. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } var query = GL.timerQueriesEXT[id]; var param = GLctx.disjointTimerQueryExt['getQueryObjectEXT'](query, pname); var ret; if (typeof param == 'boolean') { ret = param ? 1 : 0; } else { ret = param; } writeI53ToI64(params, ret); } function _emscripten_glGetQueryObjectuivEXT(id, pname, params) { if (!params) { // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense // if p == null, issue a GL error to notify user about it. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } var query = GL.timerQueriesEXT[id]; var param = GLctx.disjointTimerQueryExt['getQueryObjectEXT'](query, pname); var ret; if (typeof param == 'boolean') { ret = param ? 1 : 0; } else { ret = param; } HEAP32[((params)>>2)] = ret; } function _emscripten_glGetQueryivEXT(target, pname, params) { if (!params) { // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense // if p == null, issue a GL error to notify user about it. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } HEAP32[((params)>>2)] = GLctx.disjointTimerQueryExt['getQueryEXT'](target, pname); } function _emscripten_glGetRenderbufferParameteriv(target, pname, params) { if (!params) { // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense // if params == null, issue a GL error to notify user about it. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } HEAP32[((params)>>2)] = GLctx.getRenderbufferParameter(target, pname); } function _emscripten_glGetShaderInfoLog(shader, maxLength, length, infoLog) { var log = GLctx.getShaderInfoLog(GL.shaders[shader]); if (log === null) log = '(unknown error)'; var numBytesWrittenExclNull = (maxLength > 0 && infoLog) ? stringToUTF8(log, infoLog, maxLength) : 0; if (length) HEAP32[((length)>>2)] = numBytesWrittenExclNull; } function _emscripten_glGetShaderPrecisionFormat(shaderType, precisionType, range, precision) { var result = GLctx.getShaderPrecisionFormat(shaderType, precisionType); HEAP32[((range)>>2)] = result.rangeMin; HEAP32[(((range)+(4))>>2)] = result.rangeMax; HEAP32[((precision)>>2)] = result.precision; } function _emscripten_glGetShaderSource(shader, bufSize, length, source) { var result = GLctx.getShaderSource(GL.shaders[shader]); if (!result) return; // If an error occurs, nothing will be written to length or source. var numBytesWrittenExclNull = (bufSize > 0 && source) ? stringToUTF8(result, source, bufSize) : 0; if (length) HEAP32[((length)>>2)] = numBytesWrittenExclNull; } function _emscripten_glGetShaderiv(shader, pname, p) { if (!p) { // GLES2 specification does not specify how to behave if p is a null pointer. Since calling this function does not make sense // if p == null, issue a GL error to notify user about it. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH var log = GLctx.getShaderInfoLog(GL.shaders[shader]); if (log === null) log = '(unknown error)'; // The GLES2 specification says that if the shader has an empty info log, // a value of 0 is returned. Otherwise the log has a null char appended. // (An empty string is falsey, so we can just check that instead of // looking at log.length.) var logLength = log ? log.length + 1 : 0; HEAP32[((p)>>2)] = logLength; } else if (pname == 0x8B88) { // GL_SHADER_SOURCE_LENGTH var source = GLctx.getShaderSource(GL.shaders[shader]); // source may be a null, or the empty string, both of which are falsey // values that we report a 0 length for. var sourceLength = source ? source.length + 1 : 0; HEAP32[((p)>>2)] = sourceLength; } else { HEAP32[((p)>>2)] = GLctx.getShaderParameter(GL.shaders[shader], pname); } } function stringToNewUTF8(jsString) { var length = lengthBytesUTF8(jsString)+1; var cString = _malloc(length); stringToUTF8(jsString, cString, length); return cString; } function _emscripten_glGetString(name_) { if (GL.stringCache[name_]) return GL.stringCache[name_]; var ret; switch(name_) { case 0x1F03 /* GL_EXTENSIONS */: var exts = GLctx.getSupportedExtensions() || []; // .getSupportedExtensions() can return null if context is lost, so coerce to empty array. exts = exts.concat(exts.map(function(e) { return "GL_" + e; })); ret = stringToNewUTF8(exts.join(' ')); break; case 0x1F00 /* GL_VENDOR */: case 0x1F01 /* GL_RENDERER */: case 0x9245 /* UNMASKED_VENDOR_WEBGL */: case 0x9246 /* UNMASKED_RENDERER_WEBGL */: var s = GLctx.getParameter(name_); if (!s) { GL.recordError(0x500/*GL_INVALID_ENUM*/); } ret = stringToNewUTF8(s); break; case 0x1F02 /* GL_VERSION */: var glVersion = GLctx.getParameter(0x1F02 /*GL_VERSION*/); // return GLES version string corresponding to the version of the WebGL context { glVersion = 'OpenGL ES 2.0 (' + glVersion + ')'; } ret = stringToNewUTF8(glVersion); break; case 0x8B8C /* GL_SHADING_LANGUAGE_VERSION */: var glslVersion = GLctx.getParameter(0x8B8C /*GL_SHADING_LANGUAGE_VERSION*/); // extract the version number 'N.M' from the string 'WebGL GLSL ES N.M ...' var ver_re = /^WebGL GLSL ES ([0-9]\.[0-9][0-9]?)(?:$| .*)/; var ver_num = glslVersion.match(ver_re); if (ver_num !== null) { if (ver_num[1].length == 3) ver_num[1] = ver_num[1] + '0'; // ensure minor version has 2 digits glslVersion = 'OpenGL ES GLSL ES ' + ver_num[1] + ' (' + glslVersion + ')'; } ret = stringToNewUTF8(glslVersion); break; default: GL.recordError(0x500/*GL_INVALID_ENUM*/); return 0; } GL.stringCache[name_] = ret; return ret; } function _emscripten_glGetTexParameterfv(target, pname, params) { if (!params) { // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense // if p == null, issue a GL error to notify user about it. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } HEAPF32[((params)>>2)] = GLctx.getTexParameter(target, pname); } function _emscripten_glGetTexParameteriv(target, pname, params) { if (!params) { // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense // if p == null, issue a GL error to notify user about it. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } HEAP32[((params)>>2)] = GLctx.getTexParameter(target, pname); } /** @suppress {checkTypes} */ function jstoi_q(str) { return parseInt(str); } function _emscripten_glGetUniformLocation(program, name) { name = UTF8ToString(name); var arrayIndex = 0; // If user passed an array accessor "[index]", parse the array index off the accessor. if (name[name.length - 1] == ']') { var leftBrace = name.lastIndexOf('['); arrayIndex = name[leftBrace+1] != ']' ? jstoi_q(name.slice(leftBrace + 1)) : 0; // "index]", parseInt will ignore the ']' at the end; but treat "foo[]" as "foo[0]" name = name.slice(0, leftBrace); } var uniformInfo = GL.programInfos[program] && GL.programInfos[program].uniforms[name]; // returns pair [ dimension_of_uniform_array, uniform_location ] if (uniformInfo && arrayIndex >= 0 && arrayIndex < uniformInfo[0]) { // Check if user asked for an out-of-bounds element, i.e. for 'vec4 colors[3];' user could ask for 'colors[10]' which should return -1. return uniformInfo[1] + arrayIndex; } else { return -1; } } /** @suppress{checkTypes} */ function emscriptenWebGLGetUniform(program, location, params, type) { if (!params) { // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense // if params == null, issue a GL error to notify user about it. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } var data = GLctx.getUniform(GL.programs[program], GL.uniforms[location]); if (typeof data == 'number' || typeof data == 'boolean') { switch (type) { case 0: HEAP32[((params)>>2)] = data; break; case 2: HEAPF32[((params)>>2)] = data; break; } } else { for (var i = 0; i < data.length; i++) { switch (type) { case 0: HEAP32[(((params)+(i*4))>>2)] = data[i]; break; case 2: HEAPF32[(((params)+(i*4))>>2)] = data[i]; break; } } } } function _emscripten_glGetUniformfv(program, location, params) { emscriptenWebGLGetUniform(program, location, params, 2); } function _emscripten_glGetUniformiv(program, location, params) { emscriptenWebGLGetUniform(program, location, params, 0); } function _emscripten_glGetVertexAttribPointerv(index, pname, pointer) { if (!pointer) { // GLES2 specification does not specify how to behave if pointer is a null pointer. Since calling this function does not make sense // if pointer == null, issue a GL error to notify user about it. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } HEAP32[((pointer)>>2)] = GLctx.getVertexAttribOffset(index, pname); } /** @suppress{checkTypes} */ function emscriptenWebGLGetVertexAttrib(index, pname, params, type) { if (!params) { // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense // if params == null, issue a GL error to notify user about it. GL.recordError(0x501 /* GL_INVALID_VALUE */); return; } var data = GLctx.getVertexAttrib(index, pname); if (pname == 0x889F/*VERTEX_ATTRIB_ARRAY_BUFFER_BINDING*/) { HEAP32[((params)>>2)] = data && data["name"]; } else if (typeof data == 'number' || typeof data == 'boolean') { switch (type) { case 0: HEAP32[((params)>>2)] = data; break; case 2: HEAPF32[((params)>>2)] = data; break; case 5: HEAP32[((params)>>2)] = Math.fround(data); break; } } else { for (var i = 0; i < data.length; i++) { switch (type) { case 0: HEAP32[(((params)+(i*4))>>2)] = data[i]; break; case 2: HEAPF32[(((params)+(i*4))>>2)] = data[i]; break; case 5: HEAP32[(((params)+(i*4))>>2)] = Math.fround(data[i]); break; } } } } function _emscripten_glGetVertexAttribfv(index, pname, params) { // N.B. This function may only be called if the vertex attribute was specified using the function glVertexAttrib*f(), // otherwise the results are undefined. (GLES3 spec 6.1.12) emscriptenWebGLGetVertexAttrib(index, pname, params, 2); } function _emscripten_glGetVertexAttribiv(index, pname, params) { // N.B. This function may only be called if the vertex attribute was specified using the function glVertexAttrib*f(), // otherwise the results are undefined. (GLES3 spec 6.1.12) emscriptenWebGLGetVertexAttrib(index, pname, params, 5); } function _emscripten_glHint(x0, x1) { GLctx['hint'](x0, x1) } function _emscripten_glIsBuffer(buffer) { var b = GL.buffers[buffer]; if (!b) return 0; return GLctx.isBuffer(b); } function _emscripten_glIsEnabled(x0) { return GLctx['isEnabled'](x0) } function _emscripten_glIsFramebuffer(framebuffer) { var fb = GL.framebuffers[framebuffer]; if (!fb) return 0; return GLctx.isFramebuffer(fb); } function _emscripten_glIsProgram(program) { program = GL.programs[program]; if (!program) return 0; return GLctx.isProgram(program); } function _emscripten_glIsQueryEXT(id) { var query = GL.timerQueriesEXT[id]; if (!query) return 0; return GLctx.disjointTimerQueryExt['isQueryEXT'](query); } function _emscripten_glIsRenderbuffer(renderbuffer) { var rb = GL.renderbuffers[renderbuffer]; if (!rb) return 0; return GLctx.isRenderbuffer(rb); } function _emscripten_glIsShader(shader) { var s = GL.shaders[shader]; if (!s) return 0; return GLctx.isShader(s); } function _emscripten_glIsTexture(id) { var texture = GL.textures[id]; if (!texture) return 0; return GLctx.isTexture(texture); } function _emscripten_glIsVertexArrayOES(array) { var vao = GL.vaos[array]; if (!vao) return 0; return GLctx['isVertexArray'](vao); } function _emscripten_glLineWidth(x0) { GLctx['lineWidth'](x0) } function _emscripten_glLinkProgram(program) { GLctx.linkProgram(GL.programs[program]); GL.populateUniformTable(program); } function _emscripten_glPixelStorei(pname, param) { if (pname == 0xCF5 /* GL_UNPACK_ALIGNMENT */) { GL.unpackAlignment = param; } GLctx.pixelStorei(pname, param); } function _emscripten_glPolygonOffset(x0, x1) { GLctx['polygonOffset'](x0, x1) } function _emscripten_glQueryCounterEXT(id, target) { GLctx.disjointTimerQueryExt['queryCounterEXT'](GL.timerQueriesEXT[id], target); } function computeUnpackAlignedImageSize(width, height, sizePerPixel, alignment) { function roundedToNextMultipleOf(x, y) { return (x + y - 1) & -y; } var plainRowSize = width * sizePerPixel; var alignedRowSize = roundedToNextMultipleOf(plainRowSize, alignment); return height * alignedRowSize; } function __colorChannelsInGlTextureFormat(format) { // Micro-optimizations for size: map format to size by subtracting smallest enum value (0x1902) from all values first. // Also omit the most common size value (1) from the list, which is assumed by formats not on the list. var colorChannels = { // 0x1902 /* GL_DEPTH_COMPONENT */ - 0x1902: 1, // 0x1906 /* GL_ALPHA */ - 0x1902: 1, 5: 3, 6: 4, // 0x1909 /* GL_LUMINANCE */ - 0x1902: 1, 8: 2, 29502: 3, 29504: 4, }; return colorChannels[format - 0x1902]||1; } function heapObjectForWebGLType(type) { // Micro-optimization for size: Subtract lowest GL enum number (0x1400/* GL_BYTE */) from type to compare // smaller values for the heap, for shorter generated code size. // Also the type HEAPU16 is not tested for explicitly, but any unrecognized type will return out HEAPU16. // (since most types are HEAPU16) type -= 0x1400; if (type == 1) return HEAPU8; if (type == 4) return HEAP32; if (type == 6) return HEAPF32; if (type == 5 || type == 28922 ) return HEAPU32; return HEAPU16; } function heapAccessShiftForWebGLHeap(heap) { return 31 - Math.clz32(heap.BYTES_PER_ELEMENT); } function emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, internalFormat) { var heap = heapObjectForWebGLType(type); var shift = heapAccessShiftForWebGLHeap(heap); var byteSize = 1<> shift, pixels + bytes >> shift); } function _emscripten_glReadPixels(x, y, width, height, format, type, pixels) { var pixelData = emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, format); if (!pixelData) { GL.recordError(0x500/*GL_INVALID_ENUM*/); return; } GLctx.readPixels(x, y, width, height, format, type, pixelData); } function _emscripten_glReleaseShaderCompiler() { // NOP (as allowed by GLES 2.0 spec) } function _emscripten_glRenderbufferStorage(x0, x1, x2, x3) { GLctx['renderbufferStorage'](x0, x1, x2, x3) } function _emscripten_glSampleCoverage(value, invert) { GLctx.sampleCoverage(value, !!invert); } function _emscripten_glScissor(x0, x1, x2, x3) { GLctx['scissor'](x0, x1, x2, x3) } function _emscripten_glShaderBinary() { GL.recordError(0x500/*GL_INVALID_ENUM*/); } function _emscripten_glShaderSource(shader, count, string, length) { var source = GL.getSource(shader, count, string, length); GLctx.shaderSource(GL.shaders[shader], source); } function _emscripten_glStencilFunc(x0, x1, x2) { GLctx['stencilFunc'](x0, x1, x2) } function _emscripten_glStencilFuncSeparate(x0, x1, x2, x3) { GLctx['stencilFuncSeparate'](x0, x1, x2, x3) } function _emscripten_glStencilMask(x0) { GLctx['stencilMask'](x0) } function _emscripten_glStencilMaskSeparate(x0, x1) { GLctx['stencilMaskSeparate'](x0, x1) } function _emscripten_glStencilOp(x0, x1, x2) { GLctx['stencilOp'](x0, x1, x2) } function _emscripten_glStencilOpSeparate(x0, x1, x2, x3) { GLctx['stencilOpSeparate'](x0, x1, x2, x3) } function _emscripten_glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels) { GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, pixels ? emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, internalFormat) : null); } function _emscripten_glTexParameterf(x0, x1, x2) { GLctx['texParameterf'](x0, x1, x2) } function _emscripten_glTexParameterfv(target, pname, params) { var param = HEAPF32[((params)>>2)]; GLctx.texParameterf(target, pname, param); } function _emscripten_glTexParameteri(x0, x1, x2) { GLctx['texParameteri'](x0, x1, x2) } function _emscripten_glTexParameteriv(target, pname, params) { var param = HEAP32[((params)>>2)]; GLctx.texParameteri(target, pname, param); } function _emscripten_glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels) { var pixelData = null; if (pixels) pixelData = emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, 0); GLctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixelData); } function _emscripten_glUniform1f(location, v0) { GLctx.uniform1f(GL.uniforms[location], v0); } var miniTempWebGLFloatBuffers=[]; function _emscripten_glUniform1fv(location, count, value) { if (count <= 288) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[count-1]; for (var i = 0; i < count; ++i) { view[i] = HEAPF32[(((value)+(4*i))>>2)]; } } else { var view = HEAPF32.subarray((value)>>2, (value+count*4)>>2); } GLctx.uniform1fv(GL.uniforms[location], view); } function _emscripten_glUniform1i(location, v0) { GLctx.uniform1i(GL.uniforms[location], v0); } var __miniTempWebGLIntBuffers=[]; function _emscripten_glUniform1iv(location, count, value) { if (count <= 288) { // avoid allocation when uploading few enough uniforms var view = __miniTempWebGLIntBuffers[count-1]; for (var i = 0; i < count; ++i) { view[i] = HEAP32[(((value)+(4*i))>>2)]; } } else { var view = HEAP32.subarray((value)>>2, (value+count*4)>>2); } GLctx.uniform1iv(GL.uniforms[location], view); } function _emscripten_glUniform2f(location, v0, v1) { GLctx.uniform2f(GL.uniforms[location], v0, v1); } function _emscripten_glUniform2fv(location, count, value) { if (count <= 144) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[2*count-1]; for (var i = 0; i < 2*count; i += 2) { view[i] = HEAPF32[(((value)+(4*i))>>2)]; view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)]; } } else { var view = HEAPF32.subarray((value)>>2, (value+count*8)>>2); } GLctx.uniform2fv(GL.uniforms[location], view); } function _emscripten_glUniform2i(location, v0, v1) { GLctx.uniform2i(GL.uniforms[location], v0, v1); } function _emscripten_glUniform2iv(location, count, value) { if (count <= 144) { // avoid allocation when uploading few enough uniforms var view = __miniTempWebGLIntBuffers[2*count-1]; for (var i = 0; i < 2*count; i += 2) { view[i] = HEAP32[(((value)+(4*i))>>2)]; view[i+1] = HEAP32[(((value)+(4*i+4))>>2)]; } } else { var view = HEAP32.subarray((value)>>2, (value+count*8)>>2); } GLctx.uniform2iv(GL.uniforms[location], view); } function _emscripten_glUniform3f(location, v0, v1, v2) { GLctx.uniform3f(GL.uniforms[location], v0, v1, v2); } function _emscripten_glUniform3fv(location, count, value) { if (count <= 96) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[3*count-1]; for (var i = 0; i < 3*count; i += 3) { view[i] = HEAPF32[(((value)+(4*i))>>2)]; view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)]; view[i+2] = HEAPF32[(((value)+(4*i+8))>>2)]; } } else { var view = HEAPF32.subarray((value)>>2, (value+count*12)>>2); } GLctx.uniform3fv(GL.uniforms[location], view); } function _emscripten_glUniform3i(location, v0, v1, v2) { GLctx.uniform3i(GL.uniforms[location], v0, v1, v2); } function _emscripten_glUniform3iv(location, count, value) { if (count <= 96) { // avoid allocation when uploading few enough uniforms var view = __miniTempWebGLIntBuffers[3*count-1]; for (var i = 0; i < 3*count; i += 3) { view[i] = HEAP32[(((value)+(4*i))>>2)]; view[i+1] = HEAP32[(((value)+(4*i+4))>>2)]; view[i+2] = HEAP32[(((value)+(4*i+8))>>2)]; } } else { var view = HEAP32.subarray((value)>>2, (value+count*12)>>2); } GLctx.uniform3iv(GL.uniforms[location], view); } function _emscripten_glUniform4f(location, v0, v1, v2, v3) { GLctx.uniform4f(GL.uniforms[location], v0, v1, v2, v3); } function _emscripten_glUniform4fv(location, count, value) { if (count <= 72) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[4*count-1]; // hoist the heap out of the loop for size and for pthreads+growth. var heap = HEAPF32; value >>= 2; for (var i = 0; i < 4 * count; i += 4) { var dst = value + i; view[i] = heap[dst]; view[i + 1] = heap[dst + 1]; view[i + 2] = heap[dst + 2]; view[i + 3] = heap[dst + 3]; } } else { var view = HEAPF32.subarray((value)>>2, (value+count*16)>>2); } GLctx.uniform4fv(GL.uniforms[location], view); } function _emscripten_glUniform4i(location, v0, v1, v2, v3) { GLctx.uniform4i(GL.uniforms[location], v0, v1, v2, v3); } function _emscripten_glUniform4iv(location, count, value) { if (count <= 72) { // avoid allocation when uploading few enough uniforms var view = __miniTempWebGLIntBuffers[4*count-1]; for (var i = 0; i < 4*count; i += 4) { view[i] = HEAP32[(((value)+(4*i))>>2)]; view[i+1] = HEAP32[(((value)+(4*i+4))>>2)]; view[i+2] = HEAP32[(((value)+(4*i+8))>>2)]; view[i+3] = HEAP32[(((value)+(4*i+12))>>2)]; } } else { var view = HEAP32.subarray((value)>>2, (value+count*16)>>2); } GLctx.uniform4iv(GL.uniforms[location], view); } function _emscripten_glUniformMatrix2fv(location, count, transpose, value) { if (count <= 72) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[4*count-1]; for (var i = 0; i < 4*count; i += 4) { view[i] = HEAPF32[(((value)+(4*i))>>2)]; view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)]; view[i+2] = HEAPF32[(((value)+(4*i+8))>>2)]; view[i+3] = HEAPF32[(((value)+(4*i+12))>>2)]; } } else { var view = HEAPF32.subarray((value)>>2, (value+count*16)>>2); } GLctx.uniformMatrix2fv(GL.uniforms[location], !!transpose, view); } function _emscripten_glUniformMatrix3fv(location, count, transpose, value) { if (count <= 32) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[9*count-1]; for (var i = 0; i < 9*count; i += 9) { view[i] = HEAPF32[(((value)+(4*i))>>2)]; view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)]; view[i+2] = HEAPF32[(((value)+(4*i+8))>>2)]; view[i+3] = HEAPF32[(((value)+(4*i+12))>>2)]; view[i+4] = HEAPF32[(((value)+(4*i+16))>>2)]; view[i+5] = HEAPF32[(((value)+(4*i+20))>>2)]; view[i+6] = HEAPF32[(((value)+(4*i+24))>>2)]; view[i+7] = HEAPF32[(((value)+(4*i+28))>>2)]; view[i+8] = HEAPF32[(((value)+(4*i+32))>>2)]; } } else { var view = HEAPF32.subarray((value)>>2, (value+count*36)>>2); } GLctx.uniformMatrix3fv(GL.uniforms[location], !!transpose, view); } function _emscripten_glUniformMatrix4fv(location, count, transpose, value) { if (count <= 18) { // avoid allocation when uploading few enough uniforms var view = miniTempWebGLFloatBuffers[16*count-1]; // hoist the heap out of the loop for size and for pthreads+growth. var heap = HEAPF32; value >>= 2; for (var i = 0; i < 16 * count; i += 16) { var dst = value + i; view[i] = heap[dst]; view[i + 1] = heap[dst + 1]; view[i + 2] = heap[dst + 2]; view[i + 3] = heap[dst + 3]; view[i + 4] = heap[dst + 4]; view[i + 5] = heap[dst + 5]; view[i + 6] = heap[dst + 6]; view[i + 7] = heap[dst + 7]; view[i + 8] = heap[dst + 8]; view[i + 9] = heap[dst + 9]; view[i + 10] = heap[dst + 10]; view[i + 11] = heap[dst + 11]; view[i + 12] = heap[dst + 12]; view[i + 13] = heap[dst + 13]; view[i + 14] = heap[dst + 14]; view[i + 15] = heap[dst + 15]; } } else { var view = HEAPF32.subarray((value)>>2, (value+count*64)>>2); } GLctx.uniformMatrix4fv(GL.uniforms[location], !!transpose, view); } function _emscripten_glUseProgram(program) { GLctx.useProgram(GL.programs[program]); } function _emscripten_glValidateProgram(program) { GLctx.validateProgram(GL.programs[program]); } function _emscripten_glVertexAttrib1f(x0, x1) { GLctx['vertexAttrib1f'](x0, x1) } function _emscripten_glVertexAttrib1fv(index, v) { GLctx.vertexAttrib1f(index, HEAPF32[v>>2]); } function _emscripten_glVertexAttrib2f(x0, x1, x2) { GLctx['vertexAttrib2f'](x0, x1, x2) } function _emscripten_glVertexAttrib2fv(index, v) { GLctx.vertexAttrib2f(index, HEAPF32[v>>2], HEAPF32[v+4>>2]); } function _emscripten_glVertexAttrib3f(x0, x1, x2, x3) { GLctx['vertexAttrib3f'](x0, x1, x2, x3) } function _emscripten_glVertexAttrib3fv(index, v) { GLctx.vertexAttrib3f(index, HEAPF32[v>>2], HEAPF32[v+4>>2], HEAPF32[v+8>>2]); } function _emscripten_glVertexAttrib4f(x0, x1, x2, x3, x4) { GLctx['vertexAttrib4f'](x0, x1, x2, x3, x4) } function _emscripten_glVertexAttrib4fv(index, v) { GLctx.vertexAttrib4f(index, HEAPF32[v>>2], HEAPF32[v+4>>2], HEAPF32[v+8>>2], HEAPF32[v+12>>2]); } function _emscripten_glVertexAttribDivisorANGLE(index, divisor) { GLctx['vertexAttribDivisor'](index, divisor); } function _emscripten_glVertexAttribPointer(index, size, type, normalized, stride, ptr) { GLctx.vertexAttribPointer(index, size, type, !!normalized, stride, ptr); } function _emscripten_glViewport(x0, x1, x2, x3) { GLctx['viewport'](x0, x1, x2, x3) } function _emscripten_has_asyncify() { return 0; } function _emscripten_memcpy_big(dest, src, num) { HEAPU8.copyWithin(dest, src, src + num); } function doRequestFullscreen(target, strategy) { if (!JSEvents.fullscreenEnabled()) return -1; target = findEventTarget(target); if (!target) return -4; if (!target.requestFullscreen && !target.webkitRequestFullscreen ) { return -3; } var canPerformRequests = JSEvents.canPerformEventHandlerRequests(); // Queue this function call if we're not currently in an event handler and the user saw it appropriate to do so. if (!canPerformRequests) { if (strategy.deferUntilInEventHandler) { JSEvents.deferCall(_JSEvents_requestFullscreen, 1 /* priority over pointer lock */, [target, strategy]); return 1; } else { return -2; } } return _JSEvents_requestFullscreen(target, strategy); } function _emscripten_request_fullscreen_strategy(target, deferUntilInEventHandler, fullscreenStrategy) { var strategy = { scaleMode: HEAP32[((fullscreenStrategy)>>2)], canvasResolutionScaleMode: HEAP32[(((fullscreenStrategy)+(4))>>2)], filteringMode: HEAP32[(((fullscreenStrategy)+(8))>>2)], deferUntilInEventHandler: deferUntilInEventHandler, canvasResizedCallback: HEAP32[(((fullscreenStrategy)+(12))>>2)], canvasResizedCallbackUserData: HEAP32[(((fullscreenStrategy)+(16))>>2)] }; return doRequestFullscreen(target, strategy); } function _emscripten_request_pointerlock(target, deferUntilInEventHandler) { target = findEventTarget(target); if (!target) return -4; if (!target.requestPointerLock && !target.msRequestPointerLock ) { return -1; } var canPerformRequests = JSEvents.canPerformEventHandlerRequests(); // Queue this function call if we're not currently in an event handler and the user saw it appropriate to do so. if (!canPerformRequests) { if (deferUntilInEventHandler) { JSEvents.deferCall(requestPointerLock, 2 /* priority below fullscreen */, [target]); return 1; } else { return -2; } } return requestPointerLock(target); } function _emscripten_get_heap_size() { return HEAPU8.length; } function abortOnCannotGrowMemory(requestedSize) { abort('Cannot enlarge memory arrays to size ' + requestedSize + ' bytes (OOM). Either (1) compile with -s INITIAL_MEMORY=X with X higher than the current value ' + HEAP8.length + ', (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 '); } function _emscripten_resize_heap(requestedSize) { abortOnCannotGrowMemory(requestedSize); } function _emscripten_sample_gamepad_data() { return (JSEvents.lastGamepadState = (navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : null))) ? 0 : -1; } function registerBeforeUnloadEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) { var beforeUnloadEventHandlerFunc = function(ev) { var e = ev || event; // Note: This is always called on the main browser thread, since it needs synchronously return a value! var confirmationMessage = wasmTable.get(callbackfunc)(eventTypeId, 0, userData); if (confirmationMessage) { confirmationMessage = UTF8ToString(confirmationMessage); } if (confirmationMessage) { e.preventDefault(); e.returnValue = confirmationMessage; return confirmationMessage; } }; var eventHandler = { target: findEventTarget(target), eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: beforeUnloadEventHandlerFunc, useCapture: useCapture }; JSEvents.registerOrRemoveHandler(eventHandler); } function _emscripten_set_beforeunload_callback_on_thread(userData, callbackfunc, targetThread) { if (typeof onbeforeunload === 'undefined') return -1; // beforeunload callback can only be registered on the main browser thread, because the page will go away immediately after returning from the handler, // and there is no time to start proxying it anywhere. if (targetThread !== 1) return -5; registerBeforeUnloadEventCallback(2, userData, true, callbackfunc, 28, "beforeunload"); return 0; } function registerFocusEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { if (!JSEvents.focusEvent) JSEvents.focusEvent = _malloc( 256 ); var focusEventHandlerFunc = function(ev) { var e = ev || event; var nodeName = JSEvents.getNodeNameForTarget(e.target); var id = e.target.id ? e.target.id : ''; var focusEvent = JSEvents.focusEvent; stringToUTF8(nodeName, focusEvent + 0, 128); stringToUTF8(id, focusEvent + 128, 128); if (wasmTable.get(callbackfunc)(eventTypeId, focusEvent, userData)) e.preventDefault(); }; var eventHandler = { target: findEventTarget(target), eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: focusEventHandlerFunc, useCapture: useCapture }; JSEvents.registerOrRemoveHandler(eventHandler); } function _emscripten_set_blur_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { registerFocusEventCallback(target, userData, useCapture, callbackfunc, 12, "blur", targetThread); return 0; } function _emscripten_set_element_css_size(target, width, height) { target = findEventTarget(target); if (!target) return -4; target.style.width = width + "px"; target.style.height = height + "px"; return 0; } function _emscripten_set_focus_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { registerFocusEventCallback(target, userData, useCapture, callbackfunc, 13, "focus", targetThread); return 0; } function fillFullscreenChangeEventData(eventStruct) { var fullscreenElement = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement; var isFullscreen = !!fullscreenElement; /** @suppress{checkTypes} */ HEAP32[((eventStruct)>>2)] = isFullscreen; HEAP32[(((eventStruct)+(4))>>2)] = JSEvents.fullscreenEnabled(); // If transitioning to fullscreen, report info about the element that is now fullscreen. // If transitioning to windowed mode, report info about the element that just was fullscreen. var reportedElement = isFullscreen ? fullscreenElement : JSEvents.previousFullscreenElement; var nodeName = JSEvents.getNodeNameForTarget(reportedElement); var id = (reportedElement && reportedElement.id) ? reportedElement.id : ''; stringToUTF8(nodeName, eventStruct + 8, 128); stringToUTF8(id, eventStruct + 136, 128); HEAP32[(((eventStruct)+(264))>>2)] = reportedElement ? reportedElement.clientWidth : 0; HEAP32[(((eventStruct)+(268))>>2)] = reportedElement ? reportedElement.clientHeight : 0; HEAP32[(((eventStruct)+(272))>>2)] = screen.width; HEAP32[(((eventStruct)+(276))>>2)] = screen.height; if (isFullscreen) { JSEvents.previousFullscreenElement = fullscreenElement; } } function registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { if (!JSEvents.fullscreenChangeEvent) JSEvents.fullscreenChangeEvent = _malloc( 280 ); var fullscreenChangeEventhandlerFunc = function(ev) { var e = ev || event; var fullscreenChangeEvent = JSEvents.fullscreenChangeEvent; fillFullscreenChangeEventData(fullscreenChangeEvent); if (wasmTable.get(callbackfunc)(eventTypeId, fullscreenChangeEvent, userData)) e.preventDefault(); }; var eventHandler = { target: target, eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: fullscreenChangeEventhandlerFunc, useCapture: useCapture }; JSEvents.registerOrRemoveHandler(eventHandler); } function _emscripten_set_fullscreenchange_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { if (!JSEvents.fullscreenEnabled()) return -1; target = findEventTarget(target); if (!target) return -4; registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, 19, "fullscreenchange", targetThread); // Unprefixed Fullscreen API shipped in Chromium 71 (https://bugs.chromium.org/p/chromium/issues/detail?id=383813) // As of Safari 13.0.3 on macOS Catalina 10.15.1 still ships with prefixed webkitfullscreenchange. TODO: revisit this check once Safari ships unprefixed version. registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, 19, "webkitfullscreenchange", targetThread); return 0; } function registerGamepadEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { if (!JSEvents.gamepadEvent) JSEvents.gamepadEvent = _malloc( 1432 ); var gamepadEventHandlerFunc = function(ev) { var e = ev || event; var gamepadEvent = JSEvents.gamepadEvent; fillGamepadEventData(gamepadEvent, e["gamepad"]); if (wasmTable.get(callbackfunc)(eventTypeId, gamepadEvent, userData)) e.preventDefault(); }; var eventHandler = { target: findEventTarget(target), allowsDeferredCalls: true, eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: gamepadEventHandlerFunc, useCapture: useCapture }; JSEvents.registerOrRemoveHandler(eventHandler); } function _emscripten_set_gamepadconnected_callback_on_thread(userData, useCapture, callbackfunc, targetThread) { if (!navigator.getGamepads && !navigator.webkitGetGamepads) return -1; registerGamepadEventCallback(2, userData, useCapture, callbackfunc, 26, "gamepadconnected", targetThread); return 0; } function _emscripten_set_gamepaddisconnected_callback_on_thread(userData, useCapture, callbackfunc, targetThread) { if (!navigator.getGamepads && !navigator.webkitGetGamepads) return -1; registerGamepadEventCallback(2, userData, useCapture, callbackfunc, 27, "gamepaddisconnected", targetThread); return 0; } function registerKeyEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { if (!JSEvents.keyEvent) JSEvents.keyEvent = _malloc( 164 ); var keyEventHandlerFunc = function(e) { assert(e); var keyEventData = JSEvents.keyEvent; var idx = keyEventData >> 2; HEAP32[idx + 0] = e.location; HEAP32[idx + 1] = e.ctrlKey; HEAP32[idx + 2] = e.shiftKey; HEAP32[idx + 3] = e.altKey; HEAP32[idx + 4] = e.metaKey; HEAP32[idx + 5] = e.repeat; HEAP32[idx + 6] = e.charCode; HEAP32[idx + 7] = e.keyCode; HEAP32[idx + 8] = e.which; stringToUTF8(e.key || '', keyEventData + 36, 32); stringToUTF8(e.code || '', keyEventData + 68, 32); stringToUTF8(e.char || '', keyEventData + 100, 32); stringToUTF8(e.locale || '', keyEventData + 132, 32); if (wasmTable.get(callbackfunc)(eventTypeId, keyEventData, userData)) e.preventDefault(); }; var eventHandler = { target: findEventTarget(target), allowsDeferredCalls: true, eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: keyEventHandlerFunc, useCapture: useCapture }; JSEvents.registerOrRemoveHandler(eventHandler); } function _emscripten_set_keydown_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { registerKeyEventCallback(target, userData, useCapture, callbackfunc, 2, "keydown", targetThread); return 0; } function _emscripten_set_keypress_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { registerKeyEventCallback(target, userData, useCapture, callbackfunc, 1, "keypress", targetThread); return 0; } function _emscripten_set_keyup_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { registerKeyEventCallback(target, userData, useCapture, callbackfunc, 3, "keyup", targetThread); return 0; } function _emscripten_set_main_loop_arg(func, arg, fps, simulateInfiniteLoop) { var browserIterationFunc = function() { wasmTable.get(func)(arg); }; setMainLoop(browserIterationFunc, fps, simulateInfiniteLoop, arg); } function fillMouseEventData(eventStruct, e, target) { assert(eventStruct % 4 == 0); var idx = eventStruct >> 2; HEAP32[idx + 0] = e.screenX; HEAP32[idx + 1] = e.screenY; HEAP32[idx + 2] = e.clientX; HEAP32[idx + 3] = e.clientY; HEAP32[idx + 4] = e.ctrlKey; HEAP32[idx + 5] = e.shiftKey; HEAP32[idx + 6] = e.altKey; HEAP32[idx + 7] = e.metaKey; HEAP16[idx*2 + 16] = e.button; HEAP16[idx*2 + 17] = e.buttons; HEAP32[idx + 9] = e["movementX"] ; HEAP32[idx + 10] = e["movementY"] ; var rect = getBoundingClientRect(target); HEAP32[idx + 11] = e.clientX - rect.left; HEAP32[idx + 12] = e.clientY - rect.top; } function registerMouseEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { if (!JSEvents.mouseEvent) JSEvents.mouseEvent = _malloc( 64 ); target = findEventTarget(target); var mouseEventHandlerFunc = function(ev) { var e = ev || event; // TODO: Make this access thread safe, or this could update live while app is reading it. fillMouseEventData(JSEvents.mouseEvent, e, target); if (wasmTable.get(callbackfunc)(eventTypeId, JSEvents.mouseEvent, userData)) e.preventDefault(); }; var eventHandler = { target: target, allowsDeferredCalls: eventTypeString != 'mousemove' && eventTypeString != 'mouseenter' && eventTypeString != 'mouseleave', // Mouse move events do not allow fullscreen/pointer lock requests to be handled in them! eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: mouseEventHandlerFunc, useCapture: useCapture }; JSEvents.registerOrRemoveHandler(eventHandler); } function _emscripten_set_mousedown_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { registerMouseEventCallback(target, userData, useCapture, callbackfunc, 5, "mousedown", targetThread); return 0; } function _emscripten_set_mouseenter_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { registerMouseEventCallback(target, userData, useCapture, callbackfunc, 33, "mouseenter", targetThread); return 0; } function _emscripten_set_mouseleave_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { registerMouseEventCallback(target, userData, useCapture, callbackfunc, 34, "mouseleave", targetThread); return 0; } function _emscripten_set_mousemove_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { registerMouseEventCallback(target, userData, useCapture, callbackfunc, 8, "mousemove", targetThread); return 0; } function _emscripten_set_mouseup_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { registerMouseEventCallback(target, userData, useCapture, callbackfunc, 6, "mouseup", targetThread); return 0; } function fillPointerlockChangeEventData(eventStruct) { var pointerLockElement = document.pointerLockElement || document.mozPointerLockElement || document.webkitPointerLockElement || document.msPointerLockElement; var isPointerlocked = !!pointerLockElement; /** @suppress {checkTypes} */ HEAP32[((eventStruct)>>2)] = isPointerlocked; var nodeName = JSEvents.getNodeNameForTarget(pointerLockElement); var id = (pointerLockElement && pointerLockElement.id) ? pointerLockElement.id : ''; stringToUTF8(nodeName, eventStruct + 4, 128); stringToUTF8(id, eventStruct + 132, 128); } function registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { if (!JSEvents.pointerlockChangeEvent) JSEvents.pointerlockChangeEvent = _malloc( 260 ); var pointerlockChangeEventHandlerFunc = function(ev) { var e = ev || event; var pointerlockChangeEvent = JSEvents.pointerlockChangeEvent; fillPointerlockChangeEventData(pointerlockChangeEvent); if (wasmTable.get(callbackfunc)(eventTypeId, pointerlockChangeEvent, userData)) e.preventDefault(); }; var eventHandler = { target: target, eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: pointerlockChangeEventHandlerFunc, useCapture: useCapture }; JSEvents.registerOrRemoveHandler(eventHandler); } /** @suppress {missingProperties} */ function _emscripten_set_pointerlockchange_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { // TODO: Currently not supported in pthreads or in --proxy-to-worker mode. (In pthreads mode, document object is not defined) if (!document || !document.body || (!document.body.requestPointerLock && !document.body.mozRequestPointerLock && !document.body.webkitRequestPointerLock && !document.body.msRequestPointerLock)) { return -1; } target = findEventTarget(target); if (!target) return -4; registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, 20, "pointerlockchange", targetThread); registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, 20, "mozpointerlockchange", targetThread); registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, 20, "webkitpointerlockchange", targetThread); registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, 20, "mspointerlockchange", targetThread); return 0; } function registerUiEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { if (!JSEvents.uiEvent) JSEvents.uiEvent = _malloc( 36 ); target = findEventTarget(target); var uiEventHandlerFunc = function(ev) { var e = ev || event; if (e.target != target) { // Never take ui events such as scroll via a 'bubbled' route, but always from the direct element that // was targeted. Otherwise e.g. if app logs a message in response to a page scroll, the Emscripten log // message box could cause to scroll, generating a new (bubbled) scroll message, causing a new log print, // causing a new scroll, etc.. return; } var b = document.body; // Take document.body to a variable, Closure compiler does not outline access to it on its own. if (!b) { // During a page unload 'body' can be null, with "Cannot read property 'clientWidth' of null" being thrown return; } var uiEvent = JSEvents.uiEvent; HEAP32[((uiEvent)>>2)] = e.detail; HEAP32[(((uiEvent)+(4))>>2)] = b.clientWidth; HEAP32[(((uiEvent)+(8))>>2)] = b.clientHeight; HEAP32[(((uiEvent)+(12))>>2)] = innerWidth; HEAP32[(((uiEvent)+(16))>>2)] = innerHeight; HEAP32[(((uiEvent)+(20))>>2)] = outerWidth; HEAP32[(((uiEvent)+(24))>>2)] = outerHeight; HEAP32[(((uiEvent)+(28))>>2)] = pageXOffset; HEAP32[(((uiEvent)+(32))>>2)] = pageYOffset; if (wasmTable.get(callbackfunc)(eventTypeId, uiEvent, userData)) e.preventDefault(); }; var eventHandler = { target: target, eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: uiEventHandlerFunc, useCapture: useCapture }; JSEvents.registerOrRemoveHandler(eventHandler); } function _emscripten_set_resize_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { registerUiEventCallback(target, userData, useCapture, callbackfunc, 10, "resize", targetThread); return 0; } function registerTouchEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { if (!JSEvents.touchEvent) JSEvents.touchEvent = _malloc( 1684 ); target = findEventTarget(target); var touchEventHandlerFunc = function(e) { assert(e); var touches = {}; var et = e.touches; for(var i = 0; i < et.length; ++i) { var touch = et[i]; // Verify that browser does not recycle touch objects with old stale data, but reports new ones each time. assert(!touch.isChanged); assert(!touch.onTarget); touches[touch.identifier] = touch; } et = e.changedTouches; for(var i = 0; i < et.length; ++i) { var touch = et[i]; // Verify that browser does not recycle touch objects with old stale data, but reports new ones each time. assert(!touch.onTarget); touch.isChanged = 1; touches[touch.identifier] = touch; } et = e.targetTouches; for(var i = 0; i < et.length; ++i) { touches[et[i].identifier].onTarget = 1; } var touchEvent = JSEvents.touchEvent; var idx = touchEvent>>2; // Pre-shift the ptr to index to HEAP32 to save code size HEAP32[idx + 1] = e.ctrlKey; HEAP32[idx + 2] = e.shiftKey; HEAP32[idx + 3] = e.altKey; HEAP32[idx + 4] = e.metaKey; idx += 5; // Advance to the start of the touch array. var targetRect = getBoundingClientRect(target); var numTouches = 0; for(var i in touches) { var t = touches[i]; HEAP32[idx + 0] = t.identifier; HEAP32[idx + 1] = t.screenX; HEAP32[idx + 2] = t.screenY; HEAP32[idx + 3] = t.clientX; HEAP32[idx + 4] = t.clientY; HEAP32[idx + 5] = t.pageX; HEAP32[idx + 6] = t.pageY; HEAP32[idx + 7] = t.isChanged; HEAP32[idx + 8] = t.onTarget; HEAP32[idx + 9] = t.clientX - targetRect.left; HEAP32[idx + 10] = t.clientY - targetRect.top; idx += 13; if (++numTouches > 31) { break; } } HEAP32[((touchEvent)>>2)] = numTouches; if (wasmTable.get(callbackfunc)(eventTypeId, touchEvent, userData)) e.preventDefault(); }; var eventHandler = { target: target, allowsDeferredCalls: eventTypeString == 'touchstart' || eventTypeString == 'touchend', eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: touchEventHandlerFunc, useCapture: useCapture }; JSEvents.registerOrRemoveHandler(eventHandler); } function _emscripten_set_touchcancel_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { registerTouchEventCallback(target, userData, useCapture, callbackfunc, 25, "touchcancel", targetThread); return 0; } function _emscripten_set_touchend_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { registerTouchEventCallback(target, userData, useCapture, callbackfunc, 23, "touchend", targetThread); return 0; } function _emscripten_set_touchmove_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { registerTouchEventCallback(target, userData, useCapture, callbackfunc, 24, "touchmove", targetThread); return 0; } function _emscripten_set_touchstart_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { registerTouchEventCallback(target, userData, useCapture, callbackfunc, 22, "touchstart", targetThread); return 0; } function fillVisibilityChangeEventData(eventStruct) { var visibilityStates = [ "hidden", "visible", "prerender", "unloaded" ]; var visibilityState = visibilityStates.indexOf(document.visibilityState); // Assigning a boolean to HEAP32 with expected type coercion. /** @suppress {checkTypes} */ HEAP32[((eventStruct)>>2)] = document.hidden; HEAP32[(((eventStruct)+(4))>>2)] = visibilityState; } function registerVisibilityChangeEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { if (!JSEvents.visibilityChangeEvent) JSEvents.visibilityChangeEvent = _malloc( 8 ); var visibilityChangeEventHandlerFunc = function(ev) { var e = ev || event; var visibilityChangeEvent = JSEvents.visibilityChangeEvent; fillVisibilityChangeEventData(visibilityChangeEvent); if (wasmTable.get(callbackfunc)(eventTypeId, visibilityChangeEvent, userData)) e.preventDefault(); }; var eventHandler = { target: target, eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: visibilityChangeEventHandlerFunc, useCapture: useCapture }; JSEvents.registerOrRemoveHandler(eventHandler); } function _emscripten_set_visibilitychange_callback_on_thread(userData, useCapture, callbackfunc, targetThread) { if (!specialHTMLTargets[1]) { return -4; } registerVisibilityChangeEventCallback(specialHTMLTargets[1], userData, useCapture, callbackfunc, 21, "visibilitychange", targetThread); return 0; } function registerWheelEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) { if (!JSEvents.wheelEvent) JSEvents.wheelEvent = _malloc( 96 ); // The DOM Level 3 events spec event 'wheel' var wheelHandlerFunc = function(ev) { var e = ev || event; var wheelEvent = JSEvents.wheelEvent; fillMouseEventData(wheelEvent, e, target); HEAPF64[(((wheelEvent)+(64))>>3)] = e["deltaX"]; HEAPF64[(((wheelEvent)+(72))>>3)] = e["deltaY"]; HEAPF64[(((wheelEvent)+(80))>>3)] = e["deltaZ"]; HEAP32[(((wheelEvent)+(88))>>2)] = e["deltaMode"]; if (wasmTable.get(callbackfunc)(eventTypeId, wheelEvent, userData)) e.preventDefault(); }; var eventHandler = { target: target, allowsDeferredCalls: true, eventTypeString: eventTypeString, callbackfunc: callbackfunc, handlerFunc: wheelHandlerFunc, useCapture: useCapture }; JSEvents.registerOrRemoveHandler(eventHandler); } function _emscripten_set_wheel_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) { target = findEventTarget(target); if (typeof target.onwheel !== 'undefined') { registerWheelEventCallback(target, userData, useCapture, callbackfunc, 9, "wheel", targetThread); return 0; } else { return -1; } } function _emscripten_sleep() { throw 'Please compile your program with async support in order to use asynchronous operations like emscripten_sleep'; } function _emscripten_thread_sleep(msecs) { var start = _emscripten_get_now(); while (_emscripten_get_now() - start < msecs) { // Do nothing. } } var ENV={}; function getExecutableName() { return thisProgram || './this.program'; } function getEnvStrings() { if (!getEnvStrings.strings) { // Default values. // Browser language detection #8751 var lang = ((typeof navigator === 'object' && navigator.languages && navigator.languages[0]) || 'C').replace('-', '_') + '.UTF-8'; var env = { 'USER': 'web_user', 'LOGNAME': 'web_user', 'PATH': '/', 'PWD': '/', 'HOME': '/home/web_user', 'LANG': lang, '_': getExecutableName() }; // Apply the user-provided values, if any. for (var x in ENV) { env[x] = ENV[x]; } var strings = []; for (var x in env) { strings.push(x + '=' + env[x]); } getEnvStrings.strings = strings; } return getEnvStrings.strings; } function _environ_get(__environ, environ_buf) {try { var bufSize = 0; getEnvStrings().forEach(function(string, i) { var ptr = environ_buf + bufSize; HEAP32[(((__environ)+(i * 4))>>2)] = ptr; writeAsciiToMemory(string, ptr); bufSize += string.length + 1; }); return 0; } catch (e) { if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); return e.errno; } } function _environ_sizes_get(penviron_count, penviron_buf_size) {try { var strings = getEnvStrings(); HEAP32[((penviron_count)>>2)] = strings.length; var bufSize = 0; strings.forEach(function(string) { bufSize += string.length + 1; }); HEAP32[((penviron_buf_size)>>2)] = bufSize; return 0; } catch (e) { if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); return e.errno; } } function _exit(status) { // void _exit(int status); // http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html exit(status); } function _fd_close(fd) {try { var stream = SYSCALLS.getStreamFromFD(fd); FS.close(stream); return 0; } catch (e) { if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); return e.errno; } } function _fd_read(fd, iov, iovcnt, pnum) {try { var stream = SYSCALLS.getStreamFromFD(fd); var num = SYSCALLS.doReadv(stream, iov, iovcnt); HEAP32[((pnum)>>2)] = num return 0; } catch (e) { if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); return e.errno; } } function _fd_seek(fd, offset_low, offset_high, whence, newOffset) {try { var stream = SYSCALLS.getStreamFromFD(fd); var HIGH_OFFSET = 0x100000000; // 2^32 // use an unsigned operator on low and shift high by 32-bits var offset = offset_high * HIGH_OFFSET + (offset_low >>> 0); var DOUBLE_LIMIT = 0x20000000000000; // 2^53 // we also check for equality since DOUBLE_LIMIT + 1 == DOUBLE_LIMIT if (offset <= -DOUBLE_LIMIT || offset >= DOUBLE_LIMIT) { return -61; } FS.llseek(stream, offset, whence); (tempI64 = [stream.position>>>0,(tempDouble=stream.position,(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math.min((+(Math.floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[((newOffset)>>2)] = tempI64[0],HEAP32[(((newOffset)+(4))>>2)] = tempI64[1]); if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null; // reset readdir state return 0; } catch (e) { if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); return e.errno; } } function _fd_write(fd, iov, iovcnt, pnum) {try { var stream = SYSCALLS.getStreamFromFD(fd); var num = SYSCALLS.doWritev(stream, iov, iovcnt); HEAP32[((pnum)>>2)] = num return 0; } catch (e) { if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); return e.errno; } } function _gettimeofday(ptr) { var now = Date.now(); HEAP32[((ptr)>>2)] = (now/1000)|0; // seconds HEAP32[(((ptr)+(4))>>2)] = ((now % 1000)*1000)|0; // microseconds return 0; } function _setTempRet0($i) { setTempRet0(($i) | 0); } function _sigaction(signum, act, oldact) { //int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); err('Calling stub instead of sigaction()'); return 0; } var __sigalrm_handler=0; function _signal(sig, func) { if (sig == 14) { __sigalrm_handler = func; } else { err('Calling stub instead of signal()'); } return 0; } var readAsmConstArgsArray=[]; function readAsmConstArgs(sigPtr, buf) { // Nobody should have mutated _readAsmConstArgsArray underneath us to be something else than an array. assert(Array.isArray(readAsmConstArgsArray)); // The input buffer is allocated on the stack, so it must be stack-aligned. assert(buf % 16 == 0); readAsmConstArgsArray.length = 0; var ch; // Most arguments are i32s, so shift the buffer pointer so it is a plain // index into HEAP32. buf >>= 2; while (ch = HEAPU8[sigPtr++]) { assert(ch === 100/*'d'*/ || ch === 102/*'f'*/ || ch === 105 /*'i'*/); // A double takes two 32-bit slots, and must also be aligned - the backend // will emit padding to avoid that. var double = ch < 105; if (double && (buf & 1)) buf++; readAsmConstArgsArray.push(double ? HEAPF64[buf++ >> 1] : HEAP32[buf]); ++buf; } return readAsmConstArgsArray; } var FSNode = /** @constructor */ function(parent, name, mode, rdev) { if (!parent) { parent = this; // root node sets parent to itself } this.parent = parent; this.mount = parent.mount; this.mounted = null; this.id = FS.nextInode++; this.name = name; this.mode = mode; this.node_ops = {}; this.stream_ops = {}; this.rdev = rdev; }; var readMode = 292/*292*/ | 73/*73*/; var writeMode = 146/*146*/; Object.defineProperties(FSNode.prototype, { read: { get: /** @this{FSNode} */function() { return (this.mode & readMode) === readMode; }, set: /** @this{FSNode} */function(val) { val ? this.mode |= readMode : this.mode &= ~readMode; } }, write: { get: /** @this{FSNode} */function() { return (this.mode & writeMode) === writeMode; }, set: /** @this{FSNode} */function(val) { val ? this.mode |= writeMode : this.mode &= ~writeMode; } }, isFolder: { get: /** @this{FSNode} */function() { return FS.isDir(this.mode); } }, isDevice: { get: /** @this{FSNode} */function() { return FS.isChrdev(this.mode); } } }); FS.FSNode = FSNode; FS.staticInit();; Module["requestFullscreen"] = function Module_requestFullscreen(lockPointer, resizeCanvas) { Browser.requestFullscreen(lockPointer, resizeCanvas) }; Module["requestFullScreen"] = function Module_requestFullScreen() { Browser.requestFullScreen() }; Module["requestAnimationFrame"] = function Module_requestAnimationFrame(func) { Browser.requestAnimationFrame(func) }; Module["setCanvasSize"] = function Module_setCanvasSize(width, height, noUpdates) { Browser.setCanvasSize(width, height, noUpdates) }; Module["pauseMainLoop"] = function Module_pauseMainLoop() { Browser.mainLoop.pause() }; Module["resumeMainLoop"] = function Module_resumeMainLoop() { Browser.mainLoop.resume() }; Module["getUserMedia"] = function Module_getUserMedia() { Browser.getUserMedia() } Module["createContext"] = function Module_createContext(canvas, useWebGL, setInModule, webGLContextAttributes) { return Browser.createContext(canvas, useWebGL, setInModule, webGLContextAttributes) }; var GLctx;; for (var i = 0; i < 32; ++i) tempFixedLengthArray.push(new Array(i));; var miniTempWebGLFloatBuffersStorage = new Float32Array(288); for (/**@suppress{duplicate}*/var i = 0; i < 288; ++i) { miniTempWebGLFloatBuffers[i] = miniTempWebGLFloatBuffersStorage.subarray(0, i+1); } ; var __miniTempWebGLIntBuffersStorage = new Int32Array(288); for (/**@suppress{duplicate}*/var i = 0; i < 288; ++i) { __miniTempWebGLIntBuffers[i] = __miniTempWebGLIntBuffersStorage.subarray(0, i+1); } ; var ASSERTIONS = true; /** @type {function(string, boolean=, number=)} */ function intArrayFromString(stringy, dontAddNull, length) { var len = length > 0 ? length : lengthBytesUTF8(stringy)+1; var u8array = new Array(len); var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length); if (dontAddNull) u8array.length = numBytesWritten; return u8array; } function intArrayToString(array) { var ret = []; for (var i = 0; i < array.length; i++) { var chr = array[i]; if (chr > 0xFF) { if (ASSERTIONS) { assert(false, 'Character code ' + chr + ' (' + String.fromCharCode(chr) + ') at offset ' + i + ' not in 0x00-0xFF.'); } chr &= 0xFF; } ret.push(String.fromCharCode(chr)); } return ret.join(''); } var asmLibraryArg = { "__assert_fail": ___assert_fail, "__sys_fcntl64": ___sys_fcntl64, "__sys_ioctl": ___sys_ioctl, "__sys_open": ___sys_open, "clock_gettime": _clock_gettime, "dlclose": _dlclose, "eglBindAPI": _eglBindAPI, "eglChooseConfig": _eglChooseConfig, "eglCreateContext": _eglCreateContext, "eglCreateWindowSurface": _eglCreateWindowSurface, "eglDestroyContext": _eglDestroyContext, "eglDestroySurface": _eglDestroySurface, "eglGetConfigAttrib": _eglGetConfigAttrib, "eglGetDisplay": _eglGetDisplay, "eglGetError": _eglGetError, "eglInitialize": _eglInitialize, "eglMakeCurrent": _eglMakeCurrent, "eglQueryString": _eglQueryString, "eglSwapBuffers": _eglSwapBuffers, "eglSwapInterval": _eglSwapInterval, "eglTerminate": _eglTerminate, "eglWaitGL": _eglWaitGL, "eglWaitNative": _eglWaitNative, "emscripten_asm_const_int": _emscripten_asm_const_int, "emscripten_async_wget": _emscripten_async_wget, "emscripten_exit_fullscreen": _emscripten_exit_fullscreen, "emscripten_exit_pointerlock": _emscripten_exit_pointerlock, "emscripten_get_device_pixel_ratio": _emscripten_get_device_pixel_ratio, "emscripten_get_element_css_size": _emscripten_get_element_css_size, "emscripten_get_gamepad_status": _emscripten_get_gamepad_status, "emscripten_get_num_gamepads": _emscripten_get_num_gamepads, "emscripten_glActiveTexture": _emscripten_glActiveTexture, "emscripten_glAttachShader": _emscripten_glAttachShader, "emscripten_glBeginQueryEXT": _emscripten_glBeginQueryEXT, "emscripten_glBindAttribLocation": _emscripten_glBindAttribLocation, "emscripten_glBindBuffer": _emscripten_glBindBuffer, "emscripten_glBindFramebuffer": _emscripten_glBindFramebuffer, "emscripten_glBindRenderbuffer": _emscripten_glBindRenderbuffer, "emscripten_glBindTexture": _emscripten_glBindTexture, "emscripten_glBindVertexArrayOES": _emscripten_glBindVertexArrayOES, "emscripten_glBlendColor": _emscripten_glBlendColor, "emscripten_glBlendEquation": _emscripten_glBlendEquation, "emscripten_glBlendEquationSeparate": _emscripten_glBlendEquationSeparate, "emscripten_glBlendFunc": _emscripten_glBlendFunc, "emscripten_glBlendFuncSeparate": _emscripten_glBlendFuncSeparate, "emscripten_glBufferData": _emscripten_glBufferData, "emscripten_glBufferSubData": _emscripten_glBufferSubData, "emscripten_glCheckFramebufferStatus": _emscripten_glCheckFramebufferStatus, "emscripten_glClear": _emscripten_glClear, "emscripten_glClearColor": _emscripten_glClearColor, "emscripten_glClearDepthf": _emscripten_glClearDepthf, "emscripten_glClearStencil": _emscripten_glClearStencil, "emscripten_glColorMask": _emscripten_glColorMask, "emscripten_glCompileShader": _emscripten_glCompileShader, "emscripten_glCompressedTexImage2D": _emscripten_glCompressedTexImage2D, "emscripten_glCompressedTexSubImage2D": _emscripten_glCompressedTexSubImage2D, "emscripten_glCopyTexImage2D": _emscripten_glCopyTexImage2D, "emscripten_glCopyTexSubImage2D": _emscripten_glCopyTexSubImage2D, "emscripten_glCreateProgram": _emscripten_glCreateProgram, "emscripten_glCreateShader": _emscripten_glCreateShader, "emscripten_glCullFace": _emscripten_glCullFace, "emscripten_glDeleteBuffers": _emscripten_glDeleteBuffers, "emscripten_glDeleteFramebuffers": _emscripten_glDeleteFramebuffers, "emscripten_glDeleteProgram": _emscripten_glDeleteProgram, "emscripten_glDeleteQueriesEXT": _emscripten_glDeleteQueriesEXT, "emscripten_glDeleteRenderbuffers": _emscripten_glDeleteRenderbuffers, "emscripten_glDeleteShader": _emscripten_glDeleteShader, "emscripten_glDeleteTextures": _emscripten_glDeleteTextures, "emscripten_glDeleteVertexArraysOES": _emscripten_glDeleteVertexArraysOES, "emscripten_glDepthFunc": _emscripten_glDepthFunc, "emscripten_glDepthMask": _emscripten_glDepthMask, "emscripten_glDepthRangef": _emscripten_glDepthRangef, "emscripten_glDetachShader": _emscripten_glDetachShader, "emscripten_glDisable": _emscripten_glDisable, "emscripten_glDisableVertexAttribArray": _emscripten_glDisableVertexAttribArray, "emscripten_glDrawArrays": _emscripten_glDrawArrays, "emscripten_glDrawArraysInstancedANGLE": _emscripten_glDrawArraysInstancedANGLE, "emscripten_glDrawBuffersWEBGL": _emscripten_glDrawBuffersWEBGL, "emscripten_glDrawElements": _emscripten_glDrawElements, "emscripten_glDrawElementsInstancedANGLE": _emscripten_glDrawElementsInstancedANGLE, "emscripten_glEnable": _emscripten_glEnable, "emscripten_glEnableVertexAttribArray": _emscripten_glEnableVertexAttribArray, "emscripten_glEndQueryEXT": _emscripten_glEndQueryEXT, "emscripten_glFinish": _emscripten_glFinish, "emscripten_glFlush": _emscripten_glFlush, "emscripten_glFramebufferRenderbuffer": _emscripten_glFramebufferRenderbuffer, "emscripten_glFramebufferTexture2D": _emscripten_glFramebufferTexture2D, "emscripten_glFrontFace": _emscripten_glFrontFace, "emscripten_glGenBuffers": _emscripten_glGenBuffers, "emscripten_glGenFramebuffers": _emscripten_glGenFramebuffers, "emscripten_glGenQueriesEXT": _emscripten_glGenQueriesEXT, "emscripten_glGenRenderbuffers": _emscripten_glGenRenderbuffers, "emscripten_glGenTextures": _emscripten_glGenTextures, "emscripten_glGenVertexArraysOES": _emscripten_glGenVertexArraysOES, "emscripten_glGenerateMipmap": _emscripten_glGenerateMipmap, "emscripten_glGetActiveAttrib": _emscripten_glGetActiveAttrib, "emscripten_glGetActiveUniform": _emscripten_glGetActiveUniform, "emscripten_glGetAttachedShaders": _emscripten_glGetAttachedShaders, "emscripten_glGetAttribLocation": _emscripten_glGetAttribLocation, "emscripten_glGetBooleanv": _emscripten_glGetBooleanv, "emscripten_glGetBufferParameteriv": _emscripten_glGetBufferParameteriv, "emscripten_glGetError": _emscripten_glGetError, "emscripten_glGetFloatv": _emscripten_glGetFloatv, "emscripten_glGetFramebufferAttachmentParameteriv": _emscripten_glGetFramebufferAttachmentParameteriv, "emscripten_glGetIntegerv": _emscripten_glGetIntegerv, "emscripten_glGetProgramInfoLog": _emscripten_glGetProgramInfoLog, "emscripten_glGetProgramiv": _emscripten_glGetProgramiv, "emscripten_glGetQueryObjecti64vEXT": _emscripten_glGetQueryObjecti64vEXT, "emscripten_glGetQueryObjectivEXT": _emscripten_glGetQueryObjectivEXT, "emscripten_glGetQueryObjectui64vEXT": _emscripten_glGetQueryObjectui64vEXT, "emscripten_glGetQueryObjectuivEXT": _emscripten_glGetQueryObjectuivEXT, "emscripten_glGetQueryivEXT": _emscripten_glGetQueryivEXT, "emscripten_glGetRenderbufferParameteriv": _emscripten_glGetRenderbufferParameteriv, "emscripten_glGetShaderInfoLog": _emscripten_glGetShaderInfoLog, "emscripten_glGetShaderPrecisionFormat": _emscripten_glGetShaderPrecisionFormat, "emscripten_glGetShaderSource": _emscripten_glGetShaderSource, "emscripten_glGetShaderiv": _emscripten_glGetShaderiv, "emscripten_glGetString": _emscripten_glGetString, "emscripten_glGetTexParameterfv": _emscripten_glGetTexParameterfv, "emscripten_glGetTexParameteriv": _emscripten_glGetTexParameteriv, "emscripten_glGetUniformLocation": _emscripten_glGetUniformLocation, "emscripten_glGetUniformfv": _emscripten_glGetUniformfv, "emscripten_glGetUniformiv": _emscripten_glGetUniformiv, "emscripten_glGetVertexAttribPointerv": _emscripten_glGetVertexAttribPointerv, "emscripten_glGetVertexAttribfv": _emscripten_glGetVertexAttribfv, "emscripten_glGetVertexAttribiv": _emscripten_glGetVertexAttribiv, "emscripten_glHint": _emscripten_glHint, "emscripten_glIsBuffer": _emscripten_glIsBuffer, "emscripten_glIsEnabled": _emscripten_glIsEnabled, "emscripten_glIsFramebuffer": _emscripten_glIsFramebuffer, "emscripten_glIsProgram": _emscripten_glIsProgram, "emscripten_glIsQueryEXT": _emscripten_glIsQueryEXT, "emscripten_glIsRenderbuffer": _emscripten_glIsRenderbuffer, "emscripten_glIsShader": _emscripten_glIsShader, "emscripten_glIsTexture": _emscripten_glIsTexture, "emscripten_glIsVertexArrayOES": _emscripten_glIsVertexArrayOES, "emscripten_glLineWidth": _emscripten_glLineWidth, "emscripten_glLinkProgram": _emscripten_glLinkProgram, "emscripten_glPixelStorei": _emscripten_glPixelStorei, "emscripten_glPolygonOffset": _emscripten_glPolygonOffset, "emscripten_glQueryCounterEXT": _emscripten_glQueryCounterEXT, "emscripten_glReadPixels": _emscripten_glReadPixels, "emscripten_glReleaseShaderCompiler": _emscripten_glReleaseShaderCompiler, "emscripten_glRenderbufferStorage": _emscripten_glRenderbufferStorage, "emscripten_glSampleCoverage": _emscripten_glSampleCoverage, "emscripten_glScissor": _emscripten_glScissor, "emscripten_glShaderBinary": _emscripten_glShaderBinary, "emscripten_glShaderSource": _emscripten_glShaderSource, "emscripten_glStencilFunc": _emscripten_glStencilFunc, "emscripten_glStencilFuncSeparate": _emscripten_glStencilFuncSeparate, "emscripten_glStencilMask": _emscripten_glStencilMask, "emscripten_glStencilMaskSeparate": _emscripten_glStencilMaskSeparate, "emscripten_glStencilOp": _emscripten_glStencilOp, "emscripten_glStencilOpSeparate": _emscripten_glStencilOpSeparate, "emscripten_glTexImage2D": _emscripten_glTexImage2D, "emscripten_glTexParameterf": _emscripten_glTexParameterf, "emscripten_glTexParameterfv": _emscripten_glTexParameterfv, "emscripten_glTexParameteri": _emscripten_glTexParameteri, "emscripten_glTexParameteriv": _emscripten_glTexParameteriv, "emscripten_glTexSubImage2D": _emscripten_glTexSubImage2D, "emscripten_glUniform1f": _emscripten_glUniform1f, "emscripten_glUniform1fv": _emscripten_glUniform1fv, "emscripten_glUniform1i": _emscripten_glUniform1i, "emscripten_glUniform1iv": _emscripten_glUniform1iv, "emscripten_glUniform2f": _emscripten_glUniform2f, "emscripten_glUniform2fv": _emscripten_glUniform2fv, "emscripten_glUniform2i": _emscripten_glUniform2i, "emscripten_glUniform2iv": _emscripten_glUniform2iv, "emscripten_glUniform3f": _emscripten_glUniform3f, "emscripten_glUniform3fv": _emscripten_glUniform3fv, "emscripten_glUniform3i": _emscripten_glUniform3i, "emscripten_glUniform3iv": _emscripten_glUniform3iv, "emscripten_glUniform4f": _emscripten_glUniform4f, "emscripten_glUniform4fv": _emscripten_glUniform4fv, "emscripten_glUniform4i": _emscripten_glUniform4i, "emscripten_glUniform4iv": _emscripten_glUniform4iv, "emscripten_glUniformMatrix2fv": _emscripten_glUniformMatrix2fv, "emscripten_glUniformMatrix3fv": _emscripten_glUniformMatrix3fv, "emscripten_glUniformMatrix4fv": _emscripten_glUniformMatrix4fv, "emscripten_glUseProgram": _emscripten_glUseProgram, "emscripten_glValidateProgram": _emscripten_glValidateProgram, "emscripten_glVertexAttrib1f": _emscripten_glVertexAttrib1f, "emscripten_glVertexAttrib1fv": _emscripten_glVertexAttrib1fv, "emscripten_glVertexAttrib2f": _emscripten_glVertexAttrib2f, "emscripten_glVertexAttrib2fv": _emscripten_glVertexAttrib2fv, "emscripten_glVertexAttrib3f": _emscripten_glVertexAttrib3f, "emscripten_glVertexAttrib3fv": _emscripten_glVertexAttrib3fv, "emscripten_glVertexAttrib4f": _emscripten_glVertexAttrib4f, "emscripten_glVertexAttrib4fv": _emscripten_glVertexAttrib4fv, "emscripten_glVertexAttribDivisorANGLE": _emscripten_glVertexAttribDivisorANGLE, "emscripten_glVertexAttribPointer": _emscripten_glVertexAttribPointer, "emscripten_glViewport": _emscripten_glViewport, "emscripten_has_asyncify": _emscripten_has_asyncify, "emscripten_memcpy_big": _emscripten_memcpy_big, "emscripten_request_fullscreen_strategy": _emscripten_request_fullscreen_strategy, "emscripten_request_pointerlock": _emscripten_request_pointerlock, "emscripten_resize_heap": _emscripten_resize_heap, "emscripten_sample_gamepad_data": _emscripten_sample_gamepad_data, "emscripten_set_beforeunload_callback_on_thread": _emscripten_set_beforeunload_callback_on_thread, "emscripten_set_blur_callback_on_thread": _emscripten_set_blur_callback_on_thread, "emscripten_set_canvas_element_size": _emscripten_set_canvas_element_size, "emscripten_set_element_css_size": _emscripten_set_element_css_size, "emscripten_set_focus_callback_on_thread": _emscripten_set_focus_callback_on_thread, "emscripten_set_fullscreenchange_callback_on_thread": _emscripten_set_fullscreenchange_callback_on_thread, "emscripten_set_gamepadconnected_callback_on_thread": _emscripten_set_gamepadconnected_callback_on_thread, "emscripten_set_gamepaddisconnected_callback_on_thread": _emscripten_set_gamepaddisconnected_callback_on_thread, "emscripten_set_keydown_callback_on_thread": _emscripten_set_keydown_callback_on_thread, "emscripten_set_keypress_callback_on_thread": _emscripten_set_keypress_callback_on_thread, "emscripten_set_keyup_callback_on_thread": _emscripten_set_keyup_callback_on_thread, "emscripten_set_main_loop_arg": _emscripten_set_main_loop_arg, "emscripten_set_mousedown_callback_on_thread": _emscripten_set_mousedown_callback_on_thread, "emscripten_set_mouseenter_callback_on_thread": _emscripten_set_mouseenter_callback_on_thread, "emscripten_set_mouseleave_callback_on_thread": _emscripten_set_mouseleave_callback_on_thread, "emscripten_set_mousemove_callback_on_thread": _emscripten_set_mousemove_callback_on_thread, "emscripten_set_mouseup_callback_on_thread": _emscripten_set_mouseup_callback_on_thread, "emscripten_set_pointerlockchange_callback_on_thread": _emscripten_set_pointerlockchange_callback_on_thread, "emscripten_set_resize_callback_on_thread": _emscripten_set_resize_callback_on_thread, "emscripten_set_touchcancel_callback_on_thread": _emscripten_set_touchcancel_callback_on_thread, "emscripten_set_touchend_callback_on_thread": _emscripten_set_touchend_callback_on_thread, "emscripten_set_touchmove_callback_on_thread": _emscripten_set_touchmove_callback_on_thread, "emscripten_set_touchstart_callback_on_thread": _emscripten_set_touchstart_callback_on_thread, "emscripten_set_visibilitychange_callback_on_thread": _emscripten_set_visibilitychange_callback_on_thread, "emscripten_set_wheel_callback_on_thread": _emscripten_set_wheel_callback_on_thread, "emscripten_sleep": _emscripten_sleep, "emscripten_thread_sleep": _emscripten_thread_sleep, "environ_get": _environ_get, "environ_sizes_get": _environ_sizes_get, "exit": _exit, "fd_close": _fd_close, "fd_read": _fd_read, "fd_seek": _fd_seek, "fd_write": _fd_write, "gettimeofday": _gettimeofday, "setTempRet0": _setTempRet0, "sigaction": _sigaction, "signal": _signal }; var asm = createWasm(); /** @type {function(...*):?} */ var ___wasm_call_ctors = Module["___wasm_call_ctors"] = createExportWrapper("__wasm_call_ctors"); /** @type {function(...*):?} */ var _free = Module["_free"] = createExportWrapper("free"); /** @type {function(...*):?} */ var _memset = Module["_memset"] = createExportWrapper("memset"); /** @type {function(...*):?} */ var _malloc = Module["_malloc"] = createExportWrapper("malloc"); /** @type {function(...*):?} */ var _memcpy = Module["_memcpy"] = createExportWrapper("memcpy"); /** @type {function(...*):?} */ var _main = Module["_main"] = createExportWrapper("main"); /** @type {function(...*):?} */ var ___errno_location = Module["___errno_location"] = createExportWrapper("__errno_location"); /** @type {function(...*):?} */ var _fflush = Module["_fflush"] = createExportWrapper("fflush"); /** @type {function(...*):?} */ var stackSave = Module["stackSave"] = createExportWrapper("stackSave"); /** @type {function(...*):?} */ var stackRestore = Module["stackRestore"] = createExportWrapper("stackRestore"); /** @type {function(...*):?} */ var stackAlloc = Module["stackAlloc"] = createExportWrapper("stackAlloc"); /** @type {function(...*):?} */ var _emscripten_stack_init = Module["_emscripten_stack_init"] = function() { return (_emscripten_stack_init = Module["_emscripten_stack_init"] = Module["asm"]["emscripten_stack_init"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _emscripten_stack_get_free = Module["_emscripten_stack_get_free"] = function() { return (_emscripten_stack_get_free = Module["_emscripten_stack_get_free"] = Module["asm"]["emscripten_stack_get_free"]).apply(null, arguments); }; /** @type {function(...*):?} */ var _emscripten_stack_get_end = Module["_emscripten_stack_get_end"] = function() { return (_emscripten_stack_get_end = Module["_emscripten_stack_get_end"] = Module["asm"]["emscripten_stack_get_end"]).apply(null, arguments); }; /** @type {function(...*):?} */ var dynCall_jiji = Module["dynCall_jiji"] = createExportWrapper("dynCall_jiji"); /** @type {function(...*):?} */ var dynCall_ji = Module["dynCall_ji"] = createExportWrapper("dynCall_ji"); // === Auto-generated postamble setup entry stuff === if (!Object.getOwnPropertyDescriptor(Module, "intArrayFromString")) Module["intArrayFromString"] = function() { abort("'intArrayFromString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "intArrayToString")) Module["intArrayToString"] = function() { abort("'intArrayToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "ccall")) Module["ccall"] = function() { abort("'ccall' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "cwrap")) Module["cwrap"] = function() { abort("'cwrap' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "setValue")) Module["setValue"] = function() { abort("'setValue' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "getValue")) Module["getValue"] = function() { abort("'getValue' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "allocate")) Module["allocate"] = function() { abort("'allocate' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "UTF8ArrayToString")) Module["UTF8ArrayToString"] = function() { abort("'UTF8ArrayToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "UTF8ToString")) Module["UTF8ToString"] = function() { abort("'UTF8ToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "stringToUTF8Array")) Module["stringToUTF8Array"] = function() { abort("'stringToUTF8Array' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "stringToUTF8")) Module["stringToUTF8"] = function() { abort("'stringToUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "lengthBytesUTF8")) Module["lengthBytesUTF8"] = function() { abort("'lengthBytesUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "stackTrace")) Module["stackTrace"] = function() { abort("'stackTrace' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "addOnPreRun")) Module["addOnPreRun"] = function() { abort("'addOnPreRun' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "addOnInit")) Module["addOnInit"] = function() { abort("'addOnInit' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "addOnPreMain")) Module["addOnPreMain"] = function() { abort("'addOnPreMain' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "addOnExit")) Module["addOnExit"] = function() { abort("'addOnExit' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "addOnPostRun")) Module["addOnPostRun"] = function() { abort("'addOnPostRun' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "writeStringToMemory")) Module["writeStringToMemory"] = function() { abort("'writeStringToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "writeArrayToMemory")) Module["writeArrayToMemory"] = function() { abort("'writeArrayToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "writeAsciiToMemory")) Module["writeAsciiToMemory"] = function() { abort("'writeAsciiToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "addRunDependency")) Module["addRunDependency"] = function() { abort("'addRunDependency' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") }; if (!Object.getOwnPropertyDescriptor(Module, "removeRunDependency")) Module["removeRunDependency"] = function() { abort("'removeRunDependency' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") }; if (!Object.getOwnPropertyDescriptor(Module, "FS_createFolder")) Module["FS_createFolder"] = function() { abort("'FS_createFolder' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "FS_createPath")) Module["FS_createPath"] = function() { abort("'FS_createPath' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") }; if (!Object.getOwnPropertyDescriptor(Module, "FS_createDataFile")) Module["FS_createDataFile"] = function() { abort("'FS_createDataFile' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") }; if (!Object.getOwnPropertyDescriptor(Module, "FS_createPreloadedFile")) Module["FS_createPreloadedFile"] = function() { abort("'FS_createPreloadedFile' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") }; if (!Object.getOwnPropertyDescriptor(Module, "FS_createLazyFile")) Module["FS_createLazyFile"] = function() { abort("'FS_createLazyFile' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") }; if (!Object.getOwnPropertyDescriptor(Module, "FS_createLink")) Module["FS_createLink"] = function() { abort("'FS_createLink' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "FS_createDevice")) Module["FS_createDevice"] = function() { abort("'FS_createDevice' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") }; if (!Object.getOwnPropertyDescriptor(Module, "FS_unlink")) Module["FS_unlink"] = function() { abort("'FS_unlink' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you") }; if (!Object.getOwnPropertyDescriptor(Module, "getLEB")) Module["getLEB"] = function() { abort("'getLEB' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "getFunctionTables")) Module["getFunctionTables"] = function() { abort("'getFunctionTables' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "alignFunctionTables")) Module["alignFunctionTables"] = function() { abort("'alignFunctionTables' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerFunctions")) Module["registerFunctions"] = function() { abort("'registerFunctions' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "addFunction")) Module["addFunction"] = function() { abort("'addFunction' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "removeFunction")) Module["removeFunction"] = function() { abort("'removeFunction' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "getFuncWrapper")) Module["getFuncWrapper"] = function() { abort("'getFuncWrapper' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "prettyPrint")) Module["prettyPrint"] = function() { abort("'prettyPrint' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "makeBigInt")) Module["makeBigInt"] = function() { abort("'makeBigInt' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "dynCall")) Module["dynCall"] = function() { abort("'dynCall' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "getCompilerSetting")) Module["getCompilerSetting"] = function() { abort("'getCompilerSetting' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "print")) Module["print"] = function() { abort("'print' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "printErr")) Module["printErr"] = function() { abort("'printErr' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "getTempRet0")) Module["getTempRet0"] = function() { abort("'getTempRet0' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "setTempRet0")) Module["setTempRet0"] = function() { abort("'setTempRet0' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "callMain")) Module["callMain"] = function() { abort("'callMain' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "abort")) Module["abort"] = function() { abort("'abort' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "stringToNewUTF8")) Module["stringToNewUTF8"] = function() { abort("'stringToNewUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "setFileTime")) Module["setFileTime"] = function() { abort("'setFileTime' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "abortOnCannotGrowMemory")) Module["abortOnCannotGrowMemory"] = function() { abort("'abortOnCannotGrowMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "emscripten_realloc_buffer")) Module["emscripten_realloc_buffer"] = function() { abort("'emscripten_realloc_buffer' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "ENV")) Module["ENV"] = function() { abort("'ENV' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "ERRNO_CODES")) Module["ERRNO_CODES"] = function() { abort("'ERRNO_CODES' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "ERRNO_MESSAGES")) Module["ERRNO_MESSAGES"] = function() { abort("'ERRNO_MESSAGES' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "setErrNo")) Module["setErrNo"] = function() { abort("'setErrNo' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "inetPton4")) Module["inetPton4"] = function() { abort("'inetPton4' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "inetNtop4")) Module["inetNtop4"] = function() { abort("'inetNtop4' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "inetPton6")) Module["inetPton6"] = function() { abort("'inetPton6' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "inetNtop6")) Module["inetNtop6"] = function() { abort("'inetNtop6' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "readSockaddr")) Module["readSockaddr"] = function() { abort("'readSockaddr' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "writeSockaddr")) Module["writeSockaddr"] = function() { abort("'writeSockaddr' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "DNS")) Module["DNS"] = function() { abort("'DNS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "getHostByName")) Module["getHostByName"] = function() { abort("'getHostByName' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "GAI_ERRNO_MESSAGES")) Module["GAI_ERRNO_MESSAGES"] = function() { abort("'GAI_ERRNO_MESSAGES' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "Protocols")) Module["Protocols"] = function() { abort("'Protocols' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "Sockets")) Module["Sockets"] = function() { abort("'Sockets' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "getRandomDevice")) Module["getRandomDevice"] = function() { abort("'getRandomDevice' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "traverseStack")) Module["traverseStack"] = function() { abort("'traverseStack' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "UNWIND_CACHE")) Module["UNWIND_CACHE"] = function() { abort("'UNWIND_CACHE' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "withBuiltinMalloc")) Module["withBuiltinMalloc"] = function() { abort("'withBuiltinMalloc' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "readAsmConstArgsArray")) Module["readAsmConstArgsArray"] = function() { abort("'readAsmConstArgsArray' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "readAsmConstArgs")) Module["readAsmConstArgs"] = function() { abort("'readAsmConstArgs' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "mainThreadEM_ASM")) Module["mainThreadEM_ASM"] = function() { abort("'mainThreadEM_ASM' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "jstoi_q")) Module["jstoi_q"] = function() { abort("'jstoi_q' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "jstoi_s")) Module["jstoi_s"] = function() { abort("'jstoi_s' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "getExecutableName")) Module["getExecutableName"] = function() { abort("'getExecutableName' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "listenOnce")) Module["listenOnce"] = function() { abort("'listenOnce' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "autoResumeAudioContext")) Module["autoResumeAudioContext"] = function() { abort("'autoResumeAudioContext' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "dynCallLegacy")) Module["dynCallLegacy"] = function() { abort("'dynCallLegacy' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "getDynCaller")) Module["getDynCaller"] = function() { abort("'getDynCaller' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "dynCall")) Module["dynCall"] = function() { abort("'dynCall' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "callRuntimeCallbacks")) Module["callRuntimeCallbacks"] = function() { abort("'callRuntimeCallbacks' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "reallyNegative")) Module["reallyNegative"] = function() { abort("'reallyNegative' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "unSign")) Module["unSign"] = function() { abort("'unSign' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "reSign")) Module["reSign"] = function() { abort("'reSign' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "formatString")) Module["formatString"] = function() { abort("'formatString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "PATH")) Module["PATH"] = function() { abort("'PATH' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "PATH_FS")) Module["PATH_FS"] = function() { abort("'PATH_FS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "SYSCALLS")) Module["SYSCALLS"] = function() { abort("'SYSCALLS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "syscallMmap2")) Module["syscallMmap2"] = function() { abort("'syscallMmap2' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "syscallMunmap")) Module["syscallMunmap"] = function() { abort("'syscallMunmap' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "getSocketFromFD")) Module["getSocketFromFD"] = function() { abort("'getSocketFromFD' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "getSocketAddress")) Module["getSocketAddress"] = function() { abort("'getSocketAddress' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "JSEvents")) Module["JSEvents"] = function() { abort("'JSEvents' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerKeyEventCallback")) Module["registerKeyEventCallback"] = function() { abort("'registerKeyEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "specialHTMLTargets")) Module["specialHTMLTargets"] = function() { abort("'specialHTMLTargets' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "maybeCStringToJsString")) Module["maybeCStringToJsString"] = function() { abort("'maybeCStringToJsString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "findEventTarget")) Module["findEventTarget"] = function() { abort("'findEventTarget' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "findCanvasEventTarget")) Module["findCanvasEventTarget"] = function() { abort("'findCanvasEventTarget' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "getBoundingClientRect")) Module["getBoundingClientRect"] = function() { abort("'getBoundingClientRect' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "fillMouseEventData")) Module["fillMouseEventData"] = function() { abort("'fillMouseEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerMouseEventCallback")) Module["registerMouseEventCallback"] = function() { abort("'registerMouseEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerWheelEventCallback")) Module["registerWheelEventCallback"] = function() { abort("'registerWheelEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerUiEventCallback")) Module["registerUiEventCallback"] = function() { abort("'registerUiEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerFocusEventCallback")) Module["registerFocusEventCallback"] = function() { abort("'registerFocusEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "fillDeviceOrientationEventData")) Module["fillDeviceOrientationEventData"] = function() { abort("'fillDeviceOrientationEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerDeviceOrientationEventCallback")) Module["registerDeviceOrientationEventCallback"] = function() { abort("'registerDeviceOrientationEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "fillDeviceMotionEventData")) Module["fillDeviceMotionEventData"] = function() { abort("'fillDeviceMotionEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerDeviceMotionEventCallback")) Module["registerDeviceMotionEventCallback"] = function() { abort("'registerDeviceMotionEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "screenOrientation")) Module["screenOrientation"] = function() { abort("'screenOrientation' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "fillOrientationChangeEventData")) Module["fillOrientationChangeEventData"] = function() { abort("'fillOrientationChangeEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerOrientationChangeEventCallback")) Module["registerOrientationChangeEventCallback"] = function() { abort("'registerOrientationChangeEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "fillFullscreenChangeEventData")) Module["fillFullscreenChangeEventData"] = function() { abort("'fillFullscreenChangeEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerFullscreenChangeEventCallback")) Module["registerFullscreenChangeEventCallback"] = function() { abort("'registerFullscreenChangeEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerRestoreOldStyle")) Module["registerRestoreOldStyle"] = function() { abort("'registerRestoreOldStyle' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "hideEverythingExceptGivenElement")) Module["hideEverythingExceptGivenElement"] = function() { abort("'hideEverythingExceptGivenElement' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "restoreHiddenElements")) Module["restoreHiddenElements"] = function() { abort("'restoreHiddenElements' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "setLetterbox")) Module["setLetterbox"] = function() { abort("'setLetterbox' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "currentFullscreenStrategy")) Module["currentFullscreenStrategy"] = function() { abort("'currentFullscreenStrategy' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "restoreOldWindowedStyle")) Module["restoreOldWindowedStyle"] = function() { abort("'restoreOldWindowedStyle' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "softFullscreenResizeWebGLRenderTarget")) Module["softFullscreenResizeWebGLRenderTarget"] = function() { abort("'softFullscreenResizeWebGLRenderTarget' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "doRequestFullscreen")) Module["doRequestFullscreen"] = function() { abort("'doRequestFullscreen' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "fillPointerlockChangeEventData")) Module["fillPointerlockChangeEventData"] = function() { abort("'fillPointerlockChangeEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerPointerlockChangeEventCallback")) Module["registerPointerlockChangeEventCallback"] = function() { abort("'registerPointerlockChangeEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerPointerlockErrorEventCallback")) Module["registerPointerlockErrorEventCallback"] = function() { abort("'registerPointerlockErrorEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "requestPointerLock")) Module["requestPointerLock"] = function() { abort("'requestPointerLock' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "fillVisibilityChangeEventData")) Module["fillVisibilityChangeEventData"] = function() { abort("'fillVisibilityChangeEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerVisibilityChangeEventCallback")) Module["registerVisibilityChangeEventCallback"] = function() { abort("'registerVisibilityChangeEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerTouchEventCallback")) Module["registerTouchEventCallback"] = function() { abort("'registerTouchEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "fillGamepadEventData")) Module["fillGamepadEventData"] = function() { abort("'fillGamepadEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerGamepadEventCallback")) Module["registerGamepadEventCallback"] = function() { abort("'registerGamepadEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerBeforeUnloadEventCallback")) Module["registerBeforeUnloadEventCallback"] = function() { abort("'registerBeforeUnloadEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "fillBatteryEventData")) Module["fillBatteryEventData"] = function() { abort("'fillBatteryEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "battery")) Module["battery"] = function() { abort("'battery' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "registerBatteryEventCallback")) Module["registerBatteryEventCallback"] = function() { abort("'registerBatteryEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "setCanvasElementSize")) Module["setCanvasElementSize"] = function() { abort("'setCanvasElementSize' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "getCanvasElementSize")) Module["getCanvasElementSize"] = function() { abort("'getCanvasElementSize' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "polyfillSetImmediate")) Module["polyfillSetImmediate"] = function() { abort("'polyfillSetImmediate' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "demangle")) Module["demangle"] = function() { abort("'demangle' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "demangleAll")) Module["demangleAll"] = function() { abort("'demangleAll' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "jsStackTrace")) Module["jsStackTrace"] = function() { abort("'jsStackTrace' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "stackTrace")) Module["stackTrace"] = function() { abort("'stackTrace' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "getEnvStrings")) Module["getEnvStrings"] = function() { abort("'getEnvStrings' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "checkWasiClock")) Module["checkWasiClock"] = function() { abort("'checkWasiClock' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "writeI53ToI64")) Module["writeI53ToI64"] = function() { abort("'writeI53ToI64' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "writeI53ToI64Clamped")) Module["writeI53ToI64Clamped"] = function() { abort("'writeI53ToI64Clamped' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "writeI53ToI64Signaling")) Module["writeI53ToI64Signaling"] = function() { abort("'writeI53ToI64Signaling' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "writeI53ToU64Clamped")) Module["writeI53ToU64Clamped"] = function() { abort("'writeI53ToU64Clamped' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "writeI53ToU64Signaling")) Module["writeI53ToU64Signaling"] = function() { abort("'writeI53ToU64Signaling' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "readI53FromI64")) Module["readI53FromI64"] = function() { abort("'readI53FromI64' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "readI53FromU64")) Module["readI53FromU64"] = function() { abort("'readI53FromU64' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "convertI32PairToI53")) Module["convertI32PairToI53"] = function() { abort("'convertI32PairToI53' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "convertU32PairToI53")) Module["convertU32PairToI53"] = function() { abort("'convertU32PairToI53' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "uncaughtExceptionCount")) Module["uncaughtExceptionCount"] = function() { abort("'uncaughtExceptionCount' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "exceptionLast")) Module["exceptionLast"] = function() { abort("'exceptionLast' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "exceptionCaught")) Module["exceptionCaught"] = function() { abort("'exceptionCaught' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "ExceptionInfoAttrs")) Module["ExceptionInfoAttrs"] = function() { abort("'ExceptionInfoAttrs' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "ExceptionInfo")) Module["ExceptionInfo"] = function() { abort("'ExceptionInfo' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "CatchInfo")) Module["CatchInfo"] = function() { abort("'CatchInfo' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "exception_addRef")) Module["exception_addRef"] = function() { abort("'exception_addRef' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "exception_decRef")) Module["exception_decRef"] = function() { abort("'exception_decRef' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "Browser")) Module["Browser"] = function() { abort("'Browser' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "funcWrappers")) Module["funcWrappers"] = function() { abort("'funcWrappers' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "getFuncWrapper")) Module["getFuncWrapper"] = function() { abort("'getFuncWrapper' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "setMainLoop")) Module["setMainLoop"] = function() { abort("'setMainLoop' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "FS")) Module["FS"] = function() { abort("'FS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "mmapAlloc")) Module["mmapAlloc"] = function() { abort("'mmapAlloc' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "MEMFS")) Module["MEMFS"] = function() { abort("'MEMFS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "TTY")) Module["TTY"] = function() { abort("'TTY' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "PIPEFS")) Module["PIPEFS"] = function() { abort("'PIPEFS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "SOCKFS")) Module["SOCKFS"] = function() { abort("'SOCKFS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "_setNetworkCallback")) Module["_setNetworkCallback"] = function() { abort("'_setNetworkCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "tempFixedLengthArray")) Module["tempFixedLengthArray"] = function() { abort("'tempFixedLengthArray' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "miniTempWebGLFloatBuffers")) Module["miniTempWebGLFloatBuffers"] = function() { abort("'miniTempWebGLFloatBuffers' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "heapObjectForWebGLType")) Module["heapObjectForWebGLType"] = function() { abort("'heapObjectForWebGLType' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "heapAccessShiftForWebGLHeap")) Module["heapAccessShiftForWebGLHeap"] = function() { abort("'heapAccessShiftForWebGLHeap' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "GL")) Module["GL"] = function() { abort("'GL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLGet")) Module["emscriptenWebGLGet"] = function() { abort("'emscriptenWebGLGet' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "computeUnpackAlignedImageSize")) Module["computeUnpackAlignedImageSize"] = function() { abort("'computeUnpackAlignedImageSize' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLGetTexPixelData")) Module["emscriptenWebGLGetTexPixelData"] = function() { abort("'emscriptenWebGLGetTexPixelData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLGetUniform")) Module["emscriptenWebGLGetUniform"] = function() { abort("'emscriptenWebGLGetUniform' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "emscriptenWebGLGetVertexAttrib")) Module["emscriptenWebGLGetVertexAttrib"] = function() { abort("'emscriptenWebGLGetVertexAttrib' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "writeGLArray")) Module["writeGLArray"] = function() { abort("'writeGLArray' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "AL")) Module["AL"] = function() { abort("'AL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "SDL_unicode")) Module["SDL_unicode"] = function() { abort("'SDL_unicode' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "SDL_ttfContext")) Module["SDL_ttfContext"] = function() { abort("'SDL_ttfContext' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "SDL_audio")) Module["SDL_audio"] = function() { abort("'SDL_audio' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "SDL")) Module["SDL"] = function() { abort("'SDL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "SDL_gfx")) Module["SDL_gfx"] = function() { abort("'SDL_gfx' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "GLUT")) Module["GLUT"] = function() { abort("'GLUT' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "EGL")) Module["EGL"] = function() { abort("'EGL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "GLFW_Window")) Module["GLFW_Window"] = function() { abort("'GLFW_Window' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "GLFW")) Module["GLFW"] = function() { abort("'GLFW' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "GLEW")) Module["GLEW"] = function() { abort("'GLEW' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "IDBStore")) Module["IDBStore"] = function() { abort("'IDBStore' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "runAndAbortIfError")) Module["runAndAbortIfError"] = function() { abort("'runAndAbortIfError' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "warnOnce")) Module["warnOnce"] = function() { abort("'warnOnce' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "stackSave")) Module["stackSave"] = function() { abort("'stackSave' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "stackRestore")) Module["stackRestore"] = function() { abort("'stackRestore' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "stackAlloc")) Module["stackAlloc"] = function() { abort("'stackAlloc' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "AsciiToString")) Module["AsciiToString"] = function() { abort("'AsciiToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "stringToAscii")) Module["stringToAscii"] = function() { abort("'stringToAscii' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "UTF16ToString")) Module["UTF16ToString"] = function() { abort("'UTF16ToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "stringToUTF16")) Module["stringToUTF16"] = function() { abort("'stringToUTF16' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "lengthBytesUTF16")) Module["lengthBytesUTF16"] = function() { abort("'lengthBytesUTF16' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "UTF32ToString")) Module["UTF32ToString"] = function() { abort("'UTF32ToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "stringToUTF32")) Module["stringToUTF32"] = function() { abort("'stringToUTF32' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "lengthBytesUTF32")) Module["lengthBytesUTF32"] = function() { abort("'lengthBytesUTF32' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "allocateUTF8")) Module["allocateUTF8"] = function() { abort("'allocateUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; if (!Object.getOwnPropertyDescriptor(Module, "allocateUTF8OnStack")) Module["allocateUTF8OnStack"] = function() { abort("'allocateUTF8OnStack' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") }; Module["writeStackCookie"] = writeStackCookie; Module["checkStackCookie"] = checkStackCookie; if (!Object.getOwnPropertyDescriptor(Module, "ALLOC_NORMAL")) Object.defineProperty(Module, "ALLOC_NORMAL", { configurable: true, get: function() { abort("'ALLOC_NORMAL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") } }); if (!Object.getOwnPropertyDescriptor(Module, "ALLOC_STACK")) Object.defineProperty(Module, "ALLOC_STACK", { configurable: true, get: function() { abort("'ALLOC_STACK' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") } }); var calledRun; /** * @constructor * @this {ExitStatus} */ function ExitStatus(status) { this.name = "ExitStatus"; this.message = "Program terminated with exit(" + status + ")"; this.status = status; } var calledMain = false; dependenciesFulfilled = function runCaller() { // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false) if (!calledRun) run(); if (!calledRun) dependenciesFulfilled = runCaller; // try this again later, after new deps are fulfilled }; function callMain(args) { assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on Module["onRuntimeInitialized"])'); assert(__ATPRERUN__.length == 0, 'cannot call main when preRun functions remain to be called'); var entryFunction = Module['_main']; args = args || []; var argc = args.length+1; var argv = stackAlloc((argc + 1) * 4); HEAP32[argv >> 2] = allocateUTF8OnStack(thisProgram); for (var i = 1; i < argc; i++) { HEAP32[(argv >> 2) + i] = allocateUTF8OnStack(args[i - 1]); } HEAP32[(argv >> 2) + argc] = 0; try { var ret = entryFunction(argc, argv); // In PROXY_TO_PTHREAD builds, we should never exit the runtime below, as // execution is asynchronously handed off to a pthread. // if we're not running an evented main loop, it's time to exit exit(ret, /* implicit = */ true); } catch(e) { if (e instanceof ExitStatus) { // exit() throws this once it's done to make sure execution // has been stopped completely return; } else if (e == 'unwind') { // running an evented main loop, don't immediately exit noExitRuntime = true; return; } else { var toLog = e; if (e && typeof e === 'object' && e.stack) { toLog = [e, e.stack]; } err('exception thrown: ' + toLog); quit_(1, e); } } finally { calledMain = true; } } function stackCheckInit() { // This is normally called automatically during __wasm_call_ctors but need to // get these values before even running any of the ctors so we call it redundantly // here. // TODO(sbc): Move writeStackCookie to native to to avoid this. _emscripten_stack_init(); writeStackCookie(); } /** @type {function(Array=)} */ function run(args) { args = args || arguments_; if (runDependencies > 0) { return; } stackCheckInit(); preRun(); // a preRun added a dependency, run will be called later if (runDependencies > 0) { return; } function doRun() { // run may have just been called through dependencies being fulfilled just in this very frame, // or while the async setStatus time below was happening if (calledRun) return; calledRun = true; Module['calledRun'] = true; if (ABORT) return; initRuntime(); preMain(); if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized'](); if (shouldRunNow) callMain(args); postRun(); } if (Module['setStatus']) { Module['setStatus']('Running...'); setTimeout(function() { setTimeout(function() { Module['setStatus'](''); }, 1); doRun(); }, 1); } else { doRun(); } checkStackCookie(); } Module['run'] = run; function checkUnflushedContent() { // Compiler settings do not allow exiting the runtime, so flushing // the streams is not possible. but in ASSERTIONS mode we check // if there was something to flush, and if so tell the user they // should request that the runtime be exitable. // Normally we would not even include flush() at all, but in ASSERTIONS // builds we do so just for this check, and here we see if there is any // content to flush, that is, we check if there would have been // something a non-ASSERTIONS build would have not seen. // How we flush the streams depends on whether we are in SYSCALLS_REQUIRE_FILESYSTEM=0 // mode (which has its own special function for this; otherwise, all // the code is inside libc) var oldOut = out; var oldErr = err; var has = false; out = err = function(x) { has = true; } try { // it doesn't matter if it fails var flush = Module['_fflush']; if (flush) flush(0); // also flush in the JS FS layer ['stdout', 'stderr'].forEach(function(name) { var info = FS.analyzePath('/dev/' + name); if (!info) return; var stream = info.object; var rdev = stream.rdev; var tty = TTY.ttys[rdev]; if (tty && tty.output && tty.output.length) { has = true; } }); } catch(e) {} out = oldOut; err = oldErr; if (has) { warnOnce('stdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1 (see the FAQ), or make sure to emit a newline when you printf etc.'); } } /** @param {boolean|number=} implicit */ function exit(status, implicit) { checkUnflushedContent(); // if this is just main exit-ing implicitly, and the status is 0, then we // don't need to do anything here and can just leave. if the status is // non-zero, though, then we need to report it. // (we may have warned about this earlier, if a situation justifies doing so) if (implicit && noExitRuntime && status === 0) { return; } if (noExitRuntime) { // if exit() was called, we may warn the user if the runtime isn't actually being shut down if (!implicit) { var msg = 'program exited (with status: ' + status + '), but EXIT_RUNTIME is not set, so halting execution but not exiting the runtime or preventing further async execution (build with EXIT_RUNTIME=1, if you want a true shutdown)'; err(msg); } } else { EXITSTATUS = status; exitRuntime(); if (Module['onExit']) Module['onExit'](status); ABORT = true; } quit_(status, new ExitStatus(status)); } if (Module['preInit']) { if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']]; while (Module['preInit'].length > 0) { Module['preInit'].pop()(); } } // shouldRunNow refers to calling main(), not run(). var shouldRunNow = true; if (Module['noInitialRun']) shouldRunNow = false; run(); ================================================ FILE: platform/web/embed/package/index.html ================================================ LowRes NX
================================================ FILE: platform/web/embed/package/program.nx ================================================ PRINT "REPLACE THIS WITH" PRINT "YOUR PROGRAM" ================================================ FILE: platform/web/embed/readme.txt ================================================ Embeddable Web Player for LowRes NX Replace the file "program.nx" in the "package" folder with your own program. It must be called "program.nx". You should rename the folder to your program's name. How to publish it on itch.io Make a zip archive of your folder and upload it to itch.io. Select "This file will be played in the browser" and set the viewport dimensions to 640x512. Please add the tag "lowres-nx". You may choose "Automatically start on page load", or if not, upload a screenshot for "Embed BG" in the theme editor. ================================================ FILE: platform/web/makefile ================================================ # Declaration of variables CC = emcc CC_FLAGS = -w -s USE_SDL=2 -I ../../core -I ../../core/machine -I ../../core/accessories -I ../../core/datamanager -I ../../core/interpreter -I ../../core/libraries -I ../../core/overlay CC_FLAGS_MAIN = -s USE_SDL=2 # File names EXEC = output/LowResNX120.js SOURCES = $(wildcard ../../core/*.c) $(wildcard ../../core/*/*.c) $(wildcard ../../sdl/*.c) OBJECTS = $(SOURCES:.c=.bc) # Main target $(EXEC): $(OBJECTS) @mkdir -p $(@D) $(CC) $(CC_FLAGS_MAIN) $(OBJECTS) -o $(EXEC) # To obtain object files %.bc: %.c $(CC) -c $(CC_FLAGS) $< -o $@ # To remove generated files clean: rm -f $(OBJECTS) ================================================ FILE: programs/Gfx Designer 2.0.nx ================================================ 'TITLE: GFX DESIGNER 'AUTHOR: TIMO KLOSS, MRLEGOBOY '====================== '** RAM MAP ** GLOBAL USERCHARS,USERBG,USERPALS,TEMPMEM USERCHARS = $A000 USERBG = $B000 USERPALS = $D004 TEMPMEM = $D024 '====================== 'ENERGY SAVING MODE SYSTEM 0,1 KEYBOARD OPTIONAL DIM GLOBAL MENU$(6) DIM GLOBAL PXL(63,63) GLOBAL SELCHAR,SELSIZE,SELPAGE,SELCOLOR GLOBAL SELPAL,SELPALPAGE GLOBAL HIDECOUNT,CHAROFFSET SELCHAR=1 SELSIZE=0 SELPAGE=0 SELCOLOR=1 SELPAL=0 SELPALPAGE=0 GLOBAL MAPW,MAPH,CELLSIZE MAPW=32 MAPH=32 CELLSIZE=0 GLOBAL EDMODE,TOOLMODE,FLIPMODE,PRI,MAPX,MAPY,DRAGX,DRAGY GLOBAL STAMPW,STAMPH GLOBAL MAPBARST GLOBAL WINW,WINH EDMODE=1 GLOBAL FTYPE$,FILESOFFS,SELFILE GLOBAL PALFILE,CHARFILE,BGFILE SELFILE=-1 PALFILE=1 CHARFILE=2 BGFILE=3 DIM GLOBAL SIZESW(5),SIZESH(5) DATA 10,20,32,64,96,128 DATA 8,16,32,64,96,128 FOR I=0 TO 5 READ SIZESW(I) NEXT I FOR I=0 TO 5 READ SIZESH(I) NEXT I FONT 128 'DEFAULT USER PALETTE COPY ROM(10),SIZE(10) TO USERPALS OK=0 R=0 FILES CALL BLOADCHR(OK) CALL BLOADPAL(OK) CALL BLOADBG(OK) '==== TOUCH ZONES INIT ==== 'SETTINGS GLOBAL MAX_ZONE, ZONE_PAL 'STATUS GETTERS GLOBAL CUR_ZONE, ZONE_EVENT, ZONE_IN_X, ZONE_IN_Y, ZONE_RESULT 'INTERNAL GLOBAL ZONE_LAST_X, ZONE_LAST_Y GLOBAL E_DOWN, E_UP, E_OUT, E_DRAG E_DOWN=1 E_UP=2 E_OUT=3 E_DRAG=4 MAX_ZONE=33 DIM GLOBAL ZONEX(MAX_ZONE), ZONEY(MAX_ZONE), ZONEW(MAX_ZONE), ZONEH(MAX_ZONE), ZONEP(MAX_ZONE) TOUCHSCREEN '====================== CHAREDITOR: HIDECOUNT=0 CALL INITMEM CALL READPAL CALL READCHAR CALL READPAGE CALL DRAWCHARUI CALL RESETZONES ' BAR BUTTONS ZONE_PAL=5 CALL SETZONE(0,0,14,2,2) ' BAR TABS ZONE_PAL=-1 CALL SETZONE(7,14,14,2,2) CALL SETZONE(8,16,14,2,2) CALL SETZONE(9,18,14,2,2) ' PAGE AREA ZONE_PAL=-1 CALL SETZONE(10,2,10,16,4) ' PAGE UP/DOWN ZONE_PAL=5 CALL SETZONE(11,18,10,2,2) CALL SETZONE(12,18,12,2,2) ' SELECTION SIZE CALL SETZONE(13,0,10,2,2) CALL SETZONE(14,0,12,2,2) ' DRAW AREA ZONE_PAL=-1 CALL SETZONE(20,1,1,8,8) ' COLORS AREA CALL SETZONE(21,10,1,2,8) ' EDIT BUTTONS ZONE_PAL=5 CALL SETZONE(22,17,1,2,2) CALL SETZONE(23,17,3,2,2) CALL SETZONE(24,17,5,2,2) CALL SETZONE(25,17,7,2,2) CALL SETZONE(26,15,1,2,2) CALL SETZONE(27,15,3,2,2) CALL SETZONE(28,15,5,2,2) CALL SETZONE(29,15,7,2,2) CALL SETZONE(30,13,1,2,2) CALL SETZONE(31,13,3,2,2) CALL SETZONE(32,13,5,2,2) CALL SETZONE(33,13,7,2,2) DO CALL UPDATEZONES K$=INKEY$ IF K$<>"" THEN IF K$="2" THEN GOTO PALEDITOR IF K$="3" THEN GOTO MAPEDITOR END IF IF ZONE_EVENT=E_DOWN THEN IF CUR_ZONE=8 THEN GOTO PALEDITOR IF CUR_ZONE=9 THEN GOTO MAPEDITOR END IF CALL HANDLECHARSEL(0) IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN IF CUR_ZONE=20 THEN PXL(ZONE_IN_X,ZONE_IN_Y)=SELCOLOR PAL SELPAL MOD 4 CELL ZONE_LAST_X,ZONE_LAST_Y,16+SELCOLOR CALL WRITECHAR END IF IF CUR_ZONE=21 THEN SELCOLOR=ZONE_IN_Y\2 CALL DRAWSELCOLOR(10,1) END IF END IF IF ZONE_EVENT=E_UP THEN IF CUR_ZONE=0 THEN FTYPE$="CHARACTERS" GOTO FILESMENU END IF IF CUR_ZONE=22 THEN CALL BCLEAR IF CUR_ZONE=23 THEN CALL BCUT IF CUR_ZONE=24 THEN CALL BCOPY IF CUR_ZONE=25 THEN CALL BPASTE IF CUR_ZONE=26 THEN GOTO BIGEDITOR IF CUR_ZONE=27 THEN CALL BFLIPX IF CUR_ZONE=28 THEN CALL BFLIPY IF CUR_ZONE=29 THEN CALL BSPIN IF CUR_ZONE=30 THEN CALL BMOVEU IF CUR_ZONE=31 THEN CALL BMOVED IF CUR_ZONE=32 THEN CALL BMOVEL IF CUR_ZONE=33 THEN CALL BMOVER END IF IF HIDECOUNT>0 THEN DEC HIDECOUNT IF HIDECOUNT=0 THEN CALL HIDESELCHAR END IF WAIT VBL LOOP SUB DRAWCHARUI BG COPY 0,0,20,16 TO 0,0 CALL INITSPRITES CALL DRAWCHAR CALL DRAWSELCOLOR(10,1) CALL DRAWCHARNUM(0) CALL DRAWPAGE P=(SELPAL MOD 4) BG TINT 10,1 TO 11,8 PAL P END SUB SUB INITMEM ON RASTER OFF SPRITE OFF COPY ROM(1),SIZE(1) TO $FF00 COPY ROM(2),SIZE(2) TO $8000 BG SOURCE ROM(3) END SUB SUB INITSPRITES SPRITE 0 PAL 6 FLIP 0,0 SPRITE 1 PAL 6 FLIP 1,0 SPRITE 2 PAL 6 FLIP 0,1 SPRITE 3 PAL 6 FLIP 1,1 SPRITE 4 PAL 6 FLIP 0,0 SPRITE 5 PAL 6 FLIP 1,0 SPRITE 6 PAL 6 FLIP 0,1 SPRITE 7 PAL 6 FLIP 1,1 SPRITE 8 PAL 6 FLIP 0,0 SPRITE 9 PAL 6 FLIP 1,0 SPRITE 10 PAL 6 FLIP 0,1 SPRITE 11 PAL 6 FLIP 1,1 END SUB SUB DRAWCHAR PAL SELPAL MOD 4 FOR PY=0 TO 7 FOR PX=0 TO 7 CELL PX+1,PY+1,16+PXL(PX,PY) NEXT PX NEXT PY END SUB SUB DRAWSELCOLOR(X,Y) SX=X*8 SY=Y*8+SELCOLOR*2*8 SPRITE 4,SX,SY,20 SPRITE 5,SX+8,SY,20 SPRITE 6,SX,SY+8,20 SPRITE 7,SX+8,SY+8,20 END SUB SUB HANDLECHARSEL(MAP) IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN IF CUR_ZONE=10 THEN SELCHAR=(ZONE_IN_Y*16)+ZONE_IN_X+SELPAGE*64 CALL FIXSEL(MAP) IF NOT MAP THEN CALL READCHAR CALL DRAWCHAR END IF CALL DRAWCHARNUM(MAP) END IF END IF IF ZONE_EVENT=E_DOWN THEN IF CUR_ZONE=11 AND SELPAGE>0 THEN SELPAGE=SELPAGE-1 CALL READPAGE CALL DRAWCHARNUM(MAP) END IF IF CUR_ZONE=12 AND SELPAGE<3 THEN SELPAGE=SELPAGE+1 CALL READPAGE CALL DRAWCHARNUM(MAP) END IF IF CUR_ZONE=13 THEN IF SELSIZE>0 THEN DEC SELSIZE CALL DRAWCHARNUM(MAP) END IF IF CUR_ZONE=14 THEN IF SELSIZE<3 THEN INC SELSIZE CALL FIXSEL(MAP) IF NOT MAP THEN CALL READCHAR CALL DRAWCHAR END IF CALL DRAWCHARNUM(MAP) END IF END IF END SUB SUB FIXSEL(MAP) REM IF SELSIZE=0 THEN EXIT SUB IF MAP AND CELLSIZE=1 THEN SELCHAR=SELCHAR AND %11101110 IF SELSIZE>1 THEN SELSIZE=1 S=(SELSIZE+1)*2-1 ELSE S=SELSIZE END IF N=SELCHAR MOD 64 X=(N MOD 16) Y=(N\16) IF X+S>=16 THEN X=15-S IF Y+S>=4 THEN Y=3-S SELCHAR=(SELCHAR\64*64)+Y*16+X END SUB SUB DRAWCHARNUM(MAP) PAL 4 BG FILL 2,15 TO 13,15 CHAR 128 TEXT 3,15,"#" NUMBER 4,15,SELCHAR,3 NUMBER 8,15,SELPAGE+1,1 TEXT 9,15,"/4" IF SELCHAR>=SELPAGE*64 AND SELCHAR=SELPAGE*64 AND C<(SELPAGE+1)*64 THEN COPY USERCHARS+C*16,16 TO $8C00+(C MOD 64)*16 END IF END SUB SUB READCHAR FOR CY=0 TO SELSIZE FOR CX=0 TO SELSIZE CALL READCHARNUM(CX,CY) NEXT CX NEXT CY END SUB SUB READCHARNUM(CX,CY) AD=USERCHARS+(SELCHAR+CX+CY*16)*16 FOR PY=0 TO 7 FOR PX=0 TO 7 BV=2^(7-PX) PV=0 IF PEEK(AD) AND BV THEN PV=1 IF PEEK(AD+8) AND BV THEN PV=PV+2 PXL(PX+CX*8,PY+CY*8)=PV NEXT PX AD=AD+1 NEXT PY END SUB SUB BCLEAR S=(SELSIZE+1)*8-1 FOR PY=0 TO S FOR PX=0 TO S PXL(PX,PY)=SELCOLOR NEXT PX NEXT PY CALL WRITECHARSEL CALL DRAWCHAR CALL CHAR2PAGE CALL DRAWCHARNUM(0) END SUB SUB BCUT AD=USERCHARS+SELCHAR*16 S=(SELSIZE+1)*16 FOR I=0 TO SELSIZE COPY AD+I*256,S TO TEMPMEM+I*S FILL AD+I*256,S,0 NEXT I CALL READCHAR CALL DRAWCHAR CALL CHAR2PAGE CALL DRAWCHARNUM(0) END SUB SUB BCOPY AD=USERCHARS+SELCHAR*16 S=(SELSIZE+1)*16 FOR I=0 TO SELSIZE COPY AD+I*256,S TO TEMPMEM+I*S NEXT I CALL DRAWCHARNUM(0) END SUB SUB BPASTE AD=USERCHARS+SELCHAR*16 S=(SELSIZE+1)*16 FOR I=0 TO SELSIZE COPY TEMPMEM+I*S,S TO AD+I*256 NEXT I CALL READCHAR CALL DRAWCHAR CALL CHAR2PAGE CALL DRAWCHARNUM(0) END SUB SUB BFLIPX S=(SELSIZE+1)*8-1 S2=(SELSIZE+1)*4-1 FOR Y=0 TO S FOR X=0 TO S2 SWAP PXL(X,Y),PXL(S-X,Y) NEXT X NEXT Y CALL WRITECHARSEL CALL DRAWCHAR CALL DRAWCHARNUM(0) END SUB SUB BFLIPY S=(SELSIZE+1)*8-1 S2=(SELSIZE+1)*4-1 FOR X=0 TO S FOR Y=0 TO S2 SWAP PXL(X,Y),PXL(X,S-Y) NEXT Y NEXT X CALL WRITECHARSEL CALL DRAWCHAR CALL DRAWCHARNUM(0) END SUB SUB BSPIN S=(SELSIZE+1)*8-1 S2=(SELSIZE+1)*4-1 FOR X=0 TO S FOR Y=X+1 TO S SWAP PXL(X,Y),PXL(Y,X) NEXT Y NEXT X FOR X=0 TO S FOR Y=0 TO S2 SWAP PXL(X,Y),PXL(X,S-Y) NEXT Y NEXT X CALL WRITECHARSEL CALL DRAWCHAR CALL DRAWCHARNUM(0) END SUB SUB BMOVEU S=(SELSIZE+1)*8-1 FOR Y=0 TO S-1 FOR X=0 TO S SWAP PXL(X,Y),PXL(X,Y+1) NEXT X NEXT Y CALL WRITECHARSEL CALL DRAWCHAR CALL DRAWCHARNUM(0) END SUB SUB BMOVED S=(SELSIZE+1)*8-1 FOR Y=S TO 1 STEP -1 FOR X=0 TO S SWAP PXL(X,Y),PXL(X,Y-1) NEXT X NEXT Y CALL WRITECHARSEL CALL DRAWCHAR CALL DRAWCHARNUM(0) END SUB SUB BMOVEL S=(SELSIZE+1)*8-1 FOR Y=0 TO S FOR X=0 TO S-1 SWAP PXL(X,Y),PXL(X+1,Y) NEXT X NEXT Y CALL WRITECHARSEL CALL DRAWCHAR CALL DRAWCHARNUM(0) END SUB SUB BMOVER S=(SELSIZE+1)*8-1 FOR Y=0 TO S FOR X=S TO 1 STEP -1 SWAP PXL(X,Y),PXL(X-1,Y) NEXT X NEXT Y CALL WRITECHARSEL CALL DRAWCHAR CALL DRAWCHARNUM(0) END SUB '=================================== BIGEDITOR: IF SELCHAR MOD 16>14 THEN SELCHAR=SELCHAR-1 IF (SELCHAR MOD 64)\16>2 THEN SELCHAR=SELCHAR-16 CHAROFFSET=MIN(12,SELCHAR MOD 16) HIDECOUNT=0 CALL READCHARBIG CALL DRAWUIBIG CALL RESETZONES ZONE_PAL=-1 CALL SETZONE(0,0,0,16,16) CALL SETZONE(1,17,3,2,8) CALL SETZONE(2,16,12,4,4) ZONE_PAL=5 CALL SETZONE(3,17,0,2,2) DO CALL UPDATEZONES IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN IF CUR_ZONE=0 THEN PXL(ZONE_IN_X,ZONE_IN_Y)=SELCOLOR PAL SELPAL MOD 4 CELL ZONE_LAST_X,ZONE_LAST_Y,16+SELCOLOR CALL WRITECHARBIG END IF IF CUR_ZONE=1 THEN SELCOLOR=ZONE_IN_Y\2 CALL DRAWSELCOLOR(17,3) END IF IF CUR_ZONE=2 THEN SX=MAX(0,MIN(2,ZONE_IN_X-1)) SY=MAX(0,MIN(2,ZONE_IN_Y-1)) SELCHAR=(SY*16)+SX+SELPAGE*64+CHAROFFSET CALL READCHARBIG CALL DRAWCHARBIG CALL DRAWCHARNUMBIG END IF END IF IF ZONE_EVENT=E_UP THEN IF CUR_ZONE=3 THEN GOTO CHAREDITOR END IF IF HIDECOUNT>0 THEN HIDECOUNT=HIDECOUNT-1 IF HIDECOUNT=0 THEN CALL HIDESELCHAR END IF WAIT VBL LOOP SUB DRAWUIBIG PAL 0 BG COPY 0,32,20,16 TO 0,0 CALL DRAWCHARBIG CALL DRAWSELCOLOR(17,3) CALL DRAWPAGEBIG CALL DRAWCHARNUMBIG P=(SELPAL MOD 4) BG TINT 17,3 TO 18,10 PAL P END SUB SUB DRAWCHARBIG PAL SELPAL MOD 4 FOR PY=0 TO 15 FOR PX=0 TO 15 CELL PX,PY,16+PXL(PX,PY) NEXT PX NEXT PY END SUB SUB READCHARBIG CALL READCHARNUM(0,0) CALL READCHARNUM(1,0) CALL READCHARNUM(0,1) CALL READCHARNUM(1,1) END SUB SUB WRITECHARBIG CALL WRITECHARNUM(0,0) CALL WRITECHARNUM(1,0) CALL WRITECHARNUM(0,1) CALL WRITECHARNUM(1,1) CALL CHAR2PAGENUM(SELCHAR) CALL CHAR2PAGENUM(SELCHAR+1) CALL CHAR2PAGENUM(SELCHAR+16) CALL CHAR2PAGENUM(SELCHAR+17) END SUB SUB DRAWPAGEBIG PAL SELPAL MOD 4 CI=192+CHAROFFSET FOR CY=12 TO 15 FOR CX=16 TO 19 CELL CX,CY,CI INC CI NEXT CX ADD CI,12 NEXT CY END SUB SUB DRAWCHARNUMBIG N=SELCHAR MOD 64 SX=128+((N-CHAROFFSET) MOD 16)*8 SY=96+(N\16)*8 SPRITE 0,SX-1,SY-1,20 SPRITE 1,SX+9,SY-1,20 SPRITE 2,SX-1,SY+9,20 SPRITE 3,SX+9,SY+9,20 HIDECOUNT=60 END SUB '====================== PALEDITOR: CALL INITMEM CALL FIXSEL(-1) CALL READPAL CALL READPAGE CALL DRAWPALUI CALL RESETZONES ' BAR BUTTONS ZONE_PAL=5 CALL SETZONE(0,0,14,2,2) ' BAR TABS ZONE_PAL=-1 CALL SETZONE(7,14,14,2,2) CALL SETZONE(8,16,14,2,2) CALL SETZONE(9,18,14,2,2) ' PAGE AREA ZONE_PAL=-1 CALL SETZONE(10,2,10,16,4) ' PAGE UP/DOWN ZONE_PAL=5 CALL SETZONE(11,18,10,2,2) CALL SETZONE(12,18,12,2,2) ' SELECTION SIZE CALL SETZONE(13,0,10,2,2) CALL SETZONE(14,0,12,2,2) ' COLORS AREA ZONE_PAL=-1 CALL SETZONE(20,10,1,2,8) ' PALETTES AREA CALL SETZONE(21,1,1,7,4) ' RGB SLIDERS CALL SETZONE(22,13,1,2,4) CALL SETZONE(23,15,1,2,4) CALL SETZONE(24,17,1,2,4) ' PALETTE UP/DOWN ZONE_PAL=5 CALL SETZONE(25,1,7,2,2) CALL SETZONE(26,6,7,2,2) DO CALL UPDATEZONES K$=INKEY$ IF K$<>"" THEN IF K$="1" THEN GOTO CHAREDITOR IF K$="3" THEN GOTO MAPEDITOR END IF IF ZONE_EVENT=E_DOWN THEN IF CUR_ZONE=7 THEN GOTO CHAREDITOR IF CUR_ZONE=9 THEN GOTO MAPEDITOR END IF CALL HANDLECHARSEL(-1) '** PAL ** IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN IF CUR_ZONE=21 THEN SELPAL=SELPALPAGE*4+(ZONE_IN_X\4)+(ZONE_IN_Y\2)*2 CALL DRAWPAL END IF END IF '** PAL PAGE ** IF ZONE_EVENT=E_DOWN THEN IF CUR_ZONE=25 THEN SELPALPAGE=0 SELPAL=SELPAL MOD 4 CALL READPAL CALL DRAWPAL END IF IF CUR_ZONE=26 THEN SELPALPAGE=1 SELPAL=(SELPAL MOD 4)+4 CALL READPAL CALL DRAWPAL END IF END IF '** COLOR ** IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN IF CUR_ZONE=20 THEN SELCOLOR=ZONE_IN_Y\2 CALL DRAWSLIDERS CALL DRAWSELCOLOR(10,1) END IF END IF '** RGB SLIDERS ** IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN IF CUR_ZONE>=22 AND CUR_ZONE<=24 THEN V=3-ZONE_IN_Y A=USERPALS+SELPAL*4+SELCOLOR C=PEEK(A) R=INT(C/16) G=INT(C/4) MOD 4 B=C MOD 4 IF CUR_ZONE=22 THEN R=V IF CUR_ZONE=23 THEN G=V IF CUR_ZONE=24 THEN B=V C=R*16+G*4+B POKE A,C CALL DRAWSLIDERS CALL READPAL END IF END IF IF ZONE_EVENT=E_UP THEN IF CUR_ZONE=0 THEN FTYPE$="PALETTES" GOTO FILESMENU END IF END IF WAIT VBL LOOP SUB DRAWPALUI BG COPY 0,16,20,16 TO 0,0 PAL 4 TEXT 1,2,"#" TEXT 5,2,"#" TEXT 1,4,"#" TEXT 5,4,"#" TEXT 13,4,"R" TEXT 15,4,"G" TEXT 17,4,"B" CALL INITSPRITES FOR I=12 TO 14 SPRITE I PAL 7 SIZE 1 NEXT I CALL DRAWCHARNUM(-1) CALL DRAWPAGE CALL DRAWPAL CALL DRAWSELCOLOR(10,1) END SUB SUB READPAL COPY USERPALS+SELPALPAGE*16,16 TO $FF00 END SUB SUB DRAWPAL PAL 4 NUMBER 3,8,SELPALPAGE+1,1 TEXT 4,8,"/2" C=SELPALPAGE*4 NUMBER 2,2,C+0,1 NUMBER 6,2,C+1,1 NUMBER 2,4,C+2,1 NUMBER 6,4,C+3,1 CALL DRAWSLIDERS P=(SELPAL MOD 4) BG TINT 10,1 TO 11,8 PAL P BG TINT 2,10 TO 17,13 PAL P SX=8+(P MOD 2)*32 SY=8+(P\2)*16 SPRITE 8,SX,SY,20 SPRITE 9,SX+16,SY,20 SPRITE 10,SX,SY+8,20 SPRITE 11,SX+16,SY+8,20 END SUB SUB DRAWSLIDERS C=PEEK(USERPALS+SELPAL*4+SELCOLOR) R=C\16 G=C\4 MOD 4 B=C MOD 4 SPRITE 12,13*8,(4-R)*8,32 SPRITE 13,15*8,(4-G)*8,32 SPRITE 14,17*8,(4-B)*8,32 END SUB '====================== MAPEDITOR: SPRITE OFF CLS COPY USERCHARS,$C00 TO $8000 COPY ROM(2)+$E00,$200 TO $8E00 COPY USERPALS,32 TO $FF00 CALL FIXSEL(-1) CALL DRAWTOOLBAR CALL DRAWMAP ON RASTER CALL MAPEDITORRASTER CALL RESETZONES ' BAR BUTTONS ZONE_PAL=1 CALL SETZONE(0,0,14,2,2) ZONE_PAL=-1 CALL SETZONE(1,3,14,2,2) CALL SETZONE(2,5,14,2,2) CALL SETZONE(3,7,14,2,2) ZONE_PAL=1 CALL SETZONE(4,9,14,2,2) CALL SETZONE(5,11,14,2,2) ' BAR TABS ZONE_PAL=-1 CALL SETZONE(7,14,14,2,2) CALL SETZONE(8,16,14,2,2) CALL SETZONE(9,18,14,2,2) ' MAP AREA CALL SETZONE(10,0,0,20,14) IF CELLSIZE=0 THEN WINW=20 WINH=14 ELSE WINW=10 WINH=7 END IF OLDX=-1 OLDY=-1 FROMX=0 FROMY=0 DO CALL UPDATEZONES IF MAPBARST>0 THEN DEC MAPBARST IF MAPBARST=0 THEN SPRITE OFF 0 TO 7 END IF K$=INKEY$ IF K$<>"" THEN IF K$="1" THEN GOTO CHAREDITOR IF K$="2" THEN GOTO PALEDITOR IF K$="Z" THEN GOSUB SETSCROLLMODE IF K$="X" THEN GOSUB SETDRAWMODE IF K$="C" THEN GOSUB SETRUBBERMODE IF K$="V" THEN GOSUB TOGGLEFLIP IF K$="B" THEN GOSUB TOGGLEPRI K=ASC(K$) IF K=17 OR K$="D" THEN MAPX=MIN(MAPX+1,MAPW-WINW) CALL DRAWMAP ELSE IF K=18 OR K$="A" THEN MAPX=MAX(MAPX-1,0) CALL DRAWMAP ELSE IF K=19 OR K$="S" THEN MAPY=MIN(MAPY+1,MAPH-WINH) CALL DRAWMAP ELSE IF K=20 OR K$="W" THEN MAPY=MAX(MAPY-1,0) CALL DRAWMAP END IF END IF IF ZONE_EVENT=E_DOWN THEN IF CUR_ZONE=7 THEN GOTO CHAREDITOR IF CUR_ZONE=8 THEN GOTO PALEDITOR IF CUR_ZONE=1 THEN GOSUB SETSCROLLMODE IF CUR_ZONE=2 THEN GOSUB SETDRAWMODE IF CUR_ZONE=3 THEN GOSUB SETRUBBERMODE END IF IF (ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG) AND CUR_ZONE=10 THEN IF CELLSIZE=0 THEN X=ZONE_IN_X Y=ZONE_IN_Y ELSE X=ZONE_IN_X\2 Y=ZONE_IN_Y\2 END IF IF ZONE_EVENT=E_DOWN THEN OLDX=-1 FROMX=X FROMY=Y END IF IF X<>OLDX OR Y<>OLDY THEN '** SCROLL MODE ** IF EDMODE=0 THEN IF OLDX>=0 THEN MAPX=MIN(MAX(MAPX-X+DRAGX,0),MAPW-WINW) MAPY=MIN(MAX(MAPY-Y+DRAGY,0),MAPH-WINH) CALL DRAWMAP END IF DRAGX=X DRAGY=Y END IF '** DRAW CHAR ** IF EDMODE=1 AND TOOLMODE=0 THEN CALL MAPCHAR(X,Y) END IF '** DRAW PALETTE ** IF EDMODE=1 AND TOOLMODE=1 THEN CALL MAPPAL(X,Y) END IF '** DRAW PRIO ** IF EDMODE=1 AND TOOLMODE=2 THEN CALL MAPPRI(X,Y) END IF '** FILL CHAR ** IF EDMODE=1 AND TOOLMODE=3 THEN IF Y>FROMY THEN SY=SELSIZE+1 ELSE SY=-SELSIZE-1 IF X>FROMX THEN SX=SELSIZE+1 ELSE SX=-SELSIZE-1 FOR CY=FROMY TO Y STEP SY FOR CX=FROMX TO X STEP SX CALL MAPCHAR(CX,CY) NEXT CX NEXT CY END IF '** FILL PALETTE ** IF EDMODE=1 AND TOOLMODE=4 THEN IF Y>FROMY THEN SY=1 ELSE SY=-1 IF X>FROMX THEN SX=1 ELSE SX=-1 FOR CY=FROMY TO Y STEP SY FOR CX=FROMX TO X STEP SX CALL MAPPAL(CX,CY) NEXT CX NEXT CY END IF '** FILL PRIO ** IF EDMODE=1 AND TOOLMODE=5 THEN IF Y>FROMY THEN SY=1 ELSE SY=-1 IF X>FROMX THEN SX=1 ELSE SX=-1 FOR CY=FROMY TO Y STEP SY FOR CX=FROMX TO X STEP SX CALL MAPPRI(CX,CY) NEXT CX NEXT CY END IF '** SELECT STAMP ** IF EDMODE=1 AND TOOLMODE=6 THEN CALL MAPDRAWSEL(FROMX,FROMY,X,Y) END IF '** USE STAMP ** IF EDMODE=1 AND TOOLMODE=7 THEN CALL MAPSTAMP(X,Y) END IF '** RUBBER MODE ** IF EDMODE=2 THEN A=USERBG+4+((Y+MAPY)*MAPW+(X+MAPX))*2 POKEW A,0 A=$9000+(Y*32+X)*2 POKEW A,0 END IF OLDX=X OLDY=Y END IF END IF IF ZONE_EVENT=E_UP THEN IF CUR_ZONE=0 THEN FTYPE$="BACKGROUND" GOTO FILESMENU END IF IF CUR_ZONE=4 THEN GOSUB TOGGLEFLIP IF CUR_ZONE=5 THEN GOSUB TOGGLEPRI IF CUR_ZONE=10 THEN IF EDMODE=1 AND TOOLMODE=6 THEN SPRITE OFF 0 TO 3 CALL MAPSTAMPCOPY(MAPX+FROMX,MAPY+FROMY,MAPX+X,MAPY+Y) TOOLMODE=7 CALL DRAWTOOLBAR END IF END IF END IF WAIT VBL LOOP SETSCROLLMODE: EDMODE=0 CALL DRAWEDMODE RETURN SETDRAWMODE: IF EDMODE=1 THEN RETURN MAPTOOLSMENU ELSE EDMODE=1 CALL DRAWEDMODE END IF RETURN SETRUBBERMODE: EDMODE=2 CALL DRAWEDMODE RETURN TOGGLEFLIP: ADD FLIPMODE,1,0 TO 3 CALL DRAWTOOLBAR RETURN TOGGLEPRI: ADD PRI,1,0 TO 1 CALL DRAWTOOLBAR RETURN SUB MAPCHAR(X,Y) W=MIN(SELSIZE,WINW-1-X) H=MIN(SELSIZE,WINH-1-Y) FOR I=0 TO H FOR J=0 TO W IF CELLSIZE=0 THEN C=SELCHAR+I*16+J ELSE C=SELCHAR+I*2*16+J*2 END IF ATT=SELPAL+FLIPMODE*8+PRI*32 IF FLIPMODE AND %01 THEN CX=X+W-J ELSE CX=X+J IF FLIPMODE AND %10 THEN CY=Y+H-I ELSE CY=Y+I A=$9000+(CY*32+CX)*2 POKE A,C POKE A+1,ATT A=USERBG+4+((CY+MAPY)*MAPW+(CX+MAPX))*2 POKE A,C POKE A+1,ATT NEXT J NEXT I END SUB SUB MAPPAL(X,Y) A=USERBG+4+((Y+MAPY)*MAPW+(X+MAPX))*2+1 POKE A,(PEEK(A) AND %11111000) OR SELPAL A=$9000+(Y*32+X)*2+1 POKE A,(PEEK(A) AND %11111000) OR SELPAL END SUB SUB MAPPRI(X,Y) 'WRITE TO DATA... A=USERBG+4+((Y+MAPY)*MAPW+(X+MAPX))*2+1 ATT=PEEK(A) AND %11011111 POKE A,ATT OR PRI*32 'WRITE TO SCREEN... A=$9000+(Y*32+X)*2+1 ATT=PEEK(A) AND %11011000 POKE A,ATT OR PRI*32 OR PRI END SUB SUB MAPSTAMPCOPY(X1,Y1,X2,Y2) XL=MIN(X1,X2) XR=MAX(X1,X2) YT=MIN(Y1,Y2) YB=MAX(Y1,Y2) STAMPW=XR-XL STAMPH=YB-YT FOR I=0 TO STAMPH SRC=USERBG+4+((YT+I)*MAPW+XL)*2 TAR=TEMPMEM+I*(STAMPW+1)*2 COPY SRC,(STAMPW+1)*2 TO TAR NEXT I END SUB SUB MAPSTAMP(X,Y) W=MIN(STAMPW,WINW-1-X) H=MIN(STAMPH,WINH-1-Y) FOR I=0 TO H FOR J=0 TO W A=TEMPMEM+(I*(STAMPW+1)+J)*2 V=PEEKW(A) A=USERBG+4+((Y+MAPY+I)*MAPW+(X+MAPX+J))*2 POKEW A,V A=$9000+((Y+I)*32+X+J)*2 POKEW A,V NEXT J NEXT I END SUB SUB MAPEDITORRASTER IF RASTER=0 THEN CELL SIZE 0,CELLSIZE IF EDMODE=1 AND (TOOLMODE=2 OR TOOLMODE=5) THEN PALETTE 0,%000000,%110000,%100000,%010000 PALETTE 1,%000000,%001100,%001000,%000100 ELSE COPY USERPALS,8 TO $FF00 END IF END IF IF RASTER=112 THEN CELL SIZE 0,0 PALETTE 0,%000000,%111111,%101010,%010101 PALETTE 1,%000000,%001111,%000101,%000000 END IF END SUB SUB DRAWEDMODE COPY USERPALS,32 TO $FF00 CALL DRAWMAP CALL DRAWTOOLBAR END SUB SUB DRAWMAP BG SOURCE USERBG BG COPY MAPX,MAPY,20,14 TO 0,0 IF EDMODE=1 AND (TOOLMODE=2 OR TOOLMODE=5) THEN CALL DRAWPRIO CALL MAPBARS END SUB SUB DRAWPRIO FOR CY=0 TO 13 FOR CX=0 TO 19 A=$9000+(CY*32+CX)*2+1 ATT=PEEK(A) AND %11111000 IF ATT AND %00100000 THEN ATT=ATT OR 1 POKE A,ATT NEXT CX NEXT CY END SUB SUB DRAWTOOLBAR PAL 0 BG FILL 0,14 TO 19,14 CHAR 224 BG FILL 0,15 TO 19,15 CHAR 240 CELL 0,14,230 CELL 1,14,231 CELL 0,15,246 CELL 1,15,247 CALL DRAWTOOLBUTTON(3,225,229,EDMODE=0,0) CALL DRAWTOOLBUTTON(5,238,239,EDMODE=1,-1) CALL DRAWTOOLBUTTON(7,225,252,EDMODE=2,0) CALL DRAWFLAGBUTTON(9,232,233,248+FLIPMODE) CALL DRAWFLAGBUTTON(11,234,235,236+PRI) CALL DRAWTOOLBUTTON(14,225,226,0,0) CALL DRAWTOOLBUTTON(16,225,227,0,0) CALL DRAWTOOLBUTTON(18,225,228,-1,0) IF TOOLMODE=6 THEN CALL COPYCHAR(225,238) CALL COPYCHAR(195,239) ELSE IF TOOLMODE=7 THEN CALL COPYCHAR(225,238) CALL COPYCHAR(196,239) ELSE CALL COPYCHAR(208+TOOLMODE\3,238) CALL COPYCHAR(192+(TOOLMODE MOD 3),239) END IF END SUB SUB DRAWTOOLBUTTON(X,ICON1,ICON2,SEL,MENU) CELL X,14,ICON1 CELL X+1,14,ICON2 IF SEL THEN CELL X,15,241 IF MENU THEN CELL X+1,15,245 ELSE CELL X+1,15,242 ELSE CELL X,15,243 CELL X+1,15,244 END IF END SUB SUB DRAWFLAGBUTTON(X,C1,C2,C3) CELL X,14,C1 CELL X+1,14,C2 CELL X,15,246 CELL X+1,15,C3 END SUB SUB COPYCHAR(SRC,DST) COPY ROM(2)+SRC*16,16 TO $8000+DST*16 END SUB SUB MAPBARS IF CELLSIZE=0 THEN WINW=20 WINH=14 CS=8 ELSE WINW=10 WINH=7 CS=16 END IF SPRITE 0 PAL 0 FLIP 0,0 PRIO 1 SPRITE 1 PAL 0 FLIP 1,0 PRIO 1 SPRITE 2 PAL 0 FLIP 0,0 PRIO 1 SPRITE 3 PAL 0 FLIP 0,1 PRIO 1 BX=MAPX/MAPW*154 BW=WINW*154/MAPW BY=MAPY/MAPH*105 BH=WINH*105/MAPH SPRITE 0,BX-1,0,253 SPRITE 1,BW+BX-1,0,253 SPRITE 2,0,BY-1,254 SPRITE 3,0,BH+BY-1,254 SPRITE 4 PAL 0 PRIO 1 SPRITE 5 PAL 0 PRIO 1 SPRITE 6 PAL 0 PRIO 1 SPRITE 7 PAL 0 PRIO 1 BX=-((MAPX*CS) MOD 160)-4 BY=-((MAPY*CS) MOD 128)-4 IF BX>-32 THEN SPRITE 4,BX,BY,255 SPRITE 5,BX,BY+128,255 ELSE SPRITE OFF 4 TO 5 END IF SPRITE 6,BX+160,BY,255 SPRITE 7,BX+160,BY+128,255 MAPBARST=60 END SUB SUB MAPDRAWSEL(X1,Y1,X2,Y2) XL=MIN(X1,X2) XR=MAX(X1,X2) YT=MIN(Y1,Y2) YB=MAX(Y1,Y2) SPRITE 0 PAL 0 FLIP 0,0 PRIO 1 SPRITE 1 PAL 0 FLIP 1,0 PRIO 1 SPRITE 2 PAL 0 FLIP 0,1 PRIO 1 SPRITE 3 PAL 0 FLIP 1,1 PRIO 1 SPRITE 0,XL*8-1,YT*8-1,253 SPRITE 1,XR*8+1,YT*8-1,253 SPRITE 2,XL*8-1,YB*8+1,253 SPRITE 3,XR*8+1,YB*8+1,253 END SUB MAPTOOLSMENU: CALL INITMEM MENU$(0)="DRAW CHARACTER" MENU$(1)="DRAW PALETTE" MENU$(2)="DRAW PRIORITY" MENU$(3)="FILL CHARACTER" MENU$(4)="FILL PALETTE" MENU$(5)="FILL PRIORITY" MENU$(6)="STAMP" CALL SHOWMENU("SELECT TOOL",6,R) TOOLMODE=R GOTO MAPEDITOR '====================== SIZEMENU: CALL INITMEM GLOBAL NEWW,NEWH,NEWC NEWW=MAPW NEWH=MAPH NEWC=CELLSIZE CALL DRAWSIZEMENU CALL DRAWSIZE CALL RESETZONES ZONE_PAL=-1 CALL SETZONE(0,1,4,5,6) CALL SETZONE(1,7,4,5,6) CALL SETZONE(2,14,4,5,2) ZONE_PAL=5 CALL SETZONE(3,0,14,10,2) CALL SETZONE(4,10,14,10,2) DO CALL UPDATEZONES IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN IF CUR_ZONE=0 THEN NEWW=SIZESW(ZONE_IN_Y) CALL DRAWSIZE END IF IF CUR_ZONE=1 THEN NEWH=SIZESH(ZONE_IN_Y) CALL DRAWSIZE END IF IF CUR_ZONE=2 THEN NEWC=ZONE_IN_Y CALL DRAWSIZE END IF END IF IF ZONE_EVENT=E_UP THEN IF CUR_ZONE=3 THEN IF NEWW*NEWH*2<=8192 AND (NEWC=1 OR (NEWW>=20 AND NEWH>=16)) THEN CALL BCHANGESIZE GOTO MAPEDITOR END IF END IF IF CUR_ZONE=4 THEN GOTO FILESMENU END IF WAIT VBL LOOP SUB DRAWSIZEMENU BG SOURCE ROM(5) BG COPY 0,0,20,16 TO 0,0 PAL 5 TEXT 0,0,"BACKGROUND SIZE " PAL 4 TEXT 1,2,"WIDTH" FOR I=0 TO 5 TEXT 1,4+I,STR$(SIZESW(I)) NEXT I TEXT 7,2,"HEIGHT" FOR I=0 TO 5 TEXT 7,4+I,STR$(SIZESH(I)) NEXT I TEXT 14,2,"CELL" TEXT 14,4,"8X8" TEXT 14,5,"16X16" TEXT 4,15,"OK" TEXT 12,15,"CANCEL" END SUB SUB DRAWSIZE BG TINT 0,4 TO 19,12 PAL 4 'WIDTH FOR I=0 TO 5 IF NEWW=SIZESW(I) THEN BG TINT 1,4+I TO 5,4+I PAL 5 NEXT I 'HEIGHT FOR I=0 TO 5 IF NEWH=SIZESH(I) THEN BG TINT 7,4+I TO 11,4+I PAL 5 NEXT I 'CELL BG TINT 14,4+NEWC TO 18,4+NEWC PAL 5 PAL 4 IF NEWW*NEWH*2>8192 THEN TEXT 1,11,"TOO BIG" ELSE IF NEWC=0 AND (NEWW<20 OR NEWH<16) THEN TEXT 1,11,"TOO SMALL" ELSE TEXT 1,11," " END IF END SUB SUB BCHANGESIZE IF NEWW>MAPW THEN FOR Y=NEWH-1 TO 0 STEP -1 S=USERBG+4+Y*MAPW*2 D=USERBG+4+Y*NEWW*2 N=MAPW*2 COPY S,N TO D FILL D+N,(NEWW-MAPW)*2 NEXT Y ELSE IF NEWWMAPH THEN FILL USERBG+4+MAPH*NEWW*2,(NEWH-MAPH)*NEWW*2 MAPW=NEWW MAPH=NEWH CELLSIZE=NEWC CALL UPDATEMAPINFO MAPX=0 MAPY=0 CALL SHOWMESSAGE("SIZE CHANGED") END SUB '====================== FILESMENU: CALL INITMEM CALL FILE2SEL FILES CALL DRAWFILESMENU CALL RESETZONES ZONE_PAL=-1 CALL SETZONE(0,0,2,18,11) ZONE_PAL=5 CALL SETZONE(1,18,2,2,2) CALL SETZONE(2,18,11,2,2) CALL SETZONE(10,0,14,2,2) IF FTYPE$="CHARACTERS" THEN CALL SETZONE(15,10,14,2,2) END IF IF FTYPE$="CHARACTERS" OR FTYPE$="BACKGROUND" THEN CALL SETZONE(16,12,14,2,2) END IF CALL SETZONE(17,14,14,2,2) CALL SETZONE(18,16,14,2,2) CALL SETZONE(19,18,14,2,2) DO CALL UPDATEZONES IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN IF CUR_ZONE=0 THEN SELFILE=ZONE_IN_Y+FILESOFFS CALL DRAWFILES CALL SEL2FILE END IF END IF IF ZONE_EVENT=E_DOWN THEN IF CUR_ZONE=1 THEN FILESOFFS=0 CALL DRAWFILES END IF IF CUR_ZONE=2 THEN FILESOFFS=5 CALL DRAWFILES END IF END IF IF ZONE_EVENT=E_UP THEN IF CUR_ZONE=10 THEN GOTO EXITFILES IF FTYPE$="BACKGROUND" THEN IF CUR_ZONE=16 THEN GOTO SIZEMENU END IF IF FTYPE$="CHARACTERS" THEN IF CUR_ZONE=15 THEN CALL BLOADPAGE(OK) IF OK THEN GOTO EXITFILES END IF IF CUR_ZONE=16 THEN MENU$(0)="NORMAL" MENU$(1)="OPAQUE" MENU$(2)="OUTLINE" MENU$(3)="SHADOW" MENU$(4)="CANCEL" CALL SHOWMENU("LOAD FONT?",4,R) IF R=4 THEN CALL DRAWFILESMENU ELSE COPY ROM(11)+R*$400,$400 TO $A000+SELPAGE*$400 GOTO CHAREDITOR END IF END IF END IF IF CUR_ZONE=17 THEN MENU$(0)="CLEAR" MENU$(1)="CANCEL" CALL SHOWMENU("CLEAR "+FTYPE$+"?",1,R) IF R=0 THEN IF FTYPE$="PALETTES" THEN CALL BNEWPAL IF FTYPE$="CHARACTERS" THEN CALL BNEWCHAR IF FTYPE$="BACKGROUND" THEN CALL BNEWBG GOTO EXITFILES ELSE CALL DRAWFILESMENU END IF END IF IF CUR_ZONE=18 THEN CALL BLOAD(OK) IF OK THEN GOTO EXITFILES END IF IF CUR_ZONE=19 THEN CALL BSAVE(OK) IF OK THEN GOTO EXITFILES END IF END IF WAIT VBL LOOP EXITFILES: IF FTYPE$="PALETTES" THEN GOTO PALEDITOR IF FTYPE$="CHARACTERS" THEN GOTO CHAREDITOR IF FTYPE$="BACKGROUND" THEN GOTO MAPEDITOR END SUB DRAWFILESMENU BG SOURCE ROM(4) BG COPY 0,0,20,14 TO 0,0 BARY=14 IF FTYPE$="CHARACTERS" THEN BARY=14 IF FTYPE$="PALETTES" THEN BARY=16 IF FTYPE$="BACKGROUND" THEN BARY=18 BG COPY 0,BARY,20,2 TO 0,14 PAL 5 TEXT 0,0,FTYPE$+" FILE" SPRITE OFF CALL DRAWFILES END SUB SUB DRAWFILES FOR I=0 TO 10 N=I+FILESOFFS IF N=SELFILE THEN PAL 5 ELSE PAL 4 NUMBER 0,2+I,N,2 TEXT 2,2+I,": " TEXT 3,2+I,LEFT$(FILE$(N),15) NEXT I END SUB SUB FILE2SEL IF FTYPE$="PALETTES" THEN SELFILE=PALFILE IF FTYPE$="CHARACTERS" THEN SELFILE=CHARFILE IF FTYPE$="BACKGROUND" THEN SELFILE=BGFILE END SUB SUB SEL2FILE IF FTYPE$="PALETTES" THEN PALFILE=SELFILE IF FTYPE$="CHARACTERS" THEN CHARFILE=SELFILE IF FTYPE$="BACKGROUND" THEN BGFILE=SELFILE END SUB SUB BNEWCHAR FILL USERCHARS,$1000,0 CHARFILE=-1 SELPAGE=0 SELCHAR=1 END SUB SUB BNEWPAL COPY ROM(10),SIZE(10) TO USERPALS PALFILE=-1 SELPALPAGE=0 SELPAL=0 END SUB SUB BNEWBG FILL USERBG,$2004,0 CALL UPDATEMAPINFO MAPX=0 MAPY=0 BGFILE=-1 END SUB SUB BLOAD(OK) OK=0 IF SELFILE=-1 THEN CALL SHOWMESSAGE("SELECT FILE") EXIT SUB END IF IF FTYPE$="PALETTES" THEN CALL BLOADPAL(OK) IF FTYPE$="CHARACTERS" THEN CALL BLOADCHR(OK) IF FTYPE$="BACKGROUND" THEN CALL BLOADBG(OK) IF OK THEN CALL SHOWMESSAGE(FTYPE$+" LOADED") ELSE SELFILE=-1 CALL DRAWFILESMENU CALL SHOWMESSAGE("INVALID FORMAT") END IF END SUB SUB BSAVE(OK) OK=0 IF SELFILE=-1 THEN CALL SHOWMESSAGE("SELECT FILE") EXIT SUB END IF IF FTYPE$="PALETTES" THEN CALL BSAVEPAL IF FTYPE$="CHARACTERS" THEN CALL BSAVECHR IF FTYPE$="BACKGROUND" THEN CALL BSAVEBG CALL DRAWFILES CALL SHOWMESSAGE(FTYPE$+" SAVED") OK=-1 END SUB SUB BLOADPAGE(OK) OK=0 IF SELFILE=-1 THEN CALL SHOWMESSAGE("SELECT FILE") EXIT SUB END IF MENU$(0)="PAGE 1" MENU$(1)="PAGE 2" MENU$(2)="PAGE 3" MENU$(3)="PAGE 4" MENU$(4)="CANCEL" R=0 CALL SHOWMENU("LOAD ONLY FROM",4,R) IF R=4 THEN CALL DRAWFILESMENU ELSE LOAD SELFILE,USERCHARS+SELPAGE*$400,$400,R*$400 CALL SHOWMESSAGE("PAGE LOADED") OK=-1 END IF END SUB '====================== SUB BLOADCHR(OK) OK=0 IF FSIZE(CHARFILE)>$1000 THEN CHARFILE=-1 EXIT SUB END IF FILL USERCHARS,$1000,0 LOAD CHARFILE,USERCHARS OK=-1 END SUB SUB BSAVECHR 'FIND LAST USED CHARACTER N=0 I=255 REPEAT A=USERCHARS+I*16 FOR J=0 TO 15 IF PEEK(A+J)<>0 THEN N=I+1 NEXT J I=I-1 IF I<0 THEN N=1 UNTIL N<>0 F$=FILE$(CHARFILE) IF F$="" THEN F$="CHARACTERS" SAVE CHARFILE,F$,USERCHARS,N*16 END SUB SUB BLOADPAL(OK) OK=0 IF FSIZE(PALFILE)>32 THEN PALFILE=-1 EXIT SUB END IF LOAD PALFILE,USERPALS OK=-1 END SUB SUB BSAVEPAL F$=FILE$(PALFILE) IF F$="" THEN F$="PALETTES" SAVE PALFILE,F$,USERPALS,32 END SUB SUB UPDATEMAPINFO POKE USERBG,0 POKE USERBG+1,CELLSIZE POKE USERBG+2,MAPW POKE USERBG+3,MAPH END SUB SUB RESETBG FILL USERBG,$2004,0 MAPW=20 MAPH=16 MAPX=0 MAPY=0 CELLSIZE=0 SELCHAR=1 CALL UPDATEMAPINFO END SUB SUB BLOADBG(OK) OK=0 CALL RESETBG IF FSIZE(BGFILE)=0 THEN OK=-1 EXIT SUB END IF IF FSIZE(BGFILE)<4 OR FSIZE(BGFILE)>$2004 THEN BGFILE=-1 EXIT SUB END IF LOAD BGFILE,USERBG T=PEEK(USERBG) CELLSIZE=PEEK(USERBG+1) MAPW=PEEK(USERBG+2) MAPH=PEEK(USERBG+3) MAPX=0 MAPY=0 IF CELLSIZE=0 THEN SELCHAR=1 ELSE SELCHAR=2 'CHECK FILE FORMAT IF T=0 AND CELLSIZE<2 AND MAPW>0 AND MAPH>0 THEN OK=-1 ELSE BGFILE=-1 CALL RESETBG END IF END SUB SUB BSAVEBG F$=FILE$(BGFILE) IF F$="" THEN F$="BG" SAVE BGFILE,F$,USERBG,4+MAPW*MAPH*2 END SUB '==== GENERIC UI ==== SUB SHOWMESSAGE(MSG$) COPY $9000,$800 TO TEMPMEM PAL 5 BG FILL 0,0 TO 19,0 CHAR 128 TEXT 0,0,MSG$ WAIT 60 COPY TEMPMEM,$800 TO $9000 END SUB SUB SHOWMENU(MSG$,MAXI,RESULT) PAL 5 PRIO 1 BG FILL 0,0 TO 19,0 CHAR 128 TEXT (20-LEN(MSG$))/2,0,MSG$ PAL 4 Y=3+MAXI*2 BG FILL 0,1 TO 19,Y CHAR 1 BG FILL 0,1 TO 19,1 CHAR 3 BG FILL 0,Y TO 19,Y CHAR 5 FOR I=0 TO MAXI T$=MENU$(I) TEXT (20-LEN(T$))/2,2+I*2,T$ NEXT I WHILE TOUCH WAIT VBL WEND DO IF TOUCH AND TOUCH.Y>=12 THEN I=(((TOUCH.Y-4)\8)-1)\2 IF I<=MAXI THEN CALL HIGHLIGHT(0,2+I*2,19,2+I*2) WHILE TOUCH WAIT VBL WEND RESULT=I EXIT END IF END IF WAIT VBL LOOP PRIO 0 END SUB SUB HIGHLIGHT(X1,Y1,X2,Y2) BG TINT X1,Y1 TO X2,Y2 PAL 5 END SUB '==== TOUCH ZONES SUBPROGRAMS ==== SUB SETZONE(N,X,Y,W,H) ZONEX(N)=X ZONEY(N)=Y ZONEW(N)=W ZONEH(N)=H IF ZONE_PAL>=0 AND ZONE_PAL<8 THEN 'HIGHLIGHT AND NORMAL PALETTE P=CELL.A(X,Y) AND %111 ZONEP(N)=(ZONE_PAL*16)+P ELSE 'NO HIGHLIGHT ZONEP(N)=$FF END IF END SUB SUB CLEARZONE(N) CALL SETZONE(N,0,0,0,0) END SUB SUB RESETZONES FOR I=0 TO MAX_ZONE CALL CLEARZONE(I) NEXT I CUR_ZONE=-1 ZONE_PAL=-1 END SUB SUB UPDATEZONES CX=TOUCH.X\8 CY=TOUCH.Y\8 IF ZONE_EVENT=E_UP OR ZONE_EVENT=E_OUT THEN CUR_ZONE=-1 ZONE_EVENT=0 IF CUR_ZONE>=0 THEN CALL INSIDEZONE(CUR_ZONE,CX,CY) IF ZONE_RESULT THEN ZONE_IN_X=CX-ZONEX(CUR_ZONE) ZONE_IN_Y=CY-ZONEY(CUR_ZONE) IF NOT TOUCH THEN CALL PAINTZONE(CUR_ZONE,0) ZONE_EVENT=E_UP ELSE IF CX<>ZONE_LAST_X OR CY<>ZONE_LAST_Y THEN ZONE_EVENT=E_DRAG END IF ELSE CALL PAINTZONE(CUR_ZONE,0) ZONE_EVENT=E_OUT END IF ELSE IF TAP THEN FOR I=0 TO MAX_ZONE IF ZONEW(I)>0 THEN CALL INSIDEZONE(I,CX,CY) IF ZONE_RESULT THEN ZONE_EVENT=E_DOWN CUR_ZONE=I ZONE_IN_X=CX-ZONEX(I) ZONE_IN_Y=CY-ZONEY(I) CALL PAINTZONE(I,1) END IF END IF NEXT I END IF ZONE_LAST_X=CX ZONE_LAST_Y=CY END SUB SUB INSIDEZONE(N,CX,CY) ZONE_RESULT=(CX>=ZONEX(N) AND CX=ZONEY(N) AND CY$FF THEN IF SEL THEN P=P\16 ELSE P=P MOD 16 BG TINT ZONEX(N),ZONEY(N) TO ZONEX(N)+ZONEW(N)-1,ZONEY(N)+ZONEH(N)-1 PAL P END IF END SUB '====================== #1:MAIN PALETTES 003F2F0000383400003C0C00003F3C00 003F2A15000F050000300000003F2A15 #2:MAIN CHARACTERS 00000000000000000000000000000000 0000000000000000FFFFFFFFFFFFFFFF 0101010101010101FFFFFFFFFFFFFFFF FF0000000000000000FFFFFFFFFFFFFF 80808080808080807F7F7F7F7F7F7F7F 00000000000000FFFFFFFFFFFFFFFFFF FF000000000000FF00FFFFFFFFFFFFFF 81818181818181817F7F7F7F7F7F7F7F FF80808080808080007F7F7F7F7F7F7F FE0101010101010101FFFFFFFFFFFFFF 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 FF80B9A2B2A18080007F7F7F7F7F7F7F FE0131A9A929010101FFFFFFFFFFFFFF 00000000000000000000000000000000 FFFFFFFFFFFFFFFF0000000000000000 0000000000000000FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FF808080808080800000000000000000 40E0E0E0E0E0E0E0FFFFFFFFFFFFFFFF 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 FF80B1AAB3A28080007F7F7F7F7F7F7F FE0119A1A999010101FFFFFFFFFFFFFF 3F7FF0FFF0FF403F00000F000F807F3F F8FC1EFE1EFE04F80000E000E002FCF8 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 11397D11111101FFFFFFFFFFFFFFFFFF 1111117D391101FFFFFFFFFFFFFFFFFF 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 3179CDFDCDCD01FFFFFFFFFFFFFFFFFF 012161FD612101FFFFFFFFFFFFFFFFFF 011119FD191101FFFFFFFFFFFFFFFFFF FF80808080818387007F7F7F7F7F7F7F FE0101010181C1E101FFFFFFFFFFFFFF FF80808080808080007F7F7F7F7F7F7F FF0179FDFDFD490100FFFFB7FFFFFFFF FF0155555555550100FFBFFFFBEFFFFF FF017D7D7D757D0100FFFFD7FFDFFFFF FF809AA2A29B8080007F7F7F7F7F7F7F FE01312931A9010101FFFFFFFFFFFFFF FF809AA2A2998080007F7F7F7F7F7F7F FE01B9919191010101FFFFFFFFFFFFFF FF8099A2A2998080007F7F7F7F7F7F7F FE0131A9B121010101FFFFFFFFFFFFFF FF80B1AAB3A28080007F7F7F7F7F7F7F FE0119B189B1010101FFFFFFFFFFFFFF FF80B4A4B4A68080007F7F7F7F7F7F7F FE01B1A9B1A1010101FFFFFFFFFFFFFF 8F9F80808080807F7F7F7F7F7F7F7FFF F1F90101010101FFFFFFFFFFFFFFFFFF 808080808080C0FF7F7F7F7F7F7FBFFF 01010101010103FFFFFFFFFFFFFFFFFF FF80ACA9ADAC8080007F7F7F7F7F7F7F FE012DA9AD2D010101FFFFFFFFFFFFFF 808080808080807F7F7F7F7F7F7F7FFF 11397DF9F16101FFFFEFC7EFFFFFFFFF 0109D539D50901FFFFFFFFFFFFFFFFFF F1F1FDFD3D3D01FFFF9FBFE7E7FFFFFF 71A9BDBDFD3D01FFFFDFFFE7E7FFFFFF 0101296D290101FFFFFFFFFFFFFFFFFF 01113901391101FFFFFFFFFFFFFFFFFF 1D195545453901FFFFFFFFFFFFFFFFFF FF80B6A596B48080007F7F7F7F7F7F7F FE0159555555010101FFFFFFFFFFFFFF FF80808080809F8F007F7F7F7F7F7F7F FE0101010101F9F101FFFFFFFFFFFFFF 808080C0FFFFFFFF7F7F7FBFFFFFFFFF 01010103FFFFFFFFFFFFFFFFFFFFFFFF FDFDFDFDFDFD01FFE3E3E31F1F1FFFFF FDCDCDFDFD7D01FFFFB7B787B7FFFFFF FF80BAA2B2A28080007F7F7F7F7F7F7F FF019D9991DD010100FFFFFFFFFFFFFF FF80A1A2A2B98080007F7F7F7F7F7F7F FE0111A9B929010101FFFFFFFFFFFFFF FF80B3ABAAAB8080007F7F7F7F7F7F7F FE01A92939B9010101FFFFFFFFFFFFFF 1D3D7D7D7D7D01FFFFE7C7C7C7FFFFFF FDF5F5F585FD01FFFF9F9FFFFFFFFFFF FF809BB38AB38080007F7F7F7F7F7F7F FE01A12121B9010101FFFFFFFFFFFFFF 878381808080807F7F7F7F7F7F7F7FFF E1C18101010101FFFFFFFFFFFFFFFFFF FF80B1AAB3A28080007F7F7F7F7F7F7F FE0119A1A999010101FFFFFFFFFFFFFF 808898BE9888807F7F7F7F7F7F7F7FFF 80888CBE8C88807F7F7F7F7F7F7F7FFF FF80B1B2ABB28080007F7F7F7F7F7F7F FE0119A1A199010101FFFFFFFFFFFFFF FF8099B28BB28080007F7F7F7F7F7F7F FE0129A9B991010101FFFFFFFFFFFFFF FF809AB28AB28080007F7F7F7F7F7F7F FE01DD5991DD010101FFFFFFFFFFFFFF 2161FDFD612101FFFFFFFFFFFFFFFFFF FD9D9DFDFDFD01FFFFF7F7F787FFFFFF FF80B9BAAAA98080007F7F7F7F7F7F7F FE0129A9B911010101FFFFFFFFFFFFFF 0000000000000000FFFFFFFFFFFFFFFF 0018181818001800FFE7E7E7E7FFE7FF 006C6C2400000000FF9393DBFFFFFFFF 00247E24247E2400FFDB81DBDB81DBFF 00083E380E3E0800FFF7C1C7F1C1F7FF 0062640810264600FF9D9BF7EFD9B9FF 001C34386E643A00FFE3CBC7919BC5FF 0018183000000000FFE7E7CFFFFFFFFF 000C183030180C00FFF3E7CFCFE7F3FF 0030180C0C183000FFCFE7F3F3E7CFFF 000024187E182400FFFFDBE781E7DBFF 000018187E181800FFFFE7E781E7E7FF 0000000018183000FFFFFFFFE7E7CFFF 000000007E000000FFFFFFFF81FFFFFF 0000000000181800FFFFFFFFFFE7E7FF 00060C1830604000FFF9F3E7CF9FBFFF 003C666E76663C00FFC399918999C3FF 0018381818187E00FFE7C7E7E7E781FF 003C660C18307E00FFC399F3E7CF81FF 003C660C06663C00FFC399F3F999C3FF 0066667E06060600FF999981F9F9F9FF 007E607C06067C00FF819F83F9F983FF 001C307C66663C00FFE3CF839999C3FF 007E060C18303000FF81F9F3E7CFCFFF 003C663C66663C00FFC399C39999C3FF 003C663E06663C00FFC399C1F999C3FF 0000001800180000FFFFFFE7FFE7FFFF 0000001800183000FFFFFFE7FFE7CFFF 00000C1830180C00FFFFF3E7CFE7F3FF 0000007E007E0000FFFFFF81FF81FFFF 000030180C183000FFFFCFE7F3E7CFFF 003C660C18001800FFC399F3E7FFE7FF 003C666E6E603C00FFC39991919FC3FF 00183C667E666600FFE7C399819999FF 007C667C66667C00FF839983999983FF 003C666060663C00FFC3999F9F99C3FF 00786C66666C7800FF879399999387FF 007E607860607E00FF819F879F9F81FF 007E607860606000FF819F879F9F9FFF 003C606E66663C00FFC39F919999C3FF 0066667E66666600FF999981999999FF 003C181818183C00FFC3E7E7E7E7C3FF 001E060606663C00FFE1F9F9F999C3FF 00666C78786C6600FF999387879399FF 0060606060607E00FF9F9F9F9F9F81FF 0042667E7E666600FFBD9981819999FF 0066767E6E666600FF998981919999FF 003C666666663C00FFC399999999C3FF 007C667C60606000FF8399839F9F9FFF 003C66666A6C3E00FFC399999593C1FF 007C667C786C6600FF839983879399FF 003E603C06067C00FFC19FC3F9F983FF 007E181818181800FF81E7E7E7E7E7FF 0066666666663C00FF9999999999C3FF 00666666663C1800FF99999999C3E7FF 0066667E7E664200FF9999818199BDFF 00663C183C666600FF99C3E7C39999FF 0066663C18181800FF9999C3E7E7E7FF 007E0C1830607E00FF81F3E7CF9F81FF 003C303030303C00FFC3CFCFCFCFC3FF 006030180C060200FF9FCFE7F3F9FDFF 003C0C0C0C0C3C00FFC3F3F3F3F3C3FF 00183C6600000000FFE7C399FFFFFFFF 0000000000007E00FFFFFFFFFFFF81FF FF017D6545457D0100FFFFDFFFFFFFFF FF79B5FDE5DD857900FFCFFFBFBFFFFF FF2111EBC7AF110900DFEFF5F9F1FFFF FF016D4501456D0100FFFFFFFFFFFFFF FF3179793131FDFD00FFFFFFFFFFFF03 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 FF82878E9CB8B080007F7D7B777F7F7F FF80B6BE9CBEB680007F7F6363637F7F 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 FF0000000000000000FFFFFFFFFFFFFF FF80808080808080007F7F7F7F7F7F7F FF0179FDFDFD490100FFFFB7FFFFFFFF FF0155555555550100FFBFFFFBEFFFFF FF017D7D7D757D0100FFFFD7FFDFFFFF FF395555D5FD7D3900FFEFEFAF87C7FF FF80BAA2B2A28080007F7F7F7F7F7F7F FF019D9991DD010100FFFFFFFFFFFFFF FF80BAA2B2A38080007F7F7F7F7F7F7F FF0159555951010100FFFFFFFFFFFFFF FF80B3AAB3A28080007F7F7F7F7F7F7F FF0121A121A1010100FFFFFFFFFFFFFF 01011925251901FFFFFFFFFFFFFFFFFF 01011909091D01FFFFFFFFFFFFFFFFFF FFC1A2948894A2C1007F7F7F7F7F7F7F FF8345291129458300FFFFFFFFFFFFFF 0000000000000000FFFFFFFFFFFFFFFF 808080808080C0FF7F7F7F7F7F7FBFFF 01010101010103FFFFFFFFFFFFFFFFFF 808080C0FFFFFFFF7F7F7FBFFFFFFFFF 01010103FFFFFFFFFFFFFFFFFFFFFFFF 0101017D391103FFFFFFFFFFFFFFFFFF 808080808080807F7F7F7F7F7F7F7FFF FDCDCDFDFD7D01FFFFB7B787B7FFFFFF 01010101010101FFFFFFFFFFFFFFFFFF 0101296D290101FFFFFFFFFFFFFFFFFF 01113901391101FFFFFFFFFFFFFFFFFF 01010D05213101FFFFFFFFFFFFFFFFFF FF0111397DF9F16100FFFFEFC7EFFFFF FFFFFFE0E0000000FF81BFA0E0000000 F8F8F8E0E0E0E0E0F888B8A0A0A0A0E0 3C3CFFFFFFFF3C3C2424E70000E72424 #3:MAIN BG 00001440010405040504050405040504 05040504050401040504050401040504 05040504050405040504010402041000 10001000100010001000100010000704 1000100007047E047F04540455044604 47040404020410001000100010001000 10001000100007041000100007045604 2E045604640456045704040402041000 10001000100010001000100010000704 1100110007047E047F044E044F044804 49040404020410001000100010001000 10001000100007041100110007045604 2F0456045B0456045804040402041000 10001000100010001000100010000704 1200120007047E047F044E044F044A04 4B040404020410001000100010001000 10001000100007041200120007045604 3E0456045C0456045904040402041000 10001000100010001000100010000704 1300130007047E047F045E045F044C04 4D040404020410001000100010001000 10001000100007041300130007045604 3F0456045D0456045A04040405040604 06040604060406040604060406040504 06040604050406040604060406040604 060405046E046F040000000000000000 00000000000000000000000000000000 00000004000400044004410456046D04 00000000000000000000000000000000 00000000000000000000000400040004 500451046E046F040000000000000000 00000000000000000000000000000000 00000004000400046004610456047D04 00000000000000000000000000000000 00000000000000000000000400040004 70047104660467040304030403040304 03040304030403040304030403040304 42044304420444044204450456046504 01040104010401040104010401040104 01040104010401045204530462046304 62046304010405040504050405040504 05040504010401040504050401040504 05040504050405040504010402041100 12001300010411011201130104040204 10001000070401041504010415040104 15040404020401040104010401040104 01040104040402041000100007040104 15040104150401041504040402041102 12021302010411031203130304040204 11001100070401041504010415040104 15040404020401040104010401040104 01040104040402041100110007040104 15040104150401041504040402040104 01040104010401040104010404040204 12001200070403040304030403040304 03040404020401040104010401040104 01040104040402041200120007040104 01040104010401040104040402044004 41040104010401046004610404040204 13001300070401040104010401040104 01040404020450045104010401040104 70047104040402041300130007040104 01040104010401040104040405040604 06040604060406040604060405040504 06040604050406040604060406040604 060405046E046F040000000400040004 00040000000000000000000000000000 00000000000000044004410456046D04 00000000000000000000000000000000 00000000000000000000000000000004 500451046E046F040000000000000000 00000000000000000000000000000000 00000000000000046004610456047D04 00000000000000000000000000000000 00000000000000000000000000000004 70047104660467040304030403040304 03040304030403040304030403040304 42044304420444044204450456046504 01040104010401040104010401040104 01040104010401046204630452045304 62046304000400040004000400040004 00040004000400040004000400040004 00040004070476047704040400040004 00040004000400040004000400040004 00040004000400040004000407045604 7C040404000000000000000000000000 00000000000000000000000400040004 00040000040406040604010400000000 00000000000000000000000000000000 00000000000000000000000407041004 10040404000000000000000000000000 00000000000000000000000000000000 00000004070410041004040400000000 00000000000000000000000000000000 00000000000000000000000407041104 11040404000000000000000000000000 00000000000000000000000000000000 00000004070411041104040400000000 00000000000000000000000000000000 00000000000000000000000407041204 12040404000000000000000000000000 00000000000000000000000000000000 00000004070412041204040400000000 00000000000000000000000000000000 00000000000000000000000407041304 13040404000000000000000000000000 00000000000000000000000000000000 00000004070413041304040400000000 00000000000000000000000000000000 00000000000000000000000056040604 06040504000000000000000000000000 00000000000000000000000000000000 00000000010401040104010400000000 00000000000000000000000000000000 00000000000000000000000001040104 01040104000000000000000000000000 00000000000000000000000000000000 00000000010401040104010400000000 00000000000000000000000000000000 00000000000000000000000001040104 01040104000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000 #4:DISK BG 00001420010501050105010501050105 01050105010501050105010501050105 01050105010501050105010506040604 06040604060406040604060406040604 06040604060406040604060406040604 06040604010401040104010401040104 01040104010401040104010401040104 01040104010401044004410401040104 01040104010401040104010401040104 01040104010401040104010401040104 50045104010401040104010401040104 01040104010401040104010401040104 01040104010401040404010401040104 01040104010401040104010401040104 01040104010401040104010401040104 04040104010401040104010401040104 01040104010401040104010401040104 01040104010401040404010401040104 01040104010401040104010401040104 01040104010401040104010401040104 04040104010401040104010401040104 01040104010401040104010401040104 01040104010401040404010401040104 01040104010401040104010401040104 01040104010401040104010401040104 04040104010401040104010401040104 01040104010401040104010401040104 01040104010401040404010401040104 01040104010401040104010401040104 01040104010401040104010401040104 60046104010401040104010401040104 01040104010401040104010401040104 01040104010401047004710406040604 06040604060406040604060406040604 06040604060406040604060406040604 06040604760477040304030403040304 0304030403040304720473040E040F04 6A046B04680469047804790456047C04 01040104010401040104010401040104 7404650456043D0456046C0474046504 75046504760477040304030403040304 03040304030403040304030403040304 6A046B04680469047804790456047C04 01040104010401040104010401040104 010401040104010456046C0474046504 75046504760477040304030403040304 0304030403040304030403047A047B04 6A046B04680469047804790456047C04 01040104010401040104010401040104 0104010456046D0456046C0474046504 75046504000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000 #5:SIZE BG 00001410010701070107010701070107 01070107010701070107010701070107 01070107010701070107010703070307 03070307030703070307030703070307 03070307030703070307030703070307 03070307010701070107010701070107 01070107010701070107010701070107 01070107010701070107010701070507 05070507050705070107050705070507 05070507010701070507050705070507 05070107020701070107010701070107 07070107010701070107010704070207 01070107010701070107040702070107 01070107010701070707010701070107 01070107040702070107010701070107 01070407020701070107010701070107 07070107010701070107010704070207 01070107010701070107040702070107 01070107010701070707010701070107 01070107040702070107010701070107 01070407020701070107010701070107 07070107010701070107010704070207 01070107010701070107040702070107 01070107010701070707010701070107 01070107040702070107010701070107 01070407010703070307030703070307 01070307030703070307030701070107 03070307030703070307010701070107 01070107010701070107010701070107 01070107010701070107010701070107 01070107010701040104010401040104 01040104010401040104010401040104 01040104010401040104010705040504 05040504050405040504050405040504 05040504050405040504050405040504 05040504080403040304030403040304 03040304030409040804030403040304 03040304030403040304090404040104 01040104010401040104010401040204 04040104010401040104010401040104 01040204 #10:DEFAULT PALETTES 053F2F0000383400003C0C00003F3C00 003F2A15003F2A15003F2A15003F2A15 #11: FONTS 00000000000000000000000000000000 00181818180018000000000000000000 006C6C24000000000000000000000000 00247E24247E24000000000000000000 00083E380E3E08000000000000000000 00626408102646000000000000000000 001C34386E643A000000000000000000 00181830000000000000000000000000 000C183030180C000000000000000000 0030180C0C1830000000000000000000 000024187E1824000000000000000000 000018187E1818000000000000000000 00000000181830000000000000000000 000000007E0000000000000000000000 00000000001818000000000000000000 00060C18306040000000000000000000 003C666E76663C000000000000000000 0018381818187E000000000000000000 003C660C18307E000000000000000000 003C660C06663C000000000000000000 0066667E060606000000000000000000 007E607C06067C000000000000000000 001C307C66663C000000000000000000 007E060C183030000000000000000000 003C663C66663C000000000000000000 003C663E06663C000000000000000000 00000018001800000000000000000000 00000018001830000000000000000000 00000C1830180C000000000000000000 0000007E007E00000000000000000000 000030180C1830000000000000000000 003C660C180018000000000000000000 003C666E6E603C000000000000000000 00183C667E6666000000000000000000 007C667C66667C000000000000000000 003C666060663C000000000000000000 00786C66666C78000000000000000000 007E607860607E000000000000000000 007E6078606060000000000000000000 003C606E66663C000000000000000000 0066667E666666000000000000000000 003C181818183C000000000000000000 001E060606663C000000000000000000 00666C78786C66000000000000000000 0060606060607E000000000000000000 0042667E7E6666000000000000000000 0066767E6E6666000000000000000000 003C666666663C000000000000000000 007C667C606060000000000000000000 003C66666A6C3E000000000000000000 007C667C786C66000000000000000000 003E603C06067C000000000000000000 007E1818181818000000000000000000 0066666666663C000000000000000000 00666666663C18000000000000000000 0066667E7E6642000000000000000000 00663C183C6666000000000000000000 0066663C181818000000000000000000 007E0C1830607E000000000000000000 003C303030303C000000000000000000 006030180C0602000000000000000000 003C0C0C0C0C3C000000000000000000 00183C66000000000000000000000000 0000000000007E000000000000000000 0000000000000000FFFFFFFFFFFFFFFF 0018181818001800FFE7E7E7E7FFE7FF 006C6C2400000000FF9393DBFFFFFFFF 00247E24247E2400FFDB81DBDB81DBFF 00083E380E3E0800FFF7C1C7F1C1F7FF 0062640810264600FF9D9BF7EFD9B9FF 001C34386E643A00FFE3CBC7919BC5FF 0018183000000000FFE7E7CFFFFFFFFF 000C183030180C00FFF3E7CFCFE7F3FF 0030180C0C183000FFCFE7F3F3E7CFFF 000024187E182400FFFFDBE781E7DBFF 000018187E181800FFFFE7E781E7E7FF 0000000018183000FFFFFFFFE7E7CFFF 000000007E000000FFFFFFFF81FFFFFF 0000000000181800FFFFFFFFFFE7E7FF 00060C1830604000FFF9F3E7CF9FBFFF 003C666E76663C00FFC399918999C3FF 0018381818187E00FFE7C7E7E7E781FF 003C660C18307E00FFC399F3E7CF81FF 003C660C06663C00FFC399F3F999C3FF 0066667E06060600FF999981F9F9F9FF 007E607C06067C00FF819F83F9F983FF 001C307C66663C00FFE3CF839999C3FF 007E060C18303000FF81F9F3E7CFCFFF 003C663C66663C00FFC399C39999C3FF 003C663E06663C00FFC399C1F999C3FF 0000001800180000FFFFFFE7FFE7FFFF 0000001800183000FFFFFFE7FFE7CFFF 00000C1830180C00FFFFF3E7CFE7F3FF 0000007E007E0000FFFFFF81FF81FFFF 000030180C183000FFFFCFE7F3E7CFFF 003C660C18001800FFC399F3E7FFE7FF 003C666E6E603C00FFC39991919FC3FF 00183C667E666600FFE7C399819999FF 007C667C66667C00FF839983999983FF 003C666060663C00FFC3999F9F99C3FF 00786C66666C7800FF879399999387FF 007E607860607E00FF819F879F9F81FF 007E607860606000FF819F879F9F9FFF 003C606E66663C00FFC39F919999C3FF 0066667E66666600FF999981999999FF 003C181818183C00FFC3E7E7E7E7C3FF 001E060606663C00FFE1F9F9F999C3FF 00666C78786C6600FF999387879399FF 0060606060607E00FF9F9F9F9F9F81FF 0042667E7E666600FFBD9981819999FF 0066767E6E666600FF998981919999FF 003C666666663C00FFC399999999C3FF 007C667C60606000FF8399839F9F9FFF 003C66666A6C3E00FFC399999593C1FF 007C667C786C6600FF839983879399FF 003E603C06067C00FFC19FC3F9F983FF 007E181818181800FF81E7E7E7E7E7FF 0066666666663C00FF9999999999C3FF 00666666663C1800FF99999999C3E7FF 0066667E7E664200FF9999818199BDFF 00663C183C666600FF99C3E7C39999FF 0066663C18181800FF9999C3E7E7E7FF 007E0C1830607E00FF81F3E7CF9F81FF 003C303030303C00FFC3CFCFCFCFC3FF 006030180C060200FF9FCFE7F3F9FDFF 003C0C0C0C0C3C00FFC3F3F3F3F3C3FF 00183C6600000000FFE7C399FFFFFFFF 0000000000007E00FFFFFFFFFFFF81FF 00000000000000000000000000000000 183C3C3C3C183C181824242424182418 6CFEFE7E240000006C92925A24000000 247EFF7E7EFF7E24245A815A5A815A24 083E7F7E3F7F3E080836414631413608 62F7FE7C3E7FEF4662959A742E59A946 1C3E7E7EFFFE7F3A1C224A46919A453A 183C3C78300000001824244830000000 0C1E3C78783C1E0C0C1224484824120C 30783C1E1E3C78303048241212244830 00247E7EFF7E7E2400245A6681665A24 00183C7EFF7E3C180018246681662418 000000183C3C78300000001824244830 0000007EFF7E00000000007E817E0000 00000000183C3C180000000018242418 060F1E3C78F0E040060912244890A040 3C7EFFFFFFFF7E3C3C4299918999423C 183C7C3C3C7EFF7E182444242466817E 3C7EFF7E3C7EFF7E3C429972244E817E 3C7EFF7E6FFF7E3C3C4299726999423C 66FFFFFF7F0F0F066699998179090906 7EFFFEFE7F7FFE7C7E819E827979827C 1C3E7CFEFFFF7E3C1C224C829999423C 7EFF7F1E3C7878307E81791224484830 3C7EFF7EFFFF7E3C3C4299429999423C 3C7EFF7F7FFF7E3C3C4299417999423C 0000183C183C18000000182418241800 0000183C183C78300000182418244830 000C1E3C783C1E0C000C12244824120C 00007EFF7EFF7E0000007E817E817E00 0030783C1E3C78300030482412244830 3C7EFF7E3C183C183C42997224182418 3C7EFFFFFFFE7E3C3C429991919E423C 183C7EFFFFFFFF661824429981999966 7CFEFFFEFFFFFE7C7C8299829999827C 3C7EFFF6F6FF7E3C3C4299969699423C 78FCFEFFFFFEFC787884929999928478 7EFFFEFCF8FEFF7E7E819E84989E817E 7EFFFEFCF8F0F0607E819E8498909060 3C7EFEFFFFFF7E3C3C429E919999423C 66FFFFFFFFFFFF666699998199999966 3C7E3C3C3C3C7E3C3C4224242424423C 1E3F1F0F6FFF7E3C1E2119096999423C 66FFFEFCFCFEFF666699928484929966 60F0F0F0F0FEFF7E60909090909E817E 42E7FFFFFFFFFF6642A5998181999966 66FFFFFFFFFFFF666699898191999966 3C7EFFFFFFFF7E3C3C4299999999423C 7CFEFFFEFCF0F0607C8299829C909060 3C7EFFFFFFFE7F3E3C4299999592413E 7CFEFFFEFCFEFF667C82998284929966 3E7FFE7E3F7FFE7C3E419E423979827C 7EFF7E3C3C3C3C187E81662424242418 66FFFFFFFFFF7E3C669999999999423C 66FFFFFFFF7E3C186699999999422418 66FFFFFFFFFFE742669999818199A542 66FF7E3C7EFFFF666699422442999966 66FFFF7E3C3C3C186699994224242418 7EFF7E3C78FEFF7E7E817224489E817E 3C7E7C78787C7E3C3C424C48484C423C 60F0783C1E0F07026090482412090502 3C7E3E1E1E3E7E3C3C4232121232423C 183C7EFF660000001824429966000000 00000000007EFF7E00000000007E817E 00000000000000000000000000000000 00181C1C1C0C180C00000404040C000C 006C7E36120000000000121212000000 00247E3F367E3F120000001B12001B12 00083E3F1E3F1F040000000710011704 0062753A142E57230000113204081123 001C3E3A7E773A1D00000A021013001D 00181C3C180000000000040C18000000 000C1E3C38180C060000060C08000006 0030180C0E1E3C180000000002060C18 0000241A7E3F2C120000000200270812 0000181C7E3F1C0C000000040027040C 00000000181C3C180000000000040C18 000000007E3F000000000000003F0000 0000000000181C0C000000000000040C 00060F1E3C787020000003060C183020 003C7E7F777F3F1E000018110119031E 00183C1C1C1C7E3F000004040404003F 003C7E3F1E3C7E3F00001833060C003F 003C7E3F06673F1E000018330001031E 0066777F3F0707030000110139010103 007E7F7C3E077F3E00001F003801033E 001C3E7C7E773F1E00000E001811031E 007E3F0F1E3C381800003903060C0818 003C7E3F7E773F1E000018031811031E 003C7E3F1F673F1E000018011901031E 000000180C180C00000000000C000C00 000000180C183C18000000000C000C18 00000C1E3C180C06000000060C000006 0000007E3F7E3F00000000003F003F00 000030180C1E3C180000000000060C18 003C7E3F1E0C180C00001833060C000C 003C7E7F7F773C1E000018111117001E 00183C7E7F7F77330000001801191133 007C7E7F7E777F3E000018031811033E 003C7E7370763F1E000018131010031E 00787C76777F7E3C000010101113063C 007E7F787C707E3F00001F001C10003F 007E7F787C70703000001F001C101030 003C7E7E77773F1E00001E101111031E 0066777F7F7777330000110119111133 003C1E1C1C1C3C1E000006040404001E 001E0F0707673F1E000009010101031E 00667F7E7C7C76330000130604101033 0060707070707E3F000010101010003F 0042677F7F7F77330000010101191133 0066777F7F7777330000010111111133 003C7E7777773F1E000018111111031E 007C7E7F7E707030000018031E101030 003C7E777B7D3E1F000018111111001F 007C7E7F7E7C76330000180306101033 003E7F3C1E077F3E00001F001801033E 007E3F1C1C1C1C0C000027040404040C 0066777777773F1E000011111111031E 00667777773F1E0C000011111103060C 0066777F7F7F73210000110101193121 00663F1E3C7E77330000030600181133 0066773F1E1C1C0C000011030604040C 007E3F1E3C787E3F000033060C18003F 003C3E3838383C1E00000E080808001E 006030180C0603010000000000000101 003C1E0E0E0E3E1E000012020202021E 00183C7E330000000000001833000000 0000000000007E3F000000000000003F ================================================ FILE: programs/LowRes Adventure 1.1.nx ================================================ 'TITLE: LOWRES ADVENTURE 'AUTHOR: TIMO KLOSS SOUND 0,0,0,0 SOUND 1,3,0,0 STARTADV: HEALTH=10 NAME$="" KEY=0 CLS PRINT PRINT PRINT PRINT " -======**======-" PRINT " LOWRES ADVENTURE" PRINT " -======**======-" GOSUB TITLESONG CLS GOSUB STATUS PRINT "YOU ARRIVE AT THE" PRINT "KING'S CASTLE." PRINT "KING: FINALLY YOU" PRINT "ARE HERE TO RESCUE" PRINT "OUR COUNTRY! THE" PRINT "GHOSTS FROM THE" PRINT "SHADOW LAND CAME AND"; PRINT "STOLE MY CROWN WHEN" PRINT "I WAS SLEEPING!" PRINT "THEY SAY YOU ARE THE"; PRINT "ONLY ONE WHO CAN" PRINT "HELP US. WHAT IS" PRINT "YOUR NAME?" REPEAT INPUT ">";NAME$ UNTIL LEN(NAME$)>0 PRINT PRINT "KING: SO, "+NAME$+"," PRINT "I HAVE CHOSEN YOU TO"; PRINT "BRING BACK MY CROWN."; PRINT "BUT BE CAREFUL, THE" PRINT "SHADOW LAND IS" PRINT "DANGEROUS!" PRINT "ARE YOU READY?" PRINT "1. YES!" PRINT "2. NO, THANKS" DO INPUT ">";A IF A=1 THEN GOTO CITY IF A=2 THEN GOTO CASTLENO LOOP CASTLENO: PRINT PRINT "KING: WELL OK, I" PRINT "GUESS IT'S FINE, I" PRINT "DON'T REALLY NEED MY"; PRINT "CROWN." GOTO GAMEOVER CITY: PRINT PRINT "YOU ARE IN THE" PRINT "KING'S CITY. FROM" PRINT "HERE YOU HAVE" PRINT "SEVERAL WAYS." PRINT "1. GO TO SHADOW LAND"; PRINT "2. GO TO RIVER" DO INPUT ">";A IF A=1 THEN GOTO SHADOW IF A=2 THEN GOTO RIVER LOOP SHADOW: PRINT PRINT "A DARK AND SCARY" PRINT "PATH LEADS YOU TO A" PRINT "GIANT DOOR OF A" PRINT "BLACK MOUNTAIN." PRINT "1. OPEN DOOR" PRINT "2. RETURN TO CITY" DO INPUT ">";A IF A=1 THEN IF KEY THEN GOTO SHADOWOPEN ELSE GOTO SHADOWLOCKED END IF IF A=2 THEN GOTO CITY LOOP SHADOWLOCKED: PRINT PRINT "THE DOOR IS LOCKED," PRINT "YOU NEED A KEY." PRINT "YOU DECIDE TO RETURN"; PRINT "TO THE CITY." INPUT "> PRESS ENTER";A GOTO CITY SHADOWOPEN: PRINT PRINT "YOU OPEN THE DOOR" PRINT "WITH YOUR KEY AND" PRINT "ENTER A GIANT CAVE." PRINT "AT THE END OF THE" PRINT "CAVE YOU SEE A GHOST"; PRINT "WITH THE CROWN OF" PRINT "YOUR KING ON HIS" PRINT "HEAD." PRINT "1. ATTACK GHOST" PRINT "2. TALK WITH GHOST" PRINT "3. RETURN TO CITY" DO INPUT ">";A IF A=1 THEN GOTO GHOSTATTACK IF A=2 THEN GOTO GHOSTTALK IF A=3 THEN GOTO CITY LOOP GHOSTATTACK: PRINT PRINT "THE FINAL FIGHT WITH"; PRINT "THE GHOST OF THE" PRINT "SHADOW LAND BEGINS." ENEMYHEALTH=5 GOSUB FIGHT PRINT IF ENEMYHEALTH=0 THEN GOTO GHOSTDEFEATED ELSE PRINT "YOU RUN AWAY FROM" PRINT "THE MOUNTAIN." INPUT "> PRESS ENTER";A GOTO CITY END IF GHOSTDEFEATED: PRINT PRINT "GHOST: OKAY, OKAY," PRINT "STOP PLEASE!" PRINT "WHAT DO YOU WANT?" PRINT "YOU: THE KING'S" PRINT "CROWN!" PRINT "GHOST: AH, WHY" PRINT "DIDN'T YOU TELL IT" PRINT "BEFORE?! I DON'T" PRINT "LIKE IT ANYWAY." PRINT "HERE, TAKE IT!" INPUT "> PRESS ENTER";A PRINT PRINT "YOU: OH, THANKS!" PRINT "SORRY FOR THAT" PRINT "LITTLE FIGHT. I" PRINT "DIDN'T WANT TO" PRINT "OFFEND YOU." PRINT "GHOST: DON'T WORRY." PRINT "SEE YOU SOON." INPUT "> PRESS ENTER";A GOTO GAMEEND GHOSTTALK: PRINT PRINT "GHOST: HELLO "+NAME$+"," PRINT "HOW ARE YOU? I SEE" PRINT "YOU FOUND THE KEY TO"; PRINT "MY MOUNTAIN, NOT" PRINT "BAD. WHAT DID YOU" PRINT "COME FOR?" PRINT "1. TO DEFEAT YOU!" PRINT "2. TO ASK FOR THE" PRINT " CROWN" PRINT "3. NOTHING, BYE!" DO INPUT ">";A IF A=1 THEN GOTO GHOSTATTACK IF A=2 THEN GOTO GHOSTCROWN IF A=3 THEN GOTO CITY LOOP GHOSTCROWN: PRINT PRINT "GHOST: YEAH, YOU" PRINT "KNOW, I FOUND IT" PRINT "SOMEWHERE ON THE" PRINT "STREET. IT ACTUALLY" PRINT "DOESN'T FIT ME VERY" PRINT "WELL. YOU CAN HAVE" PRINT "IT, NO PROBLEM." PRINT "YOU: OH COOL," PRINT "THANKS!" INPUT "> PRESS ENTER";A GOTO GAMEEND RIVER: PRINT PRINT "YOU ARE AT THE" PRINT "RIVER. THERE IS A" PRINT "BIG WATERFALL." PRINT "1. GO FISHING" PRINT "2. GO BEHIND" PRINT " WATERFALL" PRINT "3. RETURN TO CITY" DO INPUT ">";A IF A=1 THEN GOTO FISHING IF A=2 THEN GOTO WATERFALL IF A=3 THEN GOTO CITY LOOP FISHING: PRINT R=RND IF R<0.1 THEN PRINT "YOU FISH AN OLD" PRINT "SHOE. JUST IN CASE" PRINT "YOU LOOK INSIDE AND" PRINT "YOU FIND A SPECIAL" PRINT "HEALING POTION!" HEALTH=HEALTH+4 ELSE IF R<0.6 THEN PRINT "YOU JUMP INTO THE" PRINT "WATER AND GRAB A" PRINT "FISH WITH YOUR" PRINT "HANDS. IT LOOKS" PRINT "TASTY AND YOU EAT" PRINT "IT." HEALTH=HEALTH+1 ELSE PRINT "YOU TRY TO CATCH A" PRINT "FISH BUT IT BITES" PRINT "YOU IN THE HAND AND" PRINT "YOU FALL" PRINT "UNFAVORABLY." HEALTH=HEALTH-2 END IF GOSUB STATUS INPUT "> PRESS ENTER";A GOTO RIVER WATERFALL: PRINT PRINT "BEHIND THE WATERFALL"; PRINT "YOU FIND THE" PRINT "ENTRANCE TO A CAVE." PRINT "FROM INSIDE YOU CAN" PRINT "HEAR SOME STRANGE" PRINT "SOUNDS." PRINT "1. ENTER CAVE" PRINT "2. RETURN TO RIVER" DO INPUT ">";A IF A=1 THEN GOTO CAVE IF A=2 THEN GOTO RIVER LOOP CAVE: PRINT PRINT "A CAVE MONSTER" PRINT "ATTACKS YOU!" ENEMYHEALTH=3 GOSUB FIGHT PRINT IF ENEMYHEALTH=0 THEN PRINT "THE MONSTER FEELS" PRINT "QUITE BAD AND" PRINT "GOES TO SLEEP. YOU" PRINT "FIND A KEY IN THE" PRINT "CAVE AND TAKE IT." KEY=-1 ELSE PRINT "YOU RUN AWAY FROM" PRINT "THE CAVE." END IF INPUT "> PRESS ENTER";A GOTO RIVER GAMEEND: PRINT PRINT "YOU RETURN TO THE" PRINT "KING'S CASTLE AND" PRINT "GIVE THE CROWN BACK" PRINT "TO HIM." PRINT "KING: WELL DONE," PRINT NAME$+"!" PRINT "NOW PEACE AND" PRINT "HARMONY WILL RETURN" PRINT "TO OUR LAND." PRINT "YOU: YEAH, SURE." PRINT PRINT "YOU GO HOME AND" PRINT "WATCH TV." PRINT PRINT "THE END" GOSUB TITLESONG GOTO RESTART FIGHT: DO PRINT IF RND<0.5 THEN PRINT "THE ENEMY HITS YOU!" PLAY 1,32,5 HEALTH=HEALTH-1 ELSE PRINT "YOU AVOID THE" PRINT "ENEMY'S ATTACK." PLAY 0,32,0 WAIT 5 PLAY 0,39,5 END IF GOSUB STATUS PRINT "> (A)TTACK OR" PRINT " (F)LEE?" DO I$=INKEY$ IF I$="A" THEN GOTO ATTACK IF I$="F" THEN RETURN WAIT VBL LOOP ATTACK: PRINT IF RND<0.6 THEN PRINT "YOU HIT THE ENEMY!" PLAY 1,39,5 ENEMYHEALTH=ENEMYHEALTH-1 IF ENEMYHEALTH=0 THEN RETURN ELSE PRINT "THE ENEMY AVOIDS" PRINT "YOUR ATTACK." PLAY 0,37,0 WAIT 5 PLAY 0,30,5 END IF PRINT "-- ENEMY'S H. "+STR$(ENEMYHEALTH)+" --" LOOP RETURN STATUS: IF HEALTH<0 THEN HEALTH=0 PRINT "-- HEALTH "+STR$(HEALTH)+" --" IF HEALTH=0 THEN RETURN GAMEOVER RETURN GAMEOVER: PRINT PRINT "GAME OVER" GOSUB GAMEOVERSONG RESTART: PRINT "> RESTART? (Y/N)" DO I$=INKEY$ IF I$="Y" THEN GOTO STARTADV IF I$="N" THEN END WAIT VBL LOOP TITLESONG: PLAY 0,37,0 WAIT 12 PLAY 0,25 WAIT 12 PLAY 0,41 WAIT 12 PLAY 0,39 WAIT 12 PLAY 0,27 WAIT 12 PLAY 0,42 WAIT 12 PLAY 0,41 WAIT 12 PLAY 0,29 WAIT 12 PLAY 0,44 WAIT 12 PLAY 0,42 WAIT 12 PLAY 0,39 WAIT 12 PLAY 0,41 WAIT 12 PLAY 0,37 WAIT 24 PLAY 0,25 WAIT 24 STOP RETURN GAMEOVERSONG: PLAY 0,44,0 WAIT 12 PLAY 0,43 WAIT 12 PLAY 0,42 WAIT 12 PLAY 0,41 WAIT 12 PLAY 0,40 WAIT 24 STOP RETURN ================================================ FILE: programs/LowRes Galaxy 2 (1.5).nx ================================================ 'TITLE: LOWRES GALAXY 2 'AUTHOR: TIMO KLOSS RANDOMIZE TIMER ' SPRITES ' 0 PLAYER SHIP ' 1 JET ' 2-9 PLAYER BULLETS ' 10-25 ALIENS ' 26-29 EXPLOSIONS ' 30-39 ALIEN BULLETS 'POINTS FOR ALIENS DIM GLOBAL POINTS(4) DATA 0,10,20,400,600 FOR I=0 TO 4 READ POINTS(I) NEXT I 'ALIENS DIM GLOBAL ALIENS(15,5) ' 0 TYPE (0=DISABLED) ' 1 TICK ' 2 START Y ' 3 HITS ' 4 AMPLITUDE ' 5 SPEED 'ALIEN BULLETS DIM GLOBAL ABULLETS(9,3) ' 0 X ' 1 Y ' 2 X VECTOR ' 3 Y VECTOR 'EXPLOSION TICKS DIM GLOBAL EXPLOSIONS(3) GLOBAL HIGHSCORE GLOBAL TICK,BGTICK GLOBAL LEVEL,SCORE,LIVES GLOBAL PX,PY GLOBAL SHIELD,HIDE,PEACE GLOBAL SHDELAY,HEAT,BULLET GLOBAL EXPLOSION GLOBAL ALIEN,ABULLET GLOBAL MSGTIMER FONT 128 CALL DRAWGAMEBG ON RASTER CALL RASTERFX GAMEPAD 1 'READ FROM PERSISTENT RAM HIGHSCORE=PEEKL($E000) TITLE: TICK=0 SPRITE OFF CALL DRAWTITLE SOUND SOURCE ROM(15) MUSIC 0 REPEAT INC BGTICK IF TICK MOD 240=0 THEN CALL SHOWMSG("PRESS ANY BUTTON") ELSE IF TICK MOD 240=120 THEN CALL SHOWMSG("HIGHSCORE: "+STR$(HIGHSCORE)) END IF INC TICK WAIT VBL UNTIL BUTTON TAP(0) STOP GAME: 'INIT PLAYER SPRITES SPRITE 0 PAL 4 SIZE 1 SPRITE 1 PAL 5 FOR I=2 TO 9 SPRITE I PAL 5 NEXT I 'INIT VARIABLES LIVES=5 SCORE=0 LEVEL=0 PX=32 PY=48 SHIELD=120 HIDE=0 PEACE=0 SHDELAY=0 HEAT=0 TICK=0 BULLET=0 ALIEN=0 FOR I=0 TO 15 ALIENS(I,0)=0 NEXT I FOR I=0 TO 9 ABULLETS(I,0)=-32 NEXT I CALL CLEAROVERLAYS CALL DRAWHUD CALL RESETSOUND SOUND SOURCE ROM(15) MUSIC 8 SOUND SOURCE ROM(14) 'GAME LOOP DO 'NEXT LEVEL? IF LIVES>0 AND TICK MOD 1800=0 THEN INC LEVEL CALL SHOWMSG("LEVEL "+STR$(LEVEL)) IF LEVEL>=2 THEN CALL ADDSCORE(LEVEL*100) TRACK 0,3 END IF END IF IF PEACE>0 THEN 'DO NOT SPAWN ALIENS DEC PEACE ELSE 'SPAWN SMALL ALIEN? M=480/(LEVEL+3) IF TICK MOD M=0 THEN CALL SPAWNALIEN(1+RND(1)) END IF 'SPAWN BIG ALIEN? M=5400/(LEVEL+2) IF TICK MOD M=M\2 THEN CALL SPAWNALIEN(3+RND(1)) END IF END IF CALL UPDALIENS CALL UPDBULLETS CALL UPDALIENBULLETS IF LIVES>0 THEN CALL UPDPLAYER ELSE 'GAME OVER IF MSGTIMER>0 THEN DEC MSGTIMER ELSE IF BUTTON TAP(0) THEN GOTO TITLE END IF END IF CALL UPDEXPLOSIONS CALL UPDMSG INC TICK INC BGTICK WAIT VBL LOOP SUB ADDSCORE(P) ADD SCORE,P CALL DRAWHUD END SUB SUB UPDPLAYER IF HIDE>0 THEN DEC HIDE SPRITE OFF 0 TO 1 EXIT SUB END IF 'PLAYER CONTROL IF UP(0) AND PY>0 THEN DEC PY IF DOWN(0) AND PY<112 THEN INC PY IF LEFT(0) AND PX>8 THEN DEC PX IF RIGHT(0) AND PX<128 THEN INC PX 'PLAYER SPRITE IF SHIELD>0 AND SHIELD MOD 4<2 THEN SPRITE OFF 0 TO 1 ELSE SPRITE 0,PX,PY,1 SPRITE 1,PX-8,PY+5,19+INT((TICK MOD 16)/8) END IF 'SHOOT? SHDELAY=SHDELAY-1 IF BUTTON(0) THEN IF SHDELAY<=0 THEN SPRITE BULLET+2,PX+8,PY+11,3 BULLET=(BULLET+1) MOD 8 PLAY 2,50+RND*2 SOUND 0 HEAT=HEAT+1 IF HEAT>=5 THEN SHDELAY=30 HEAT=0 ELSE SHDELAY=8 END IF END IF ELSE HEAT=0 END IF IF SHIELD>0 THEN DEC SHIELD ELSE 'PLAYER HIT BY BULLET? IF SPRITE HIT(0,30 TO 39) THEN SPRITE OFF HIT ABULLETS(HIT-30,0)=-32 CALL LOSESHIP END IF 'COLLISION WITH ALIEN? IF SPRITE HIT(0,10 TO 25) THEN CALL LOSESHIP END IF END IF END SUB SUB LOSESHIP DEC LIVES CALL DRAWHUD SPRITE OFF 0 TO 1 PLAY 2,30 SOUND 4 CALL EXPLODE(PX,PY,0) CALL EXPLODE(PX-8+RND*16,PY-8+RND*16,10) CALL EXPLODE(PX-8+RND*16,PY-8+RND*16,30) IF LIVES=0 THEN CALL DRAWGAMEOVER IF SCORE>HIGHSCORE THEN HIGHSCORE=SCORE CALL SHOWMSG("NEW HIGHSCORE!") 'WRITE TO PERSISTENT RAM POKEL $E000,HIGHSCORE ELSE CALL SHOWMSG("HIGHSCORE: "+STR$(HIGHSCORE)) END IF SOUND SOURCE ROM(15) MUSIC 32 SOUND SOURCE ROM(14) MSGTIMER=100 ELSE HIDE=120 SHIELD=120 PEACE=180 END IF END SUB SUB UPDBULLETS FOR I=2 TO 9 IF SPRITE.X(I)>=0 THEN SPRITE I,SPRITE.X(I)+3,, IF SPRITE.X(I)>160 THEN SPRITE OFF I END IF NEXT I END SUB SUB SPAWNALIEN(TYPE) I=ALIEN IF ALIENS(I,0)>0 THEN EXIT SUB ALIENS(I,0)=TYPE ALIENS(I,1)=0 ALIENS(I,2)=16+RND*80 IF TYPE=1 THEN ALIENS(I,3)=1 ALIENS(I,4)=RND*12 ALIENS(I,5)=0.5 ELSE IF TYPE=2 THEN ALIENS(I,3)=1 ALIENS(I,4)=RND*20 ALIENS(I,5)=0.35 ELSE IF TYPE=3 THEN ALIENS(I,3)=4 ALIENS(I,4)=RND*32 ALIENS(I,5)=0.25 ELSE IF TYPE=4 THEN ALIENS(I,3)=8 ALIENS(I,4)=RND*40 ALIENS(I,5)=0.15 END IF ALIEN=(ALIEN+1) MOD 16 END SUB SUB UPDALIENS FOR I=0 TO 15 IF ALIENS(I,0)>0 THEN CALL UPDALIEN(I) END IF NEXT I END SUB SUB UPDALIEN(I) TYPE=ALIENS(I,0) N=10+I ALIENS(I,1)=ALIENS(I,1)+1 T=ALIENS(I,1) X=160-T*ALIENS(I,5) X=X-SIN(T*0.03)*24 'STILL ON SCREEN? IF X>-32 THEN 'UPDATE SPRITE Y=ALIENS(I,2) AY=ALIENS(I,4) IF TYPE MOD 2=0 THEN P=6 ELSE P=7 IF TYPE<=2 THEN S=0 C=10+INT((T MOD 24)/12) Y=Y+SIN(T/20)*AY ELSE S=1 C=6+INT((T MOD 32)/16)*2 Y=Y+SIN(T/60)*AY END IF SPRITE N PAL P SIZE S SPRITE N,X,Y,C 'HIT BY BULLET? IF SPRITE HIT(N,2 TO 9) THEN SPRITE OFF HIT SPRITE N PAL 3 ALIENS(I,3)=ALIENS(I,3)-1 IF ALIENS(I,3)=0 THEN CALL ADDSCORE(POINTS(TYPE)) SPRITE OFF N ALIENS(I,0)=0 IF S=0 THEN CALL EXPLODE(X-4,Y-4,0) ELSE CALL EXPLODE(X,Y,0) END IF IF TYPE>=3 THEN PLAY 3,40 SOUND 5 ELSE PLAY 3,45 SOUND 2 END IF END IF 'SHOOT? IF PEACE=0 AND X>50 AND T MOD 120=30 THEN CALL ALIENSHOOT(X+4,Y+4) END IF ELSE 'OUT OF SCREEN, RESET SPRITE OFF N ALIENS(I,0)=0 END IF END SUB SUB ALIENSHOOT(X,Y) I=ABULLET IF ABULLETS(I,0)>-32 THEN EXIT SUB N=I+30 PLAY 3,45+RND*2 SOUND 1 SPRITE N,X,Y,4 SPRITE N PAL 3 ABULLETS(I,0)=X ABULLETS(I,1)=Y U=PX+6-X V=PY+6-Y W=SQR(U*U+V*V) ABULLETS(I,2)=U/W ABULLETS(I,3)=V/W ABULLET=(ABULLET+1) MOD 3 END SUB SUB UPDALIENBULLETS FOR I=0 TO 9 N=I+30 X=ABULLETS(I,0) Y=ABULLETS(I,1) X=X+ABULLETS(I,2) Y=Y+ABULLETS(I,3) IF X>=-8 AND X<160 AND Y>=-8 AND Y<128 THEN ABULLETS(I,0)=X ABULLETS(I,1)=Y SPRITE N,X,Y, ELSE ABULLETS(I,0)=-32 SPRITE OFF N END IF NEXT I END SUB SUB EXPLODE(X,Y,DELAY) N=EXPLOSION+40 SPRITE N PAL 5 SIZE 0 SPRITE N,X,Y,0 EXPLOSIONS(EXPLOSION)=20+DELAY EXPLOSION=(EXPLOSION+1) MOD 4 END SUB SUB UPDEXPLOSIONS FOR I=0 TO 3 T=EXPLOSIONS(I) IF T>0 THEN IF T<=20 THEN N=I+40 SPRITE N SIZE 1 SPRITE N,,,32+((20-T)\5)*2 IF T=20 THEN PLAY 3,25+RND*5 SOUND 3 END IF DEC T IF T=0 THEN SPRITE OFF N EXPLOSIONS(I)=T END IF NEXT I END SUB SUB DRAWHUD BG 0 PAL 4 PRIO 1 BG FILL 0,0 TO 4,0 CHAR 0 FOR I=0 TO LIVES-1 CELL I,0,5 NEXT I PAL 0 NUMBER 15,0,SCORE,5 END SUB SUB DRAWGAMEBG BG SOURCE ROM(3) BG 0 BG COPY 0,0,32,16 TO 0,0 BG 1 BG COPY 0,16,32,16 TO 0,0 END SUB SUB DRAWTITLE BG 0 BG FILL 0,0 TO 19,6 CHAR 0 BG SOURCE ROM(4) BG COPY 0,0,20,6 TO 4,1 END SUB SUB DRAWGAMEOVER BG 0 BG SOURCE ROM(4) BG COPY 0,7,8,2 TO 1,5 BG COPY 9,7,8,2 TO 11,5 END SUB SUB CLEAROVERLAYS BG 0 BG FILL 0,0 TO 19,6 CHAR 0 BG 1 BG FILL 0,11 TO 19,11 CHAR 0 END SUB SUB SHOWMSG(MSG$) BG 1 PAL 0 PRIO 1 L=LEN(MSG$) BG FILL 0,11 TO 19,11 CHAR 0 TEXT (20-L)/2,11,MSG$ MSGTIMER=120 END SUB SUB UPDMSG IF MSGTIMER>0 THEN DEC MSGTIMER IF MSGTIMER=0 THEN BG 1 BG FILL 0,11 TO 19,11 CHAR 0 END IF END IF END SUB SUB RESETSOUND STOP FOR I=0 TO 3 VOLUME I,15,%11 NEXT I END SUB SUB RASTERFX 'STARS AND FOREGROUND HILLS IF RASTER=0 THEN SCROLL 1,BGTICK/6,0 ELSE IF RASTER=88 THEN SCROLL 1,0,0 ELSE IF RASTER=96 THEN SCROLL 1,BGTICK,0 END IF 'STATUS BAR AND PLANET SURFACE IF RASTER=0 THEN SCROLL 0,0,0 ELSE IF RASTER=58 THEN SCROLL 0,BGTICK*0.5,0 ELSE IF RASTER=80 THEN SCROLL 0,BGTICK*5/8,0 ELSE IF RASTER=96 THEN SCROLL 0,BGTICK*6/8,0 ELSE IF RASTER=112 THEN SCROLL 0,BGTICK*7/8,0 END IF END SUB #1:MAIN PALETTES 003F1B0500261101003B2612003F3E39 001F2A15003C3006003F3121003F1612 #2:MAIN CHARACTERS 00000000000000000000000000000000 000040600078FF54000040607078FFFF 000000000000FC02000000000000FCFE 04E733040000000006F83C0600000000 60F0F060000000006090906000000000 406070821E764040406070FEF0766040 00041030400C8E0E00071F2F7F73F5F5 002008040260E1E000E0F8FCFE9E5F5F 041028400C8E0E06071F377F73F5F5F9 2008040260E1E0C0E0F8FCFE9E5F5F3F 0042816600006642007EFFBDFFFF6642 42816666000066247EFFBDBDFF7E6624 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 A8FF007802604000FFFF7C787C604000 7FBDFA7C00000000FFC3867C00000000 100409A30904100010060EBC0E061000 40120413041240004012071C07124000 00000000000000000000000000000000 040080403C383018FBFFFF7F3C383018 400001023C1C0C18BFFFFFFE3C1C0C18 800040203C1C0E02FF7F7F3F3C1C0E02 010002043C387040FFFEFEFC3C387040 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 000001070F1F1F3F0000010608101020 000080E0F0F8F8FC0000806010080804 071F3F7F7FFFFFFF0610204000808000 E0F8FCFEFEFFFFFF6008040200010100 071F3F7C78F1E2E404102043078F1E1C E0F81C02780102002008E4FEFE030301 020816285020A000030F1E387060E0C0 4010280400000000C0F0380400000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 3F1F1F0F070100002010100806010000 FCF8F8F0E08000000408081060800000 FFFFFF7F7F3F1F070080800040201006 FFFFFFFEFEFCF8E00001010002040860 C0C8C86868301A043838B81858281607 00000000000000000000000000000000 0080204020100000C0C0606020100000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00400000000002000000000000000200 00000000000000000000020000000000 000008002A0008000000080836080800 1F604080868C8C8C001F3F7F7E7C7C7C 6018180C0C000000E0F8F8FCFC000000 1F604080868C8C8C001F3F7F7F7C7C7C 6018180C0C0C8C8CE0F8F8FCFCFC7C7C EC8C8C8C8C8C8C8C1C7C7C7C7C7C7C7C 00000000000000000000000000000000 EC8C8C8C4C4700001C7C7C7C3C3E3F1F EC8C8C8C981830601C7C7C7C78F8F0E0 00000000000000000000000000000000 00000000000000000000000000000000 FF80800003232323007F7FFF3F1F1F1F FE0000000323232301FFFFFF3F1F1F1F C0C0C0C000000000C0C0C0C000000000 00000000000000000000100000000000 00000000000004000000000000000400 00483030480000000078484878000000 8C8C8C86030000017C7C7C7EFC7F7F1F EC8C8C8C0C0C0C0C1C7C7C7CFCFCFCFC 878080808C8C8C0C787F7F7F7C7C7CFC 8C0C0C0C8C8C8C8C7CFCFCFC7C7C7C7C 8C8C8C8C878080007C7C7C7C787F7FFF 00000000EC0C0C1C000000001CFCFCFC 2046468C8C8C8C0C1F3F3F7C7C7C7CFC 3018180C0C8C8C0CF0F8F8FCFC7C7CFC 0808080808080800070707070707070F C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0 23232323E18080001F1F1F1F1E7F7FFF 23232323E00000001F1F1F1F1FFFFFFF 00000000C0C0C0C000000000C0C0C0C0 0000000000000000FFFFFFFFFFFFFFFF 00007EC3C37E0000FFFFFFFCFC81FFFF 00000000000000000000000000000000 000000000000082200000000001876DF 00000000000000000000000000000000 B0988C878280808070787C7E7D7F7F7F 2C4C8C0C0C0C0C0C1C3C7CFCFCFCFCFC FF8080808C8C8680007F7F7F7C7C797F EC0C0C0C0000C0C01CFCFCFC0000C0C0 8C8C8C07020000007C7C7CFE7D3F1F0F 8C8C8C0C183060C07C7C7CFCF8F0E0C0 FF8080808C8C8C8D007F7F7F7D7C7C7C 6018180C0C0C0C0CE0F8F8FCFCFCFCFC 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 0000000038443800FFFFFFFFFFFBC7FF 00001C0E00000000FFFFFFF1FFFFFFFF 000001040102164C0103060B1E3D69B3 0000000000000000FFFFFFFFFFFFFFFF 00804060B010280480C0E0F0F8FCFEFF 898C8C8C8C8C8C0C7F7C7C7C7C7C7CFC 8C8C8C8C8C8C8C0C7C7C7C7C7C7C7CFC 80808C8C878080007F7F7C7C787F7FFF C0C00000EC0C0C0CC0C000001CFCFCFC 8C8C8C87020000007C7C7C7EFD7F7F1F 8C8C8C0C0C1818607C7C7CFCFCF8F8E0 868080808C8C8C0C797F7F7F7F7D7CFC 18186030180C0C0CF8F8E0F0F8FCFCFC 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00181818180018000000000000000000 006C6C24000000000000000000000000 00247E24247E24000000000000000000 00083E380E3E08000000000000000000 00626408102646000000000000000000 001C34386E643A000000000000000000 00181830000000000000000000000000 000C183030180C000000000000000000 0030180C0C1830000000000000000000 000024187E1824000000000000000000 000018187E1818000000000000000000 00000000181830000000000000000000 000000007E0000000000000000000000 00000000001818000000000000000000 00060C18306040000000000000000000 003C666E76663C000000000000000000 0018381818187E000000000000000000 003C660C18307E000000000000000000 003C660C06663C000000000000000000 0066667E060606000000000000000000 007E607C06067C000000000000000000 001C307C66663C000000000000000000 007E060C183030000000000000000000 003C663C66663C000000000000000000 003C663E06663C000000000000000000 00000018001800000000000000000000 00000018001830000000000000000000 00000C1830180C000000000000000000 0000007E007E00000000000000000000 000030180C1830000000000000000000 003C660C180018000000000000000000 003C666E6E603C000000000000000000 00183C667E6666000000000000000000 007C667C66667C000000000000000000 003C666060663C000000000000000000 00786C66666C78000000000000000000 007E607860607E000000000000000000 007E6078606060000000000000000000 003C606E66663C000000000000000000 0066667E666666000000000000000000 003C181818183C000000000000000000 001E060606663C000000000000000000 00666C78786C66000000000000000000 0060606060607E000000000000000000 0042667E7E6666000000000000000000 0066767E6E6666000000000000000000 003C666666663C000000000000000000 007C667C606060000000000000000000 003C66666A6C3E000000000000000000 007C667C786C66000000000000000000 003E603C06067C000000000000000000 007E1818181818000000000000000000 0066666666663C000000000000000000 00666666663C18000000000000000000 0066667E7E6642000000000000000000 00663C183C6666000000000000000000 0066663C181818000000000000000000 007E0C1830607E000000000000000000 003C303030303C000000000000000000 006030180C0602000000000000000000 003C0C0C0C0C3C000000000000000000 00183C66000000000000000000000000 0000000000007E000000000000000000 #3:MAIN BG 00002020000000000000002100210021 00210000002100210021000000000000 00000021002100210021002100000021 00210000000000210021002100210021 00210021000000000000002100210000 00210000002100210021000000000000 00000021002100210000002100000021 00000021000000000021002100210021 00210021002100000000000000210021 00210021002100210021000000210000 00210021002100000021002100000021 00210021002100000000002100000021 00000021000000210000000000000000 00000000002100210021000000000021 00000021000000000021002100000000 00000000000000000021002100210000 00210021000000000000002100210021 00210021000000210021002100000021 00210021000000210021002100000021 00210021002100000021002100210000 00000000000000000000002100210021 00210000002100210021002100210021 00210000000000210021002100000021 00210021002100000000002100210021 00000000002100210021000000000021 00210021000000000021002100210021 00000000002100210021002100000021 00210021002100210000002100210021 00210021002100210021002100210021 00210021002100216201630164010021 00210021000000210021002100210021 00006201630164010021002100210021 00210001000100016201630164010001 62010001640163017201730174010001 00016201630164010021002100010001 63017201730174016301002100216201 63016401630163017201730174016301 63016301720173017301730173017401 63017201730174016301630163017201 73017301730173017301740163017201 73017401600160016001600160016001 60016001710160017301730173016001 60016001710160016001600160016001 71016001600160016001600160016001 60016001700160016001710170017101 70016001710160017001600170017101 70016001600171016001600160017101 60016001710160016001710160016001 71016001600160016001710160016001 60016001600160016001600160016001 60017001610160016001710170016001 60016001700160016001600160016001 60016001700171016001600160017001 60016101700171016001600170016001 60016001700160017101600160016001 61016001600170016001710170017101 70017101600160017001600160016001 60017001600160016001610160017001 60016101600160016001700160017101 60016001600161016001700160016001 60016101700160016001610160017001 60016001600170016001600160017101 70017101600171017001710170016001 70017101700160016001600161016001 70017101400041000000000000000000 51000000000000005000000000000000 50000000000000000000000000000000 00000000000000000000500000000000 40004100500051000000400041000000 00000000000000000000510000000000 00005100000000004000410000000000 00004000410000000000000000000000 50004200000051000000500051000000 50000000420041000000000040004100 00000000510000005000510000000000 00005000510000000000400041000000 00000000000000000000000051000000 51000000500051005000000050005100 00005100000000000000000000000000 00000000000000005100500051000000 51000000000040004100000000000000 00000000000000000000000000000000 00004200000041000000000000005100 40004100000000005201000000000000 00000000000050005100500000004000 50000000000040004100410000000000 00000000510000000000500000000000 50005100000000000000400041000000 00000000000000005000000000005000 51000000000050005100000000004000 50000000500000005100000000005000 00000000000051000000500051000000 00000000000000005000410000000000 00004000410000000000000000005000 51000000000000000000000051000000 00005100000050000000000000004000 41000000500000005000420000005100 62010001640100005201510000005100 50005100000040004100000000004200 00000000000000000000510000005000 51000000510000000000000062016201 00010001000100005100000000000000 00000000000050005100000051000000 50000000000000005100000051000000 00000000400062016201620100010001 00010001500000000000000062220002 64225100000051000000000000000000 00000000400041000000000000000000 00000000500000010001000100010001 41000000000000010001000100020002 64020001000100010001000100010001 00010001000100010001000100010001 00010000000000000000000000000001 00210020002000220002002200226322 00220002000000000000000100010001 00010001000000000000000100010001 00010000000000006221632264220020 00000002000262020002002272227322 74220022000200016222632264220000 00000000000000000000000062226322 64010000000000027222732274220020 00210022000200226322722270227322 73227422002200027222732274226322 63226402000062026322632272227322 74220022002272227322712273227422 00220002002272227322732273226122 73227122742272227322732273227322 73227422002072227322732271227322 73227422 #4:OVERLAY BG 00001410000000000000AC20AF20B720 B220A520B32000000000000000000000 00000000000000000000000043204420 45204620472048204520462049204A20 49204A20000000000000000000000000 00000000532054205520562057205820 5520562059205A205B205C2000000000 00000000000000000000000000000000 00000000000200020002000200000000 00000000000000000000000000000000 00000000000000000000000200024D22 4E224F22000000000000000000000000 00000000000000000000000000000000 0000000000025D225E225F2200000000 00000000000000000000000000000000 00000000000000000000000000020000 00000000000000000000000000000000 00000000000000000000000043254425 45254625652566256725682580254525 462547254725672568256B256C250000 00000000532554255525562575257625 77257825002579257A2569256A257725 78257B257C2500000000000000000005 00050000000000000000000000000000 00000005000500250025000500050005 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000 #14:SFX 5801505019FC00005801506019FB0000 4801004F19FF00007806007F01FE0000 780A04AF19FD00007805027F19FD0000 0800505012FF00000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 296FE6306F00356F002A6F00316F0036 6F002C6F00336F00386F00FF00000000 E0000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 #15:MAIN SOUND 230030AA0004A0002800606019000000 22006060000250002800303019FE0000 38002020000000003800606000000000 21004068120B30002400418A10372000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 800103180002041A000103180002041A 000116180002171A000116180082171A 85064040050B4040090A4040090C4040 05064040050B40400D0E40400D0F4040 11104040111040401314404013144040 13154040139540404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 87884040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 193F00000000193F00000000315F0019 3F00000000193F00000000554500193F 00000000315F00000000000000000000 193F00000000193F00000000315F0019 3F00000000193F00000000000000193F 00554500315F00000000554500554800 1D0F0000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 1B0F0000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 190F0000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 1B0F0000000000000000000000000000 00000000000000000000000000000000 00000000180F00000000000000000000 356F00000000356F00000000386F0000 0000356F00000000000000000000386F 00000000FF0000000000356F00000000 3C6F000000000000000000003A6F0000 0000386F000000000000000000003A6F 00000000000000FF0000000000000000 3D6F000000000000000000003C6F0000 00003A6F00000000FF0000000000356F 00000000000000000000FF0000000000 386F00000000386F000000003A6F0000 0000386F00000000000000000000376F 00000000000000000000FF0000000000 1B2FE81B2F000000001B2F001C2F0000 00001B2F001B2F00000000202F001B2F 001B2F001E2F001B2F001C2F001B2F00 1B2F001B2F000000001B2F001C2F0000 00001B2F001B2F00000000202F001B2F 001B2F001E2F001B2F001C2F001B2F00 337800000000000000FF000000000000 00002E7800000000000000FF00000000 00000000337800000000000000000000 31780000000030780000000000000000 00002E7800000000000000FF00000000 000000002C7800000000000000000000 190F0000000000000000000000000000 0000000000000000FF00000000000000 00000000000000000000000000000000 190F0000000000000000000000000000 0000000000000000FF00000000000000 00000000000000000000000000000000 4278003F78003A78003678003378002E 78002A7800277800FF00000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 1E2F001E2F001E2F00000000192F0000 0000192F001E2F000000001F2F000000 001F2F000000001F2F000000001F2F00 1E2F001E2F001E2F00000000192F0000 0000192F001E2F000000001F2F000000 001F2F000000001F2F001A2F001C2F00 36780000000000000037780000000000 00003678000000000000003778000000 00000000367800000000FF0000000000 2A78000000000000002B780000000000 00002C78000000000000002D78000000 000000002E78000000002F7800000000 2E7800000000000000FF000000000000 00002A7800000000000000FF00000000 00000000257800000000000000000000 277800000000000000000000FF000000 00000000002C78000000000000002D78 00000000317800000000FF0000000000 36780000000000000037780000000000 00003678000000000000003778000000 00000000367800000000FF0000000000 2A78000000000000002B780000000000 00003078000000000000002F78000000 000000002E78000000002D7800000000 1E2F00000000192F000000001E2F0000 0000192F001C2F000000001C2F001B2F 00000000192F00000000172F00000000 152F00152F00000000172F00172F0000 0000192F00192F000000001C2F001C2F 000000001B2F00000000192F00000000 36780000000000000038780000000000 0000397800000000000000FF00000000 00000000000000000000000000000000 34780000000000000033780000000000 00003178000000000000002F78000000 00000000317800000000FF0000000000 36780000000000000038780000000000 0000397800000000000000000000FF00 00000000000000000000000000000000 2D78000000000000002F780000000000 00003178000000000000003478000000 00000000317800000000FF0000000000 33780000000000000000000000000000 0000000000000000000000FF00000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 1B2F001B2F000000001B2F001C2F0000 00001B2F001B2F00000000202F001B2F 000000001C2F000000001C2F00000000 1B2F001B2F00000000162F00162F0000 0000172F00172F00000000122F00122F 00000000122F00000000122F00000000 1B2F001B2F000000000000001B2F0000 00000000000000001B2F000000000000 000000001B2F00000000000000000000 122F00122F000000000000000F2F0000 00000000000000001B2F000F2F000000 000000001B2F000F2F00000000000000 1B0FD000000000000000000000000000 0000000000000000120F000000000000 00000000000000000000000000000000 190F0000000000000000000000000000 0000000000000000140F000000000000 00000000000000000000FF0000000000 2A7800000000000000000000FF000000 00000000000000002E78000000000000 00000000FF0000000000000000000000 2C780000000000000000000025780000 00000000000000002778000000000000 00000000FF0000000000000000000000 2E7800000000000000000000FF000000 00000000000000003178000000000000 00000000FF0000000000000000000000 3678000000000000000000003A780000 00000000000000003378000000000000 00000000FF0000000000000000000000 416F00000000356F000000003C6F0000 0000416F00000000000000FF00000000 00000000000000000000000000000000 436F00000000376F00000000446F0000 0000466F00000000000000FF00000000 00000000000000000000000000000000 3D6F00000000316F00000000FF000000 00003D6F00000000316F00000000FF00 00000000000000000000000000000000 436F00000000436F00000000446F0000 0000436F000000003F6F000000000000 00FF0000000000000000000000000000 2918012C1A02291C01291C022C1C0129 1C02291A012C18020000000000002918 012C1A02291C01291A022C1801000000 271801301A02271C01271C02301C0127 1C02271A013018020000000000002718 01301A02271C01271A02301801000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 251801291A02251C01251C02291C0125 1C02291A012518020000000000002518 01291A02251C01251A02291801000000 271801271A022C1C01271C022C1C012C 1C02271A012C18020000000000002718 012C1A02271C01271A022C1801000000 ================================================ FILE: programs/Sound Composer 1.5.nx ================================================ 'TITLE: SOUND COMPOSER 'AUTHOR: TIMO KLOSS 'ENERGY SAVING MODE SYSTEM 0,1 GLOBAL NUMINST,INSTSIZE NUMINST=16 INSTSIZE=8 GLOBAL NUMPATT,PATTSIZE NUMPATT=64 PATTSIZE=4 GLOBAL NUMTRAC,NUMROWS,ROWSIZE,TRACSIZE NUMTRAC=64 NUMROWS=32 ROWSIZE=3 TRACSIZE=NUMROWS*ROWSIZE GLOBAL SONGSIZE SONGSIZE=NUMINST*INSTSIZE + NUMPATT*PATTSIZE + NUMTRAC*TRACSIZE '==== MEMORY MAP ==== GLOBAL AINST,APATT,ATRAC AINST=$A000 APATT=AINST+NUMINST*INSTSIZE ATRAC=APATT+NUMPATT*PATTSIZE ACOPYINST=ATRAC+NUMTRAC*TRACSIZE ACOPYPATT=ACOPYINST+INSTSIZE ACOPYTRAC=ACOPYPATT+PATTSIZE IF ACOPYTRAC+TRACSIZE>$E000 THEN END '====================== DIM GLOBAL MENU$(6) 'EDIT FOR DIFFERENT KEYBOARD LAYOUTS GLOBAL KEYS$, VALKEYS$ KEYS$="ZSXDCVGBHNJMQ2W3ER5T6Y7UI" VALKEYS$="0123456789ABCDEF" DIM GLOBAL NOTES$(11), HEXS$(15), SNDWAV$(3), LFOWAV$(3), FLAG$(1) DATA "C-","C#","D-","D#","E-","F-" DATA "F#","G-","G#","A-","A#","B-" FOR I=0 TO 11 READ NOTES$(I) NEXT I 'CACHE HEX VALUES 0-F FOR I=0 TO 15 HEXS$(I)=HEX$(I) NEXT I DATA "SAW","TRI","PUL","NOI" FOR I=0 TO 3 READ SNDWAV$(I) NEXT I DATA "TRI","SAW","SQR","RND" FOR I=0 TO 3 READ LFOWAV$(I) NEXT I FLAG$(0)="OFF" FLAG$(1)="ON " GLOBAL METERSENABLED, OLDX, OLDY GLOBAL CURPAT, CURVOI, CURROW, CURTRA, CURCOL, CUROCT, CURSND, CURNOTE, FOCUS, SELTAB CALL INITSTATUS SUB INITSTATUS CURPAT=0 CURVOI=0 CURROW=0 CURTRA=0 CURCOL=0 CUROCT=3 CURSND=0 CURNOTE=46 FOCUS=-1 SELTAB=0 END SUB GLOBAL SOUNDFILE,FILESOFFS CALL CLEARMUSIC 'LOAD MAIN SOUND SOUNDFILE=15 FILESOFFS=5 OK=0 R=0 UPD=0 CALL BLOAD(OK) KEYBOARD OPTIONAL '==== PLAYER ==== GLOBAL PLAYER_MODE, PLAYER_SPEED, PLAYER_TICK, PLAYER_PAT, PLAYER_ROW, PLAYER_BREAK GLOBAL PLAYER_TRA, PLAYER_VOI ON VBL CALL UPDATEPLAYER '==== TOUCH ZONES INIT ==== 'SETTINGS GLOBAL MAX_ZONE, ZONE_PAL 'STATUS GETTERS GLOBAL CUR_ZONE, ZONE_EVENT, ZONE_IN_X, ZONE_IN_Y, ZONE_RESULT 'INTERNAL GLOBAL ZONE_LAST_X, ZONE_LAST_Y GLOBAL E_DOWN, E_UP, E_OUT, E_DRAG E_DOWN=1 E_UP=2 E_OUT=3 E_DRAG=4 MAX_ZONE=35 DIM GLOBAL ZONEX(MAX_ZONE), ZONEY(MAX_ZONE), ZONEW(MAX_ZONE), ZONEH(MAX_ZONE), ZONEP(MAX_ZONE) TOUCHSCREEN '======== MAIN: IF SELTAB=0 THEN GOTO PATTERNEDITOR IF SELTAB=1 THEN GOTO TRACKEDITOR IF SELTAB=2 THEN GOTO SOUNDEDITOR END '======== PATTERNEDITOR: SELTAB=0 SPRITE OFF SPRITE 0 PAL 3 FLIP 0,0 SPRITE 1 PAL 3 FLIP 1,0 SPRITE 2 PAL 3 FLIP 0,1 SPRITE 3 PAL 3 FLIP 1,1 CALL METERSON CALL RESETZONES BG SOURCE ROM(3) BG COPY 0,0,20,2 TO 0,0 ZONE_PAL=1 CALL SETZONE(0,0,0,2,2) CALL SETZONE(1,2,0,2,2) CALL SETZONE(2,4,0,2,2) CALL SETZONE(3,6,0,2,2) CALL SETZONE(4,8,0,2,2) CALL SETZONE(5,10,0,2,2) ZONE_PAL=-1 CALL SETZONE(6,12,0,2,2) CALL SETZONE(7,14,0,2,2) CALL SETZONE(8,16,0,2,2) ZONE_PAL=1 CALL SETZONE(9,18,0,2,2) ZONE_PAL=-1 CALL SETZONE(10,0,2,2,2) CALL SETZONE(11,3,2,3,2) CALL SETZONE(12,7,2,3,2) CALL SETZONE(13,11,2,3,2) CALL SETZONE(14,15,2,3,2) ZONE_PAL=1 CALL SETZONE(15,18,2,2,2) ZONE_PAL=-1 CALL SETZONE(30,0,4,20,10) FOCUS=-1 CALL DRAWPATINFO CALL DRAWPATTERN CALL SHOWKEYBOARD CALL UPDATEPATCURSOR OLDX=-1 OLDY=-1 DRAGGED=0 DO CALL UPDATEZONES IF ZONE_EVENT=E_UP THEN IF CUR_ZONE=0 THEN CALL PLAYMUSIC(CURPAT,1) IF CUR_ZONE=1 THEN CALL PLAYMUSIC(CURPAT,2) IF CUR_ZONE=2 THEN STOP IF PLAYER_MODE>0 THEN CALL STOPPLAYER ELSE IF CURROW>0 THEN CURROW=0 CALL DRAWPATTERN ELSE CURPAT=CURPAT-1 CALL GETLOOPSTART(CURPAT) CALL DRAWPATINFO CALL DRAWPATTERN END IF END IF IF CUR_ZONE=5 THEN GOTO PATTERNMENU IF CUR_ZONE=9 THEN GOTO DISKMENU END IF IF ZONE_EVENT=E_DOWN THEN IF CUR_ZONE=3 AND CURPAT>0 THEN CURPAT=CURPAT-1 CALL CHANGEMUSIC(CURPAT) CALL DRAWPATINFO CALL DRAWPATTERN END IF IF CUR_ZONE=4 AND CURPAT-1 THEN FOCUS=-1 CALL SHOWKEYBOARD CALL UPDATEPATCURSOR END IF OLDY=ZONE_IN_Y DRAGGED=0 ELSE IF ZONE_EVENT=E_DRAG THEN IF PLAYER_MODE<>1 AND PLAYER_MODE<>2 THEN CURROW=MIN(NUMROWS-1,MAX(0,CURROW+OLDY-ZONE_IN_Y)) CALL DRAWPATTERN CALL UPDATEPATCURSOR OLDY=ZONE_IN_Y END IF DRAGGED=-1 ELSE IF ZONE_EVENT=E_UP THEN IF NOT DRAGGED THEN CURVOI=MIN(3,MAX(0,(ZONE_IN_X-3)\4)) IF PLAYER_MODE<>1 AND PLAYER_MODE<>2 THEN CURROW=MIN(NUMROWS-1,MAX(0,CURROW+ZONE_IN_Y-4)) END IF CALL DRAWPATTERN CALL UPDATEPATCURSOR END IF ELSE IF ZONE_EVENT=E_OUT THEN END IF END IF IF ZONE_EVENT=E_DOWN AND CUR_ZONE>=10 AND CUR_ZONE<=14 THEN FOCUS=CUR_ZONE IF FOCUS>=11 AND FOCUS<=14 THEN CURVOI=FOCUS-11 CALL SHOWWHEEL CALL UPDATEPATCURSOR END IF IF ZONE_EVENT=E_UP AND CUR_ZONE=15 THEN CALL TOGGLELOOP CALL DRAWLOOP END IF K$=INKEY$ IF K$=" " THEN STOP IF PLAYER_MODE>0 THEN CALL STOPPLAYER ELSE CALL PLAYMUSIC(CURPAT,2) END IF ELSE IF K$<>"" AND FOCUS=-1 THEN K=ASC(K$) IF K=17 THEN CURVOI=(CURVOI+1) MOD 4 CALL UPDATEPATCURSOR ELSE IF K=18 THEN CURVOI=(CURVOI+3) MOD 4 CALL UPDATEPATCURSOR END IF IF PLAYER_MODE<>1 AND PLAYER_MODE<>2 THEN IF K=19 THEN CURROW=(CURROW+1) MOD NUMROWS CALL DRAWPATTERN ELSE IF K=20 THEN CURROW=(CURROW+NUMROWS-1) MOD NUMROWS CALL DRAWPATTERN END IF END IF END IF VOIX=3+CURVOI*4 CALL GETTRACK(CURPAT,CURVOI,CURTRA) CALL HANDLEKEYBOARD(0,VOIX,K$,UPD) IF K$<>"" AND UPD AND PLAYER_MODE<>1 AND PLAYER_MODE<>2 THEN CURROW=(CURROW+1) MOD NUMROWS CALL DRAWPATTERN END IF IF FOCUS=10 THEN CALL HANDLEWHEEL(CURPAT,0,NUMPATT-1,K$,UPD) IF UPD THEN CALL DRAWPATINFO CALL DRAWPATTERN END IF END IF IF FOCUS>=11 AND FOCUS<=14 THEN CALL HANDLEWHEEL(CURTRA,-1,NUMTRAC-1,K$,UPD) IF UPD THEN CALL SETTRACK(CURPAT,CURVOI,CURTRA) CALL DRAWPATINFO CALL DRAWTRACK(CURTRA,VOIX) END IF END IF IF PLAYER_MODE=1 OR PLAYER_MODE=2 THEN IF PLAYER_ROW<>CURROW OR PLAYER_PAT<>CURPAT THEN P=CURPAT CURROW=PLAYER_ROW CURPAT=PLAYER_PAT IF CURPAT<>P THEN CALL DRAWPATINFO CALL DRAWPATTERN END IF END IF WAIT VBL LOOP PATTERNMENU: MENU$(0)="COPY" MENU$(1)="PASTE" MENU$(2)="INSERT EMPTY" MENU$(3)="REMOVE" MENU$(4)="CANCEL" CALL SHOWMENU("EDIT PATTERN",4,R) IF R=0 THEN COPY APATT+CURPAT*PATTSIZE,PATTSIZE TO ACOPYPATT IF R=1 THEN COPY ACOPYPATT,PATTSIZE TO APATT+CURPAT*PATTSIZE IF R=2 THEN COPY APATT+CURPAT*PATTSIZE,(NUMPATT-CURPAT-1)*PATTSIZE TO APATT+(CURPAT+1)*PATTSIZE FILL APATT+CURPAT*PATTSIZE,PATTSIZE,64 END IF IF R=3 THEN COPY APATT+(CURPAT+1)*PATTSIZE,(NUMPATT-CURPAT-1)*PATTSIZE TO APATT+CURPAT*PATTSIZE FILL APATT+(NUMPATT-1)*PATTSIZE,PATTSIZE,64 END IF GOTO PATTERNEDITOR SUB DRAWPATTERN PAL 3 BG FILL 0,4 TO 20,13 CHAR 0 CALL DRAWPOS T=-1 FOR V=0 TO 3 CALL GETTRACK(CURPAT,V,T) CALL DRAWTRACK(T,3+V*4) NEXT V END SUB SUB DRAWPATINFO PAL 0 BG FILL 0,2 TO 19,3 CHAR 1 TEXT 0,2,"PA" PAL 2 NUMBER 0,3,CURPAT,2 T=-1 FOR V=0 TO 3 CALL GETTRACK(CURPAT,V,T) X=3+V*4 PAL 0 TEXT X,2,"V" NUMBER X+1,2,V,1 TEXT X,3,"T" PAL 2 IF T<0 THEN TEXT X+1,3,"--" ELSE NUMBER X+1,3,T,2 END IF NEXT V CALL DRAWLOOP END SUB SUB DRAWLOOP V=0 CALL GETLOOP(CURPAT,0,V) FLIP 0,1 IF V=1 THEN PAL 0 ELSE PAL 2 CELL 18,2,46 CALL GETLOOP(CURPAT,1,V) FLIP 1,0 IF V=1 THEN PAL 0 ELSE PAL 2 CELL 19,2,46 CALL GETLOOP(CURPAT,2,V) FLIP 0,0 IF V=1 THEN PAL 0 ELSE PAL 2 CELL 19,3,47 END SUB SUB TOGGLELOOP V1=0 V2=0 V3=0 CALL GETLOOP(CURPAT,0,V1) CALL GETLOOP(CURPAT,1,V2) CALL GETLOOP(CURPAT,2,V3) V=V1+V2*2+V3*4 V=(V+1) MOD 5 CALL SETLOOP(CURPAT,0,V MOD 2) CALL SETLOOP(CURPAT,1,V\2 MOD 2) CALL SETLOOP(CURPAT,2,V\4 MOD 2) END SUB SUB DRAWPOS PAL 1 BG FILL 0,8 TO 19,8 CHAR 1 FOR I=0 TO 9 P=I+CURROW-4 IF P>=0 AND P=0 AND R=0 THEN CALL SETCURSOR(ZONEX(FOCUS), ZONEY(FOCUS), ZONEW(FOCUS), ZONEH(FOCUS)) ELSE CALL SETCURSOR(3+CURVOI*4,8,3,1) END IF END SUB SUB SETCURSOR(X,Y,W,H) SPRITE 0,X*8-1,Y*8-1,32 SPRITE 1,(X+W)*8-7,Y*8-1,32 SPRITE 2,X*8-1,(Y+H)*8-7,32 SPRITE 3,(X+W)*8-7,(Y+H)*8-7,32 END SUB SUB PREPARETRACK(TRA,VOIX) IF TRA=-1 THEN CALL GETFREETRACK(TRA) CALL SETTRACK(CURPAT,CURVOI,TRA) CALL DRAWPATINFO CALL DRAWTRACK(TRA,VOIX) END IF END SUB '=================================== TRACKEDITOR: SELTAB=1 SPRITE OFF CALL METERSON CALL RESETZONES BG SOURCE ROM(3) BG COPY 0,8,20,2 TO 0,0 ZONE_PAL=1 CALL SETZONE(0,0,0,2,2) CALL SETZONE(1,2,0,2,2) CALL SETZONE(2,4,0,2,2) CALL SETZONE(3,6,0,2,2) CALL SETZONE(4,8,0,2,2) ZONE_PAL=-1 CALL SETZONE(6,12,0,2,2) CALL SETZONE(7,14,0,2,2) CALL SETZONE(8,16,0,2,2) ZONE_PAL=1 CALL SETZONE(9,18,0,2,2) ZONE_PAL=-1 CALL SETZONE(10,0,2,2,2) ZONE_PAL=-1 CALL SETZONE(30,0,4,20,10) FOCUS=-1 CURCOL=0 IF CURTRA=-1 THEN CURTRA=0 CALL DRAWTRAINFO CALL DRAWFULLTRACK CALL UPDATETRACURSOR CALL UPDATETRAINPUT OLDX=-1 OLDY=-1 DRAGGED=0 DO CALL UPDATEZONES IF ZONE_EVENT=E_UP THEN IF CUR_ZONE=0 THEN CALL PLAYTRACK(CURTRA,CURVOI) IF CUR_ZONE=1 THEN STOP IF PLAYER_MODE>0 THEN CALL STOPPLAYER ELSE IF CURROW>0 THEN CURROW=0 CALL DRAWFULLTRACK END IF END IF IF CUR_ZONE=4 THEN GOTO TRACKMENU IF CUR_ZONE=9 THEN GOTO DISKMENU END IF IF ZONE_EVENT=E_DOWN THEN IF CUR_ZONE=2 AND CURTRA>0 THEN CURTRA=CURTRA-1 CALL CHANGETRACK(CURTRA) CALL DRAWTRAINFO CALL DRAWFULLTRACK END IF IF CUR_ZONE=3 AND CURTRA-1 THEN FOCUS=-1 CALL UPDATETRACURSOR END IF OLDY=ZONE_IN_Y DRAGGED=0 ELSE IF ZONE_EVENT=E_DRAG THEN IF PLAYER_MODE<>3 THEN CURROW=MIN(NUMROWS-1,MAX(0,CURROW+OLDY-ZONE_IN_Y)) CALL DRAWFULLTRACK CALL UPDATETRACURSOR OLDY=ZONE_IN_Y END IF DRAGGED=-1 ELSE IF ZONE_EVENT=E_UP THEN IF NOT DRAGGED THEN IF ZONE_IN_X<7 THEN CURCOL=0 ELSE CURCOL=MIN(4,1+(ZONE_IN_X-7)\2) END IF IF PLAYER_MODE<>3 THEN CURROW=MIN(NUMROWS-1,MAX(0,CURROW+ZONE_IN_Y-4)) END IF CALL DRAWFULLTRACK CALL UPDATETRACURSOR END IF CALL UPDATETRAINPUT END IF END IF K$=INKEY$ IF K$=" " THEN STOP IF PLAYER_MODE>0 THEN CALL STOPPLAYER ELSE CALL PLAYTRACK(CURTRA,CURVOI) END IF ELSE IF K$<>"" AND FOCUS=-1 THEN K=ASC(K$) IF K=17 THEN CURCOL=(CURCOL+1) MOD 5 CALL UPDATETRACURSOR CALL UPDATETRAINPUT ELSE IF K=18 THEN CURCOL=(CURCOL+4) MOD 5 CALL UPDATETRACURSOR CALL UPDATETRAINPUT END IF IF PLAYER_MODE<>3 THEN IF K=19 THEN CURROW=(CURROW+1) MOD NUMROWS CALL DRAWFULLTRACK ELSE IF K=20 THEN CURROW=(CURROW+NUMROWS-1) MOD NUMROWS CALL DRAWFULLTRACK END IF END IF END IF IF FOCUS=10 THEN CALL HANDLEWHEEL(CURTRA,0,NUMTRAC-1,K$,UPD) IF UPD THEN CALL DRAWTRAINFO CALL DRAWFULLTRACK END IF END IF IF CURCOL=0 THEN CALL HANDLEKEYBOARD(1,3,K$,UPD) ELSE CALL HANDLEHEXPICKER(K$,UPD) END IF IF K$<>"" AND UPD AND PLAYER_MODE<>3 THEN CURROW=(CURROW+1) MOD NUMROWS CALL DRAWFULLTRACK END IF IF PLAYER_MODE=3 THEN IF PLAYER_ROW<>CURROW OR PLAYER_TRA<>CURTRA THEN T=CURTRA CURROW=PLAYER_ROW CURTRA=PLAYER_TRA IF CURTRA<>T THEN CALL DRAWTRAINFO CALL DRAWFULLTRACK END IF END IF WAIT VBL LOOP TRACKMENU: MENU$(0)="COPY" MENU$(1)="PASTE" MENU$(2)="OCTAVE UP" MENU$(3)="OCTAVE DOWN" MENU$(4)="NOTE UP" MENU$(5)="NOTE DOWN" MENU$(6)="CANCEL" CALL SHOWMENU("EDIT TRACK",6,R) IF R=0 THEN COPY ATRAC+CURTRA*TRACSIZE,TRACSIZE TO ACOPYTRAC IF R=1 THEN COPY ACOPYTRAC,TRACSIZE TO ATRAC+CURTRA*TRACSIZE IF R=2 THEN CALL TRANSPOSE(CURTRA,+12) IF R=3 THEN CALL TRANSPOSE(CURTRA,-12) IF R=4 THEN CALL TRANSPOSE(CURTRA,+1) IF R=5 THEN CALL TRANSPOSE(CURTRA,-1) GOTO TRACKEDITOR SUB UPDATETRACURSOR IF FOCUS>=0 THEN CALL SETCURSOR(ZONEX(FOCUS), ZONEY(FOCUS), ZONEW(FOCUS), ZONEH(FOCUS)) ELSE IF CURCOL=0 THEN CALL SETCURSOR(3,8,3,1) ELSE CALL SETCURSOR(7+(CURCOL-1)*2,8,1,1) END IF END IF END SUB SUB UPDATETRAINPUT IF CURCOL=0 THEN CALL SHOWKEYBOARD ELSE CALL SHOWHEXPICKER END IF END SUB SUB DRAWTRAINFO PAL 0 BG FILL 0,2 TO 19,3 CHAR 1 TEXT 0,2,"TR" PAL 2 NUMBER 0,3,CURTRA,2 PAL 0 TEXT 3,3,"N S V C P" END SUB SUB DRAWFULLTRACK PAL 3 BG FILL 0,4 TO 20,13 CHAR 0 CALL DRAWPOS CALL DRAWTRACK(CURTRA,3) CALL DRAWTRACKPARAMS END SUB SUB DRAWTRACKPARAMS FOR I=0 TO 9 R=I+CURROW-4 IF R>=0 AND R0 AND NOTE<255 THEN TEXT 7,Y,HEXS$(SND) TEXT 9,Y,HEXS$(VOL) ELSE TEXT 7,Y,"- -" END IF IF COM>0 OR PAR>0 THEN TEXT 11,Y,HEXS$(COM) TEXT 13,Y,HEXS$(PAR) ELSE TEXT 11,Y,"- -" END IF END SUB '=================================== SOUNDEDITOR: SELTAB=2 CALL METERSOFF SPRITE OFF CALL RESETZONES BG SOURCE ROM(3) BG COPY 0,10,20,2 TO 0,0 PAL 0 BG FILL 0,2 TO 19,16 CHAR 1 BG COPY 0,12,20,2 TO 0,2 TEXT 0,2,"SND" FOR I=0 TO 15 TEXT 4+I,2,HEXS$(I) NEXT I PAL 0 TEXT 0,5,"SND:" TEXT 4,5,"WAV" TEXT 8,5,"PW" TEXT 12,5,"LEN" TEXT 0,7,"ENV:" TEXT 4,7,"ATT" TEXT 8,7,"DEC" TEXT 12,7,"SUS" TEXT 16,7,"REL" TEXT 0,9,"LFO:" TEXT 4,9,"RAT" TEXT 8,9,"FRQ" TEXT 12,9,"VOL" TEXT 16,9,"PW" TEXT 4,11,"WAV" TEXT 8,11,"INV" TEXT 12,11,"ENV" TEXT 16,11,"TRG" ZONE_PAL=1 CALL SETZONE(0,0,0,2,2) CALL SETZONE(1,2,0,2,2) CALL SETZONE(2,4,0,2,2) ZONE_PAL=-1 CALL SETZONE(6,12,0,2,2) CALL SETZONE(7,14,0,2,2) CALL SETZONE(8,16,0,2,2) ZONE_PAL=1 CALL SETZONE(9,18,0,2,2) ZONE_PAL=-1 CALL SETZONE(10,4,2,16,2) CALL SETZONE(11,4,5,3,2) CALL SETZONE(12,8,5,3,2) CALL SETZONE(13,12,5,3,2) CALL SETZONE(14,4,7,3,2) CALL SETZONE(15,8,7,3,2) CALL SETZONE(16,12,7,3,2) CALL SETZONE(17,16,7,3,2) CALL SETZONE(18,4,9,3,2) CALL SETZONE(19,8,9,3,2) CALL SETZONE(30,12,9,3,2) CALL SETZONE(31,16,9,3,2) CALL SETZONE(32,4,11,3,2) CALL SETZONE(33,8,11,3,2) CALL SETZONE(34,12,11,3,2) CALL SETZONE(35,16,11,3,2) FOCUS=-1 CALL DRAWSNDSEL CALL DRAWSNDPARAMS CALL SHOWKEYBOARD CALL UPDATESNDCURSOR OLDX=-1 DO CALL UPDATEZONES IF ZONE_EVENT=E_UP THEN IF CUR_ZONE=0 THEN STOP CALL STOPPLAYER END IF IF CUR_ZONE=2 THEN GOTO SOUNDMENU IF CUR_ZONE=9 THEN GOTO DISKMENU END IF IF ZONE_EVENT=E_DOWN THEN IF CUR_ZONE=1 THEN FOCUS=-1 CALL SHOWKEYBOARD END IF IF CUR_ZONE=6 THEN GOTO PATTERNEDITOR IF CUR_ZONE=7 THEN GOTO TRACKEDITOR END IF IF CUR_ZONE=10 AND (ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG) THEN CURSND=ZONE_IN_X CALL DRAWSNDSEL CALL DRAWSNDPARAMS END IF IF ZONE_EVENT=E_DOWN THEN IF (CUR_ZONE>=12 AND CUR_ZONE<=19) OR (CUR_ZONE>=30 AND CUR_ZONE<=31) THEN IF FOCUS<>CUR_ZONE THEN FOCUS=CUR_ZONE IF CUR_ZONE=13 THEN CALL SHOWWHEEL ELSE CALL SHOWHEXPICKER END IF END IF ELSE IF CUR_ZONE=11 OR CUR_ZONE>=32 THEN IF FOCUS>=0 THEN FOCUS=-1 CALL SHOWKEYBOARD END IF END IF CALL UPDATESNDCURSOR A=AINST+CURSND*INSTSIZE IF CUR_ZONE=11 THEN 'WAVEFORM V=(PEEK(A)\16) AND %11 POKE A,(PEEK(A) AND %11001111) OR (((V+1) MOD 4)*16) CALL DRAWSNDPARAMS END IF IF CUR_ZONE=32 THEN 'LFO WAVEFORM V=PEEK(A+4) AND %11 POKE A+4,(PEEK(A+4) AND %11111100) OR ((V+1) MOD 4) CALL DRAWSNDPARAMS END IF IF CUR_ZONE=33 THEN 'LFO INVERT POKE A+4,PEEK(A+4) XOR %00000100 CALL DRAWSNDPARAMS END IF IF CUR_ZONE=34 THEN 'LFO ENV MODE POKE A+4,PEEK(A+4) XOR %00001000 CALL DRAWSNDPARAMS END IF IF CUR_ZONE=35 THEN 'LFO TRIGGER POKE A+4,PEEK(A+4) XOR %00010000 CALL DRAWSNDPARAMS END IF END IF K$=INKEY$ IF K$=" " THEN STOP CALL STOPPLAYER END IF IF FOCUS=-1 THEN CALL HANDLEKEYBOARD(2,0,K$,UPD) ELSE IF FOCUS=13 THEN 'WHEEL: LENGTH IF CUR_ZONE=20 THEN IF ZONE_EVENT=E_DOWN THEN VOLUME CURVOI,15,%11 CALL SETSOUND(CURVOI,CURSND) PLAY CURVOI,CURNOTE ELSE IF ZONE_EVENT=E_UP OR ZONE_EVENT=E_OUT THEN STOP CURVOI END IF END IF A=AINST+CURSND*INSTSIZE V=PEEK(A+1) CALL HANDLEWHEEL(V,0,255,K$,UPD) IF UPD THEN POKE A+1,V IF V>0 THEN POKE A,PEEK(A) OR %01000000 ELSE POKE A,PEEK(A) AND %10111111 END IF CALL DRAWSNDPARAMS CALL SETSOUND(CURVOI,CURSND) PLAY CURVOI,CURNOTE END IF ELSE CALL HANDLESNDHEXPICKER(K$) END IF WAIT VBL LOOP SOUNDMENU: MENU$(0)="COPY" MENU$(1)="PASTE" MENU$(2)="CANCEL" CALL SHOWMENU("EDIT SOUND",2,R) IF R=0 THEN COPY AINST+CURSND*INSTSIZE,INSTSIZE TO ACOPYINST IF R=1 THEN COPY ACOPYINST,INSTSIZE TO AINST+CURSND*INSTSIZE GOTO SOUNDEDITOR SUB UPDATESNDCURSOR IF FOCUS>=0 THEN CALL SETCURSOR(ZONEX(FOCUS), ZONEY(FOCUS), ZONEW(FOCUS), ZONEH(FOCUS)) ELSE SPRITE OFF END IF END SUB SUB DRAWSNDSEL BG TINT 4,2 TO 19,3 PAL 0 BG TINT 4+CURSND,2 TO 4+CURSND,3 PAL 1 END SUB SUB DRAWSNDPARAMS A=AINST+CURSND*INSTSIZE PAL 2 'SND: TEXT 4,6,SNDWAV$(PEEK(A)\16 MOD 4) TEXT 8,6,HEXS$(PEEK(A) MOD 16) IF PEEK(A) AND %01000000 THEN TEXT 12,6," " TEXT 12,6,STR$(PEEK(A+1)) ELSE TEXT 12,6,"OFF" END IF 'ENV: TEXT 4,8,HEXS$(PEEK(A+2) MOD 16) TEXT 8,8,HEXS$(PEEK(A+2)\16) TEXT 12,8,HEXS$(PEEK(A+3) MOD 16) TEXT 16,8,HEXS$(PEEK(A+3)\16) 'LFO: TEXT 4,10,HEXS$(PEEK(A+5) MOD 16) TEXT 8,10,HEXS$(PEEK(A+5)\16) TEXT 12,10,HEXS$(PEEK(A+6) MOD 16) TEXT 16,10,HEXS$(PEEK(A+6)\16) 'LFO ATTR: TEXT 4,12,LFOWAV$(PEEK(A+4) MOD 4) TEXT 8,12,FLAG$(PEEK(A+4)\4 MOD 2) TEXT 12,12,FLAG$(PEEK(A+4)\8 MOD 2) TEXT 16,12,FLAG$(PEEK(A+4)\16 MOD 2) END SUB SUB HANDLESNDHEXPICKER(KEY$) KEYVAL=INSTR(VALKEYS$,KEY$)-1 IF CUR_ZONE=20 OR KEYVAL>=0 THEN IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG OR KEYVAL>=0 THEN A=AINST+CURSND*INSTSIZE IF KEYVAL>=0 THEN N=KEYVAL ELSE N=ZONE_IN_X 'SND: IF FOCUS=12 THEN POKE A,(PEEK(A) AND $F0) OR N 'ENV: IF FOCUS=14 THEN POKE A+2,(PEEK(A+2) AND $F0) OR N IF FOCUS=15 THEN POKE A+2,(PEEK(A+2) AND $0F) OR N*16 IF FOCUS=16 THEN POKE A+3,(PEEK(A+3) AND $F0) OR N IF FOCUS=17 THEN POKE A+3,(PEEK(A+3) AND $0F) OR N*16 'LFO: IF FOCUS=18 THEN POKE A+5,(PEEK(A+5) AND $F0) OR N IF FOCUS=19 THEN POKE A+5,(PEEK(A+5) AND $0F) OR N*16 IF FOCUS=30 THEN POKE A+6,(PEEK(A+6) AND $F0) OR N IF FOCUS=31 THEN POKE A+6,(PEEK(A+6) AND $0F) OR N*16 CALL DRAWSNDPARAMS VOLUME CURVOI,15,%11 CALL SETSOUND(CURVOI,CURSND) PLAY CURVOI,CURNOTE ELSE IF ZONE_EVENT=E_UP OR ZONE_EVENT=E_OUT THEN STOP CURVOI END IF END IF END SUB '==== INPUT BARS ==== SUB SHOWKEYBOARD BG COPY 0,2,20,2 TO 0,14 CALL DRAWOCTAVE CALL CLEARZONES(20,29) ZONE_PAL=-1 CALL SETZONE(20,4,14,12,2) ZONE_PAL=1 CALL SETZONE(21,0,14,2,2) CALL SETZONE(22,2,14,2,2) CALL SETZONE(23,16,14,2,2) CALL SETZONE(24,18,14,2,2) END SUB 'MODE 0: PATTERN EDITOR 'MODE 1: TRACK EDITOR 'MODE 2: SOUND EDITOR SUB HANDLEKEYBOARD(MODE,VOIX,KEY$,UPD) UPD=0 KEY=INSTR(KEYS$,KEY$) IF FOCUS=-1 THEN IF CUR_ZONE=20 OR KEY>0 THEN IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG OR KEY>0 THEN IF MODE=0 THEN CALL PREPARETRACK(CURTRA,VOIX) IF KEY>0 THEN N=CUROCT*12+KEY ELSE N=CUROCT*12+ZONE_IN_X+1 END IF IF N>=1 AND N<=95 THEN CURNOTE=N IF MODE=2 THEN VOLUME CURVOI,15,%11 CALL SETSOUND(CURVOI,CURSND) PLAY CURVOI,N ELSE IF CURTRA>=0 THEN CALL SETNOTE(CURTRA,CURROW,N) UPD=-1 CALL PLAYROW(CURTRA,CURROW,CURVOI) END IF END IF ELSE IF ZONE_EVENT=E_UP OR ZONE_EVENT=E_OUT THEN STOP CURVOI END IF END IF IF CUR_ZONE=21 AND ZONE_EVENT=E_DOWN THEN IF CUROCT>0 THEN CUROCT=CUROCT-1 CALL DRAWOCTAVE END IF END IF IF CUR_ZONE=22 AND ZONE_EVENT=E_DOWN THEN IF CUROCT<7 THEN CUROCT=CUROCT+1 CALL DRAWOCTAVE END IF END IF IF MODE<>2 THEN IF (CUR_ZONE=23 AND ZONE_EVENT=E_UP) OR KEY$=CHR$(10) THEN IF MODE=0 THEN CALL PREPARETRACK(CURTRA,VOIX) IF CURTRA>=0 THEN CALL SETNOTE(CURTRA,CURROW,255) UPD=-1 STOP CURVOI END IF END IF IF ((CUR_ZONE=24 AND ZONE_EVENT=E_UP) OR KEY$=CHR$(8)) AND CURTRA>=0 THEN CALL SETNOTE(CURTRA,CURROW,0) UPD=-1 STOP CURVOI END IF IF UPD THEN CALL DRAWNOTE(CURTRA,CURROW,VOIX,8) IF MODE=1 THEN CALL DRAWPARAM(CURTRA,CURROW,8) END IF END IF END IF END SUB SUB SHOWWHEEL BG COPY 0,4,20,2 TO 0,14 CALL CLEARZONES(20,29) ZONE_PAL=-1 CALL SETZONE(20,0,14,20,2) END SUB SUB HANDLEWHEEL(VALUE,VMIN,VMAX,KEY$,UPD) UPD=0 IF CUR_ZONE=20 THEN IF ZONE_EVENT=E_DOWN THEN OLDX=ZONE_IN_X ELSE IF ZONE_EVENT=E_DRAG THEN VALUE=MAX(VMIN,MIN(VMAX,VALUE+ZONE_IN_X-OLDX)) UPD=-1 OLDX=ZONE_IN_X END IF END IF IF KEY$<>"" THEN K=ASC(KEY$) IF K=17 THEN VALUE=MAX(VMIN,MIN(VMAX,VALUE+10)) UPD=-1 ELSE IF K=18 THEN VALUE=MAX(VMIN,MIN(VMAX,VALUE-10)) UPD=-1 ELSE IF K=19 THEN VALUE=MAX(VMIN,MIN(VMAX,VALUE-1)) UPD=-1 ELSE IF K=20 THEN VALUE=MAX(VMIN,MIN(VMAX,VALUE+1)) UPD=-1 ELSE IF KEY$>="0" AND KEY$<="9" THEN V$=STR$(MAX(0,VALUE)) IF VMAX<99 THEN L=1 ELSE L=2 V$=RIGHT$(V$,L)+KEY$ V=VAL(V$) IF V>VMAX THEN V=VAL(KEY$) VALUE=V UPD=-1 ELSE IF KEY$=CHR$(8) THEN VALUE=VMIN UPD=-1 END IF END IF END SUB SUB SHOWHEXPICKER BG COPY 0,6,20,2 TO 0,14 PAL 0 FOR I=0 TO 15 TEXT I,15,HEXS$(I) NEXT I CALL CLEARZONES(20,29) ZONE_PAL=-1 CALL SETZONE(20,0,14,16,2) END SUB SUB HANDLEHEXPICKER(KEY$,UPD) UPD=0 KEYVAL=INSTR(VALKEYS$,KEY$)-1 IF FOCUS=-1 THEN IF CUR_ZONE=20 OR KEYVAL>=0 THEN IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG OR KEYVAL>=0 THEN IF KEYVAL>=0 THEN N=KEYVAL ELSE N=ZONE_IN_X IF CURCOL=1 THEN CALL SETSND(CURTRA,CURROW,N) CALL PLAYROW(CURTRA,CURROW,CURVOI) CURSND=N END IF IF CURCOL=2 THEN CALL SETVOL(CURTRA,CURROW,N) CALL PLAYROW(CURTRA,CURROW,CURVOI) END IF IF CURCOL=3 THEN CALL SETCOM(CURTRA,CURROW,N) IF CURCOL=4 THEN CALL SETPAR(CURTRA,CURROW,N) CALL DRAWPARAM(CURTRA,CURROW,8) UPD=-1 ELSE IF ZONE_EVENT=E_UP OR ZONE_EVENT=E_OUT THEN STOP CURVOI END IF END IF END IF END SUB '=================================== DISKMENU: CALL METERSOFF SPRITE OFF CALL RESETZONES FILES CALL DRAWDISKMENU ZONE_PAL=-1 CALL SETZONE(0,0,2,18,11) ZONE_PAL=1 CALL SETZONE(1,18,2,2,2) CALL SETZONE(2,18,11,2,2) CALL SETZONE(3,0,14,2,2) CALL SETZONE(4,16,14,2,2) CALL SETZONE(5,18,14,2,2) CALL SETZONE(6,14,14,2,2) DO CALL UPDATEZONES IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN IF CUR_ZONE=0 THEN SOUNDFILE=ZONE_IN_Y+FILESOFFS CALL DRAWFILES END IF END IF IF ZONE_EVENT=E_DOWN THEN IF CUR_ZONE=1 THEN FILESOFFS=0 CALL DRAWFILES END IF IF CUR_ZONE=2 THEN FILESOFFS=5 CALL DRAWFILES END IF END IF IF ZONE_EVENT=E_UP THEN IF CUR_ZONE=3 THEN GOTO MAIN IF CUR_ZONE=4 THEN OK=0 CALL BLOAD(OK) IF OK THEN GOTO MAIN END IF IF CUR_ZONE=5 THEN OK=0 CALL BSAVE(OK) IF OK THEN GOTO MAIN END IF IF CUR_ZONE=6 THEN MENU$(0)="CLEAR ALL" MENU$(1)="KEEP SOUNDS" MENU$(2)="CANCEL" CALL SHOWMENU("CLEAR MUSIC?",2,R) CALL DRAWDISKMENU IF R=0 THEN CALL BNEW GOTO MAIN END IF IF R=1 THEN CALL BCLEARTRACKS GOTO MAIN END IF END IF END IF WAIT VBL LOOP SUB DRAWDISKMENU BG SOURCE ROM(4) BG COPY 0,0,20,16 TO 0,0 PAL 1 TEXT 0,0,"SOUND FILE " SPRITE OFF CALL DRAWFILES END SUB SUB DRAWFILES FOR I=0 TO 10 N=I+FILESOFFS IF N=SOUNDFILE THEN PAL 1 ELSE PAL 0 NUMBER 0,2+I,N,2 TEXT 2,2+I,": " TEXT 3,2+I,LEFT$(FILE$(N),17) NEXT I END SUB SUB BLOAD(OK) OK=0 IF SOUNDFILE=-1 THEN CALL SHOWMESSAGE("SELECT FILE") EXIT SUB END IF CALL CLEARMUSIC LOAD SOUNDFILE,$A000,SONGSIZE CALL INITSTATUS OK=-1 END SUB SUB BSAVE(OK) IF SOUNDFILE=-1 THEN CALL SHOWMESSAGE("SELECT FILE") EXIT SUB END IF LT=-1 HP=0 CALL GETLASTTRACK(LT) CALL HASPATTERNS(HP) F$=FILE$(SOUNDFILE) IF F$="" THEN F$="SOUND" S=NUMINST*INSTSIZE IF LT>=0 OR HP THEN S=S+NUMPATT*PATTSIZE+(LT+1)*TRACSIZE END IF SAVE SOUNDFILE,F$,$A000,S CALL DRAWFILES CALL SHOWMESSAGE("SAVED") OK=-1 END SUB SUB BNEW CALL CLEARMUSIC CALL INITSTATUS SOUNDFILE=-1 END SUB SUB BCLEARTRACKS CALL CLEARPATTERNS CALL CLEARTRACKS CALL INITSTATUS SOUNDFILE=-1 END SUB '=================================== SUB SETNOTE(TRA,ROW,N) A=ATRAC+TRA*TRACSIZE+ROW*ROWSIZE POKE A,N IF N>0 AND N<255 THEN CALL SETSND(TRA,ROW,CURSND) CALL SETVOL(TRA,ROW,15) ELSE IF N=0 THEN POKE A+1,0 END IF END SUB SUB SETSND(TRA,ROW,N) A=ATRAC+TRA*TRACSIZE+ROW*ROWSIZE NOTE=PEEK(A) IF NOTE>0 AND NOTE<255 THEN POKE A+1,(PEEK(A+1) AND $0F) OR N*16 END IF END SUB SUB SETVOL(TRA,ROW,N) A=ATRAC+TRA*TRACSIZE+ROW*ROWSIZE NOTE=PEEK(A) IF NOTE>0 AND NOTE<255 THEN POKE A+1,(PEEK(A+1) AND $F0) OR N END IF END SUB SUB SETCOM(TRA,ROW,N) A=ATRAC+TRA*TRACSIZE+ROW*ROWSIZE+2 POKE A,(PEEK(A) AND $0F) OR N*16 END SUB SUB SETPAR(TRA,ROW,N) A=ATRAC+TRA*TRACSIZE+ROW*ROWSIZE+2 POKE A,(PEEK(A) AND $F0) OR N END SUB SUB GETROW(TRA,ROW,NOTE,SND,VOL,COM,PAR) A=ATRAC+TRA*TRACSIZE+ROW*ROWSIZE NOTE=PEEK(A) SND=PEEK(A+1)\16 VOL=PEEK(A+1) AND $0F COM=PEEK(A+2)\16 PAR=PEEK(A+2) AND $0F END SUB SUB TRANSPOSE(TRA,N) FOR ROW=0 TO NUMROWS-1 A=ATRAC+TRA*TRACSIZE+ROW*ROWSIZE NOTE=PEEK(A) IF NOTE>0 AND NOTE<255 THEN TN=NOTE+N IF TN>0 AND TN<255 THEN POKE A,TN END IF NEXT ROW END SUB SUB SETTRACK(PAT,VOI,TRA) A=APATT+PAT*PATTSIZE+VOI HI=PEEK(A) AND %10000000 IF TRA>=0 THEN POKE A,(TRA OR HI) ELSE POKE A,(%01000000 OR HI) END SUB SUB GETTRACK(PAT,VOI,TRA) A=APATT+PAT*PATTSIZE+VOI TRA=(PEEK(A) AND %01111111) IF TRA=%01000000 THEN TRA=-1 END SUB SUB ISPATTERNEMPTY(PAT,EMP) EMP=-1 T=-1 FOR V=0 TO 3 CALL GETTRACK(PAT,V,T) IF T>=0 THEN EMP=0 EXIT SUB END IF NEXT V END SUB SUB HASPATTERNS(HAS) HAS=0 EMP=0 FOR P=0 TO NUMPATT-1 CALL ISPATTERNEMPTY(P,EMP) IF NOT EMP THEN HAS=-1 EXIT SUB END IF NEXT P END SUB SUB GETFREETRACK(TRA) NOTE=0 SND=0 VOL=0 COM=0 PAR=0 FOR T=0 TO NUMTRAC-1 OK=-1 FOR R=0 TO NUMROWS-1 CALL GETROW(T,R,NOTE,SND,VOL,COM,PAR) IF NOTE>0 OR SND>0 OR VOL>0 OR COM>0 OR PAR>0 THEN OK=0 R=31 END IF NEXT R IF OK THEN TRA=T EXIT SUB END IF NEXT T TRA=-1 END SUB SUB GETLOOPSTART(PAT) V=0 FOR P=PAT TO 0 STEP -1 CALL GETLOOP(P,0,V) IF V=1 THEN PAT=P EXIT SUB END IF NEXT P PAT=0 END SUB SUB SETLOOP(PAT,PARAM,VALUE) A=APATT+PAT*PATTSIZE+PARAM TRA=(PEEK(A) AND %01111111) POKE A,(VALUE*128 OR TRA) END SUB SUB GETLOOP(PAT,PARAM,VALUE) A=APATT+PAT*PATTSIZE+PARAM VALUE=PEEK(A)\128 END SUB SUB CLEARMUSIC 'SOUNDS CALL CLEARSOUNDS 'PATTERNS CALL CLEARPATTERNS 'TRACKS CALL CLEARTRACKS END SUB SUB CLEARPATTERNS FILL APATT,NUMPATT*PATTSIZE,%01000000 END SUB SUB CLEARTRACKS FOR T=0 TO NUMTRAC-1 CALL CLEARTRACK(T) NEXT T END SUB SUB CLEARSOUNDS COPY ROM(15),NUMINST*INSTSIZE TO AINST END SUB SUB CLEARTRACK(TRA) A=ATRAC+TRA*TRACSIZE FILL A,TRACSIZE,0 END SUB SUB GETLASTTRACK(TRA) NOTE=0 SND=0 VOL=0 COM=0 PAR=0 FOR T=NUMTRAC-1 TO 0 STEP -1 OK=0 FOR R=0 TO NUMROWS-1 CALL GETROW(T,R,NOTE,SND,VOL,COM,PAR) IF NOTE>0 OR SND>0 OR VOL>0 OR COM>0 OR PAR>0 THEN OK=-1 R=31 END IF NEXT R IF OK THEN TRA=T EXIT SUB END IF NEXT T TRA=-1 END SUB '==== PLAYER ==== 'MODE 1: SONG 'MODE 2: PATTERN SUB PLAYMUSIC(PAT,MODE) STOP PLAYER_PAT=PAT PLAYER_MODE=MODE PLAYER_SPEED=8 PLAYER_TICK=-1 PLAYER_ROW=0 PLAYER_BREAK=0 PLAYER_TRA=-1 PLAYER_VOI=-1 SYSTEM 0,0 END SUB SUB PLAYTRACK(TRA,VOI) STOP PLAYER_TRA=TRA PLAYER_VOI=VOI PLAYER_MODE=3 PLAYER_SPEED=8 PLAYER_TICK=-1 PLAYER_ROW=0 PLAYER_BREAK=0 PLAYER_PAT=-1 SYSTEM 0,0 END SUB SUB CHANGEMUSIC(PAT) IF PLAYER_MODE<1 OR PLAYER_MODE>2 THEN EXIT SUB STOP PLAYER_PAT=PAT PLAYER_TICK=-1 PLAYER_ROW=0 END SUB SUB CHANGETRACK(TRA) IF PLAYER_MODE<>3 THEN EXIT SUB STOP PLAYER_TRA=TRA PLAYER_TICK=-1 PLAYER_ROW=0 END SUB SUB STOPPLAYER PLAYER_MODE=0 SYSTEM 0,1 END SUB SUB UPDATEPLAYER CALL UPDMETERSVBL IF PLAYER_MODE=0 THEN EXIT SUB IF PLAYER_TICK=-1 THEN PLAYER_TICK=0 ELSE IF PLAYER_TICK=0 THEN IF PLAYER_BREAK THEN PLAYER_BREAK=0 PLAYER_ROW=0 ELSE PLAYER_ROW=(PLAYER_ROW+1) MOD NUMROWS END IF IF PLAYER_ROW=0 AND PLAYER_MODE=1 THEN V=0 CALL GETLOOP(PLAYER_PAT,2,V) IF V=1 THEN PLAYER_MODE=0 EXIT SUB END IF CALL GETLOOP(PLAYER_PAT,1,V) IF V=1 THEN CALL GETLOOPSTART(PLAYER_PAT) ELSE P=PLAYER_PAT+1 IF P=0 THEN CALL PLAYROW(T,PLAYER_ROW,V) NEXT V END IF IF PLAYER_SPEED=0 THEN PLAYER_MODE=0 EXIT SUB END IF END IF PLAYER_TICK=(PLAYER_TICK+1) MOD PLAYER_SPEED END SUB SUB PLAYROW(TRA,ROW,VOI) NOTE=0 SND=0 VOL=0 COM=0 PAR=0 CALL GETROW(TRA,ROW,NOTE,SND,VOL,COM,PAR) IF NOTE>0 AND NOTE<255 THEN CALL SETSOUND(VOI,SND) IF VOL>0 THEN VOLUME VOI,VOL, CALL COMMAND(VOI,COM,PAR) IF NOTE=255 THEN STOP VOI ELSE IF NOTE>0 THEN PLAY VOI,NOTE END IF END SUB SUB SETSOUND(VOI,SND) A=AINST+SND*INSTSIZE TA=$FF40+VOI*12+4 COPY A,INSTSIZE TO TA END SUB SUB COMMAND(VOI,COM,PAR) IF COM=0 AND PAR=0 THEN EXIT SUB IF COM=$0 THEN VOLUME VOI,,PAR AND %11 ELSE IF COM=$1 THEN ENVELOPE VOI,PAR,,, ELSE IF COM=$2 THEN ENVELOPE VOI,,PAR,, ELSE IF COM=$3 THEN ENVELOPE VOI,,,PAR, ELSE IF COM=$4 THEN ENVELOPE VOI,,,,PAR ELSE IF COM=$5 THEN LFO VOI,PAR,,, ELSE IF COM=$6 THEN LFO VOI,,PAR,, ELSE IF COM=$7 THEN LFO VOI,,,PAR, ELSE IF COM=$8 THEN LFO VOI,,,,PAR ELSE IF COM=$9 THEN SOUND VOI,,PAR, ELSE IF COM=$D THEN PLAYER_SPEED=$10+PAR ELSE IF COM=$E THEN PLAYER_SPEED=PAR ELSE IF COM=$F THEN IF PAR=0 THEN PLAYER_BREAK=-1 ELSE IF PAR=1 THEN STOP VOI VOLUME VOI,0, END IF END IF END SUB '==== GENERIC UI ==== SUB SHOWMESSAGE(MSG$) COPY $9000,40 TO $D000 PAL 1 BG FILL 0,0 TO 19,0 CHAR 192 TEXT 0,0,MSG$ WAIT 60 COPY $D000,40 TO $9000 END SUB SUB SHOWMENU(MSG$,MAXI,RESULT) PAL 1 PRIO 1 BG FILL 0,0 TO 19,0 CHAR 192 TEXT (20-LEN(MSG$))/2,0,MSG$ PAL 0 Y=3+MAXI*2 BG FILL 0,1 TO 19,Y CHAR 1 BG FILL 0,1 TO 19,1 CHAR 31 BG FILL 0,Y TO 19,Y CHAR 16 FOR I=0 TO MAXI T$=MENU$(I) TEXT (20-LEN(T$))/2,2+I*2,T$ NEXT I WHILE TOUCH WAIT VBL WEND DO IF TOUCH AND TOUCH.Y>=12 THEN I=(((TOUCH.Y-4)\8)-1)\2 IF I<=MAXI THEN CALL HIGHLIGHT(0,2+I*2,19,2+I*2) WHILE TOUCH WAIT VBL WEND RESULT=I GOTO EXITMENU END IF END IF WAIT VBL LOOP EXITMENU: PRIO 0 END SUB SUB HIGHLIGHT(X1,Y1,X2,Y2) BG TINT X1,Y1 TO X2,Y2 PAL 1 END SUB SUB METERSON SPRITE OFF 60 TO 63 FOR V=0 TO 3 SPRITE 60+V PAL 4 SIZE 3 NEXT V METERSENABLED=-1 ON RASTER CALL UPDMETERSRASTER END SUB SUB METERSOFF METERSENABLED=0 ON RASTER OFF SPRITE OFF 60 TO 63 END SUB SUB UPDMETERSVBL IF NOT METERSENABLED THEN EXIT SUB IF SELTAB=0 THEN FOR V=0 TO 3 PEAK=PEEK($FF40+V*12+3) SPRITE 60+V,(6+V*4)*8,(255-PEAK)/8+32,128 NEXT V ELSE IF SELTAB=1 THEN PEAK=PEEK($FF40+CURVOI*12+3) SPRITE 60,6*8,(255-PEAK)/8+32,128 END IF END SUB SUB UPDMETERSRASTER IF RASTER=64 THEN SPRITE OFF 60 TO 63 END SUB '==== TOUCH ZONES SUBPROGRAMS ==== SUB SETZONE(N,X,Y,W,H) ZONEX(N)=X ZONEY(N)=Y ZONEW(N)=W ZONEH(N)=H IF ZONE_PAL>=0 AND ZONE_PAL<8 THEN 'HIGHLIGHT AND NORMAL PALETTE P=CELL.A(X,Y) AND %111 ZONEP(N)=(ZONE_PAL*16)+P ELSE 'NO HIGHLIGHT ZONEP(N)=$FF END IF END SUB SUB CLEARZONE(N) CALL SETZONE(N,0,0,0,0) END SUB SUB CLEARZONES(N,M) FOR I=N TO M CALL SETZONE(I,0,0,0,0) NEXT I END SUB SUB RESETZONES FOR I=0 TO MAX_ZONE CALL CLEARZONE(I) NEXT I CUR_ZONE=-1 ZONE_PAL=-1 END SUB SUB UPDATEZONES CX=TOUCH.X\8 CY=TOUCH.Y\8 IF ZONE_EVENT=E_UP OR ZONE_EVENT=E_OUT THEN CUR_ZONE=-1 ZONE_EVENT=0 IF CUR_ZONE>=0 THEN CALL INSIDEZONE(CUR_ZONE,CX,CY) IF ZONE_RESULT THEN ZONE_IN_X=CX-ZONEX(CUR_ZONE) ZONE_IN_Y=CY-ZONEY(CUR_ZONE) IF NOT TOUCH THEN CALL PAINTZONE(CUR_ZONE,0) ZONE_EVENT=E_UP ELSE IF CX<>ZONE_LAST_X OR CY<>ZONE_LAST_Y THEN ZONE_EVENT=E_DRAG END IF ELSE CALL PAINTZONE(CUR_ZONE,0) ZONE_EVENT=E_OUT END IF ELSE IF TAP THEN FOR I=0 TO MAX_ZONE IF ZONEW(I)>0 THEN CALL INSIDEZONE(I,CX,CY) IF ZONE_RESULT THEN ZONE_EVENT=E_DOWN CUR_ZONE=I ZONE_IN_X=CX-ZONEX(I) ZONE_IN_Y=CY-ZONEY(I) CALL PAINTZONE(I,1) END IF END IF NEXT I END IF ZONE_LAST_X=CX ZONE_LAST_Y=CY END SUB SUB INSIDEZONE(N,CX,CY) ZONE_RESULT=(CX>=ZONEX(N) AND CX=ZONEY(N) AND CY$FF THEN IF SEL THEN P=P\16 ELSE P=P MOD 16 BG TINT ZONEX(N),ZONEY(N) TO ZONEX(N)+ZONEW(N)-1,ZONEY(N)+ZONEH(N)-1 PAL P END IF END SUB '======== #1:MAIN PALETTES 003F2A15000F050000052A00000A0030 003F3C38003F2A15003F2A15003F2A15 #2:MAIN CHARACTERS 00000000000000000000000000000000 0000000000000000FFFFFFFFFFFFFFFF FF809BB28AB28080007F7F7F7F7F7F7F FE0119A1A999010101FFFFFFFFFFFFFF FF80B1AAB3A28080007F7F7F7F7F7F7F FE0139919191010101FFFFFFFFFFFFFF FF80808080818387007F7F7F7F7F7F7F FE0101010181C1E101FFFFFFFFFFFFFF 01452911294501FFFFFFFFFFFFFFFFFF 014931FD314901FFFFFFFFFFFFFFFFFF FF80BAA2B2A28080007F7F7F7F7F7F7F FE019D9991DD010101FFFFFFFFFFFFFF 808898BE9888807F7F7F7F7F7F7F7FFF 80888CBE8C88807F7F7F7F7F7F7F7FFF 80808080808080807F7F7F7F7F7F7F7F 0101010101010101FFFFFFFFFFFFFFFF 00000000000000FFFFFFFFFFFFFFFFFF 808080808080807F7F7F7F7F7F7F7FFF FDFDFDFDFDFD01FFFFFFFFFFFFFFFFFF C1F1FDFDF1C101FFFFFFFFFFFFFFFFFF FF809BB189B18080007F7F7F7F7F7F7F FE0191292911010101FFFFFFFFFFFFFF 8F9F80808080807F7F7F7F7F7F7F7FFF F1F90101010101FFFFFFFFFFFFFFFFFF FF8091AAAA918080007F7F7F7F7F7F7F FE01B9111191010101FFFFFFFFFFFFFF FF80A1A2A2B98080007F7F7F7F7F7F7F FE0111A9B929010101FFFFFFFFFFFFFF FF80B1B2ABB28080007F7F7F7F7F7F7F FE0119A1A199010101FFFFFFFFFFFFFF FF000000000000FF00FFFFFFFFFFFFFF FF0000000000000000FFFFFFFFFFFFFF FF80808080808080FF80808080808080 FEFEFEFEFEFEFEFE0101010101010101 BEFEFEFEFEBEBEBEFEBEBEBEBEFEFEFE FFAA5555555555550055FFFFFFFFFFFF 012161FD612101FFFFFFFFFFFFFFFFFF 011119FD191101FFFFFFFFFFFFFFFFFF FF80808080809F8F007F7F7F7F7F7F7F FE0101010101F9F101FFFFFFFFFFFFFF FEFEFEE6DEDEE6FE0101010101010101 FDCDCDFDFD7D01FFFFB7B787B7FFFFFF FF8099B28BB28080007F7F7F7F7F7F7F FE0129A9A991010101FFFFFFFFFFFFFF FFAA557560FF60750055FFDF9F009FDF FFAA555507FF07550055FFFBF900F9FB 00003044463F0604FFFFCFBBB9C0F9FB 00003C3C3C3C0000FFFFC3C3C3C3FFFF FE81818181818181017F7F7F7F7F7F7F FEFEFEFEFEFE807F0101010101017FFF FEFEFEFEFEFE8000FEFEFEFEFEFE8000 555555555555FFFFFFFFFFFFFFFFFFFF FF80B3ABAAB38080007F7F7F7F7F7F7F FE01A12121B9010101FFFFFFFFFFFFFF 878381808080807F7F7F7F7F7F7F7FFF E1C18101010101FFFFFFFFFFFFFFFFFF EED6D6D6EEFE807F0101010101017FFF EECEEEEEC6FE807F0101010101017FFF CEF6EEDEC6FE807F0101010101017FFF CEF6EEF6CEFE807F0101010101017FFF DED6C6F6F6FE807F0101010101017FFF C6DECEF6CEFE807F0101010101017FFF E6DECED6EEFE807F0101010101017FFF C6F6EEEEEEFE807F0101010101017FFF FF80BB9293928080007F7F7F7F7F7F7F FE0111A939A9010101FFFFFFFFFFFFFF 3F40808080808080C0BF7F7F7F7F7F7F FC0201010101010103FFFFFFFFFFFFFF 000000003F408080FFFFFFFFC0BF7F7F 00000000FC020101FFFFFFFF03FFFFFF 01010101010101FFFFFFFFFFFFFFFFFF 017D7D7D7D7D01FFFFABABAB8383FFFF FF80B3ABAAAB8080007F7F7F7F7F7F7F FE01A92939B9010101FFFFFFFFFFFFFF FF80BBB2A2BB8080007F7F7F7F7F7F7F FE0101818101010101FFFFFFFFFFFFFF 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 81A1E9FDE9A181FFFFFFFFFFFFFFFFFF 011D011D011D01FFFFFFFFFFFFFFFFFF 80808080808080FF7F7F7F7F7F7F7FFF 016D016D016D01FFFFFFFFFFFFFFFFFF FF80808080808080007F7F7F7F7F7F7F 818181818181817F7F7F7F7F7F7F7FFF FF80ABB3AAAB8080007F7F7F7F7F7F7F FE01A9291191010101FFFFFFFFFFFFFF 0101017D391101FFFFFFFFFFFFFFFFFF 1D3D7D7D7D7D01FFFFE7C7C7C7FFFFFF 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 24242424242424241C1C1C1C1C1C1C1C 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 24242424242424241C1C1C1C1C1C1C1C 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 24242424242424241C1C1C1C1C1C1C1C 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 24242424242424241C1C1C1C1C1C1C1C 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 0000000000000000FFFFFFFFFFFFFFFF 0018181818001800FFE7E7E7E7FFE7FF 006C6C2400000000FF9393DBFFFFFFFF 00247E24247E2400FFDB81DBDB81DBFF 00083E380E3E0800FFF7C1C7F1C1F7FF 0062640810264600FF9D9BF7EFD9B9FF 001C34386E643A00FFE3CBC7919BC5FF 0018183000000000FFE7E7CFFFFFFFFF 000C183030180C00FFF3E7CFCFE7F3FF 0030180C0C183000FFCFE7F3F3E7CFFF 000024187E182400FFFFDBE781E7DBFF 000018187E181800FFFFE7E781E7E7FF 0000000018183000FFFFFFFFE7E7CFFF 000000007E000000FFFFFFFF81FFFFFF 0000000000181800FFFFFFFFFFE7E7FF 00060C1830604000FFF9F3E7CF9FBFFF 003C666E76663C00FFC399918999C3FF 0018381818187E00FFE7C7E7E7E781FF 003C660C18307E00FFC399F3E7CF81FF 003C660C06663C00FFC399F3F999C3FF 0066667E06060600FF999981F9F9F9FF 007E607C06067C00FF819F83F9F983FF 001C307C66663C00FFE3CF839999C3FF 007E060C18303000FF81F9F3E7CFCFFF 003C663C66663C00FFC399C39999C3FF 003C663E06663C00FFC399C1F999C3FF 0000001800180000FFFFFFE7FFE7FFFF 0000001800183000FFFFFFE7FFE7CFFF 00000C1830180C00FFFFF3E7CFE7F3FF 0000007E007E0000FFFFFF81FF81FFFF 000030180C183000FFFFCFE7F3E7CFFF 003C660C18001800FFC399F3E7FFE7FF 003C666E6E603C00FFC39991919FC3FF 00183C667E666600FFE7C399819999FF 007C667C66667C00FF839983999983FF 003C666060663C00FFC3999F9F99C3FF 00786C66666C7800FF879399999387FF 007E607860607E00FF819F879F9F81FF 007E607860606000FF819F879F9F9FFF 003C606E66663C00FFC39F919999C3FF 0066667E66666600FF999981999999FF 003C181818183C00FFC3E7E7E7E7C3FF 001E060606663C00FFE1F9F9F999C3FF 00666C78786C6600FF999387879399FF 0060606060607E00FF9F9F9F9F9F81FF 0042667E7E666600FFBD9981819999FF 0066767E6E666600FF998981919999FF 003C666666663C00FFC399999999C3FF 007C667C60606000FF8399839F9F9FFF 003C66666A6C3E00FFC399999593C1FF 007C667C786C6600FF839983879399FF 003E603C06067C00FFC19FC3F9F983FF 007E181818181800FF81E7E7E7E7E7FF 0066666666663C00FF9999999999C3FF 00666666663C1800FF99999999C3E7FF 0066667E7E664200FF9999818199BDFF 00663C183C666600FF99C3E7C39999FF 0066663C18181800FF9999C3E7E7E7FF 007E0C1830607E00FF81F3E7CF9F81FF 003C303030303C00FFC3CFCFCFCFC3FF 006030180C060200FF9FCFE7F3F9FDFF 003C0C0C0C0C3C00FFC3F3F3F3F3C3FF 00183C6600000000FFE7C399FFFFFFFF 0000000000007E00FFFFFFFFFFFF81FF #3:MAIN BG 00001410020003000400050014001500 04000500040005004A004B0042004300 44004500440045000A000B0011001300 11001300110012001100240011002500 11005800520053005200510052005000 11002900180019001800190028002200 21002200210021002200210022002100 22002100140015003400350011002400 11002500380032003100320031003100 32003100320031003200310011000900 11000800230023002300230023002300 2300230023002C002D00230023002300 23002300230023002300230033003300 33003300330033003300330033003300 33003300330033003300330033003300 33003300300030003000300030003000 30003000300030003000300030003000 3000300054001F001F001F0001000100 01000100010001000100010001000100 0100010001000100010001000E000100 01000100400041001400150040004100 400041004A004B000100010044004500 42004300440045000A000B0011001300 11001200110024001100250011005800 10001000520053005200510052005000 1100290014001500560057004A004B00 01000100010001000100010044004500 44004500420043000A000B0011001200 11004700110058001000100010001000 10001000520053005200510052005000 110029000100010001000F0001000100 01000100010001000100010001000100 01000100010001000100010010001000 10004600550055005500550055005500 55005500550055005500550055005500 55005500000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000 #4:DISK BG 00001410000000000000000000000000 00000000000000000000000000000000 0000000000000000000000001E001E00 1E001E001E001E001E001E001E001E00 1E001E001E001E001E001E001E001E00 1E001E00000000000000000000000000 00000000000000000000000000000000 00000000000000000600070000000000 00000000000000000000000000000000 00000000000000000000000000000000 16001700000000000000000000000000 00000000000000000000000000000000 00000000000000000E00010000000000 00000000000000000000000000000000 00000000000000000000000000000000 0E000100000000000000000000000000 00000000000000000000000000000000 00000000000000000E00010000000000 00000000000000000000000000000000 00000000000000000000000000000000 0E000100000000000000000000000000 00000000000000000000000000000000 00000000000000000E00010000000000 00000000000000000000000000000000 00000000000000000000000000000000 0E000100000000000000000000000000 00000000000000000000000000000000 00000000000000000E00010000000000 00000000000000000000000000000000 00000000000000000000000000000000 26002700000000000000000000000000 00000000000000000000000000000000 0000000000000000360037001E001E00 1E001E001E001E001E001E001E001E00 1E001E001E001E001E001E001E001E00 1E001E001C001D001F001F001F001F00 1F001F001F001F001F001F001F001F00 480049001A001B002A002B0011002400 01000100010001000100010001000100 0100010001000100110059000C002900 0D002900 #15:SOUND PRESETS 2800303A000000001800846C003A0000 08006060000000002800303019FE0000 38002020000000003800505000000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 ================================================ FILE: programs/Star Scroller 1.2.nx ================================================ 'TITLE: STAR SCROLLER 'AUTHORS: TIMO KLOSS, DESBYC DIM GLOBAL STARX(127) FOR I=0 TO 127 STARX(I)=INT(RND*256) NEXT I BG 1 BG FILL 0,0 TO 0,15 CHAR 3 GLOBAL SX,T ON RASTER CALL RASTERFX T$="WELCOME TO LOWRES NX! A VIRTUAL RETRO GAME CONSOLE,WHICH CAN BE PROGRAMMED IN THE CLASSIC BASIC LANGUAGE. HAVE FUN! ... STAR SCROLLER CODE AND GFX BY TIMO, MUSIC BY DESBYC. GREETINGS TO ALL LOWRES NX CODERS! " CELL SIZE 0,1 BG 0 PAL 1 PRIO 1 P=1 L=LEN(T$) MUSIC DO IF SX MOD 16=0 THEN P$=MID$(T$,P,1) IF P$>="A" AND P$<="Z" THEN N=ASC(P$)-ASC("A") C=(N\8)*32+(N MOD 8)*2+64 ELSE IF P$="." THEN C=164 IF P$="," THEN C=166 IF P$="!" THEN C=168 IF P$="?" THEN C=170 IF P$=" " THEN C=174 END IF CELL SX/16+10,0,C P=(P MOD L)+1 END IF SCROLL 0,SX,SIN(T*0.02)*32-56 P2=PEEK($FF5B) FOR I=0 TO 31 X1=SIN(T*0.02+I*PI/16) Y1=COS(T*0.03+I*PI/16) X2=COS(T*0.045+I*0.09) Y2=SIN(T*0.035+I*0.1) X3=SIN(T*0.025+I*0.12) Y3=COS(T*0.015+I*0.13) Z=1+P2/1536 X=(X1*X2*60+X3*32)*Z Y=(Y1*Y2*60+Y3*32)*Z SPRITE I PAL 2+(I/4) MOD 2 SPRITE I,X+76,Y+60,2 NEXT I T=T+1 SX=SX+1 WAIT VBL LOOP SUB RASTERFX F=0.5 D=((RASTER MOD 4)+1) SCROLL 1,TIMER*F/D+STARX(RASTER),0 END SUB #1:MAIN PALETTES 001B070205383420002F0A05003B2211 00000000000000000000000000000000 #2:MAIN CHARACTERS 00000000000000000000000000000000 00000000000000000000000000000000 2402B1310183463C3C7ECFCFFFFF7E3C 80008080800080800080808000808080 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 0F3F7046C9909090000F3F3970606060 00C0E020309090900000C0C0E0606060 FFFF809F90909F9F007F7F606060607F 00C0E020B09020400000C0C06060C080 0F3F7046C9909090000F3F3970606060 00C0E02030F000000000C0C0E0000000 FFFF809E91909090007F7F6160606060 00C0E020309090900000C0C0E0606060 FFFF809F90909F9F007F7F606060607E F0F010F00000000000E0E00000000000 FFFF809F90909F9F007F7F606060607E F0F010F00000000000E0E00000000000 0F3F7046C9909093000F3F3970606060 00C0E02030F000F00000C0C0E0000000 F0F0909090909F9F006060606060607F F0F090909090909000606060606060E0 9F9F809F909090F0607F7F6060606000 90901090909090F060E0E06060606000 9F9090909F9F80FF60606060607F7F00 20909090A020C000C0606060C0C00000 909090994F46300F60606070393F0F00 0000F0F02020C000000000E0C0C00000 909090919F9E80FF60606060617F7F00 909090902020C000606060E0C0C00000 819F90909F9F80FF7E606060607F7F00 00000000F0F010F00000000000E0E000 819F9090909090F07E60606060606000 00000000000000000000000000000000 939390994F46300F61606070393F0F00 F09090902020C000E06060E0C0C00000 809F9090909090F07F60606060606000 10909090909090F0E060606060606000 3F3F203909090909001F1F0606060606 C0C040C0000000000080800000000000 FFFF80FF00000000007F7F0000000000 F0F010909090909000E0E06060606060 F0F0909193969C99006060606163677E F0F0909020408000006060E0C0800000 F0F09090909090900060606060606060 00000000000000000000000000000000 E0F0998F86909996006070797F6F6660 70F09010109090900060E0E0E0606060 F0F88C86939994920070787C6E676361 F0F0909090909010006060606060E0E0 0F3F7046C9909090000F3F3970606060 00C0E020309090900000C0C0E0606060 FFFF809F90909F9F007F7F606060607F 00C0E020B090A0200000C0C06060C0C0 090909093939203F06060606061F1F00 00000000C0C040C00000000000808000 00000001FFFE80FF00000000017F7F00 909090902020C000606060E0C0C00000 81999492919090F07E67636160606000 0080C060309090F0000080C0E0606000 909090909F9F80FF60606060607F7F00 00000000F0F010F00000000000E0E000 90909090909090F06060606060606000 90909090909090F06060606060606000 91909090909090F06060606060606000 10909090909090F0E060606060606000 909090994F46300F60606070393F0F00 909090902020C000606060E0C0C00000 809F9090909090F07F60606060606000 C0000000000000000000000000000000 0F3F7046C9909090000F3F3970606060 00C0E020309090900000C0C0E0606060 FFFF809F90909F9F007F7F606060607F 00C0E020B090A0200000C0C06060C0C0 0F3F7047C8884F67000F3F387070381F 00C0E030F00000C00000C0E000000000 FFFF80F909090909007F7F0606060606 F0F010F00000000000E0E00000000000 F0F09090909090900060606060606060 F0F09090909090900060606060606060 F0F09090909090900060606060606060 F0F09090909090900060606060606060 F0F09090909090900060606060606060 F0F09090909090900060606060606060 F0F059492F26101000603030191F0F0F F0F0A020404080800060C0C080800000 909090984F47300F60606070383F0F00 90909090A03010F060606060C0E0E000 80989492919090F07F67636160606000 4080C060309090F0800080C0E0606000 1E01F1F15F4E300F01000060313F0F00 E02030102020C000C0C0E0E0C0C00000 090909090909090F0606060606060600 00000000000000000000000000000000 909090994F46300F60606070393F0F00 909090902020C000606060E0C0C00000 905949292F16100F60303010190F0F00 90A020404080800060C0C08080000000 969F9990868990E060666F7F79706000 9090909010109070606060E0E0E06000 101030266949D0F00F0F1F1930306000 8080C0406020B0F000008080C0C06000 F0F059492F26101000603030191F0F0F F0F0A020404080800060C0C080800000 FFFF80FF0103060C007F7F0000010307 F0F010909020408000E0E060E0C08000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 F8F88888888888880070707070707070 00000000000000000000000000000000 0F3F76C9F0010306000F397000000103 00C0E020B09020400000C0C060E0C080 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 090909090909090F0606060606060600 00000000000000000000000000000000 193264C89F9F80FF0E1C3870607F7F00 00000000F0F010F00000000000E0E000 00000070F88888700000000070707000 00000000000000000000000000000000 000000787850D0E00000000030206000 00000000000000000000000000000000 50700070F88888702000000070707000 00000000000000000000000000000000 060D0F00060F09060306000000060600 80000000000000000000000000000000 #15:MAIN SOUND 6801F03200017000680200F508026000 2800303019FE00007801F04F0AFF0000 7801F00F02FF00000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 0800000F000000000800000F00000000 00404007000103400002030700040340 80040306000403060004030600010306 00020306000440400000030300000305 00000305000403060000030300814007 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 40404040404040404040404040404040 170F44190F00160F44190F00190F0027 0F00190F44250F00170F001A0F001D0F 44190F00190F00250F00190F00190F00 170F001E0F44160F00190F00190F001B 0F00190F00190F00170F001A0F00250F 47190F00190F00190F00190F00190F00 311F0000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 3D1F0000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 192F00000000000000000000192F0000 0000000000000000192F00192F000000 00000000192F00000000000000000000 192F00000000000000000000192F0000 0000000000000000192F000000000000 00000000192F00192F00192F00192F00 461F00471F003B1F003A1F0000000000 0000000000000000441F00451F00391F 00381F00000000000000000000000000 3F1F00401F00341F00331F0000000000 00000000000000003D1F003E1F00321F 00000000411F00000000000000000000 5F4F005F4F005F4F005F4F005F4F005F 4F005F4F005F4F005F4F005F4F005F4F 005F4F005F4F005F4F005F4F005F4F00 5F4F005F4F005F4F005F4F005F4F005F 4F005F4F005F4F005F4F005F4F005F4F 005F4F005F4F005F4F005F4F005F4F00 5F4F005F4F005F4F005F4F003D3F005F 4F005F4F005F4F005F4F005F4F005F4F 005F4F003D3F005F4F005F4F005F4F00 5F4F005F4F005F4F005F4F003D3F005F 4F005F4F005F4F005F4F005F4F005F4F 005F4F003D3F005F4F003D3F003D3F00 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000323F00000000323F00323F00 ================================================ FILE: programs test/BG Scroll.nx ================================================ BG COPY 0,0,20,16 TO 0,0 GAMEPAD 1 DO IF LEFT TAP(0) THEN BG SCROLL 2,2 TO 17,13 STEP -1,0 IF RIGHT TAP(0) THEN BG SCROLL 2,2 TO 17,13 STEP 1,0 IF UP TAP(0) THEN BG SCROLL 2,2 TO 17,13 STEP 0,-1 IF DOWN TAP(0) THEN BG SCROLL 2,2 TO 17,13 STEP 0,1 WAIT VBL LOOP #1:MAIN PALETTES 053F2F00053834000000000000000000 00000000000000000000000000000000 #2:MAIN CHARACTERS 00000000000000000000000000000000 81422418182442810000000000000000 FFFFFFFFFFFFFFFF0000000000000000 0000000000000000FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 183C42C3C3423C18005A004242005A00 #3:MAIN BG 000014100101010100000101000000000000 01010101010100000101010100000000 01010000000001010101010100000101 01010101000001010101010100000101 00000000010101010000010100000101 01010000000000000200000002000000 02000000020000000000000000000000 00000000030001010000000001010000 02000000000000000000000002000000 00000000000000000000000000000101 01010000000000000200000002000200 02000000020000000000000003000000 00000000000001010101010100000000 03000000000000000000000003000000 00000000000000000000000000000101 00000101010100000000030002000200 02000300000000000000000000000000 00000200020000000000010101010000 00000000000000000000000000000000 02000200000000000200000000000000 01010000010100000000000000000000 00000000000002000000000002000200 00000000000001010000010100000000 00000301000000000300000000000200 00000000000002000000000003000000 01010101000000000000000000000000 00000000000000000000000000000200 00000000000001010000000001010000 04000400040004000000000004000000 00000000000002000000000004000101 00000101010104000400040004000400 04000400040004000000000000000200 00000000000001010000000001010300 00000000040004000400040004000400 04000400000000000000030100000000 01010101000001010101010100000101 00000101010101010101010101010000 00000101010101010101010101010000 01010101010101010101010101010000 01010000000001010101010100000101 0101 ================================================ FILE: programs test/Crazy Text.nx ================================================ RANDOMIZE TIMER PRINT "++++++++++++++++++++"; PRINT "...................."; PRINT "////////////////////"; PRINT PRINT " HELLO!" PRINT " THIS HAPPENDS WHEN" PRINT " YOU PRINT SOME " PRINT " TEXT ON SCREEN AND" PRINT " THEN CHANGE THE" PRINT " CELL ATTRIBUTES" PRINT " RANDOMLY." PRINT PRINT PRINT "////////////////////"; PRINT ".....LOWRES NX....."; PRINT "++++++++++++++++++++"; DO ATTR (RND*4,RND*2,RND*2,0,0) BG FILL RND*10,RND*8 TO 10+RND*10,8+RND*8 WAIT 4 LOOP ================================================ FILE: programs test/Demo 0.5.nx ================================================ '** SCROLLING BACKGROUND AND SPRITES DEMO 0.5 ** GLOBAL TICK,WATERY BG 0 BG COPY 0,0,32,16 TO 0,0 BG 1 BG COPY 0,16,32,16 TO 0,0 ON RASTER CALL RASTERFX GAMEPAD 1 BG 1 ATTR (7,,,1) TEXT 0,0,"SCORE" 'INIT SPRITE SPRITE.A 0,(6,,,,1) WATERY=112 JUMP=0 PX=32 PY=64 VY=0 DO TICK=TICK+1 'PLAYER CONTROL IF LEFT(0) THEN PX=PX-1 IF RIGHT(0) THEN PX=PX+1 IF PY=64 AND (BUTTON TAP(0,0) OR BUTTON TAP(0,1)) THEN VY=-8 'ANIMATE PLAYER SPRITE PY=PY+VY/2 IF PY=64 THEN VY=0 FR=130+((TICK/8) MOD 3)*2 ELSE VY=VY+0.5 FR=136 END IF SPRITE 0,PX,PY,FR 'SCROLL MAIN PLANE SCROLL 0,TICK,0 'WATER LEVEL WATERY=INT(112+SIN(TICK*0.01)*12) 'FAKE SCORE COUNTER NUMBER 15,0,TICK/20,5 WAIT VBL LOOP SUB RASTERFX '3D SCROLLING, SKY COLORS AND UNDER WATER IF RASTER=0 THEN SCROLL 1,0,0 COPY ROM(1),SIZE(1) TO $FF00 PALETTE 0,%110111,,, EXIT SUB END IF IF RASTER=WATERY THEN COPY ROM(5),SIZE(5) TO $FF00 EXIT SUB END IF IF RASTER>WATERY-1 THEN OFFS=SIN((RASTER+TICK*0.5)*0.1)*4 SCROLL 1,TICK*1.5+OFFS,0 SCROLL 0,TICK+OFFS,0 EXIT SUB ELSE IF RASTER<8 THEN SCROLL 1,0,0 ELSE IF RASTER<32 THEN IF RASTER=14 THEN PALETTE 0,%100111,,, IF RASTER=28 THEN PALETTE 0,%010111,,, SCROLL 1,TICK*3/8,0 EXIT SUB ELSE IF RASTER<56 THEN IF RASTER=42 THEN PALETTE 0,%000111,,, SCROLL 1,TICK/4,0 ELSE IF RASTER>56+3*8 THEN SCROLL 1,TICK*1.5,0 ELSE SCROLL 1,TICK*0.5*(0.5+(RASTER-55)/32),0 END IF END SUB #1:MAIN PALETTES 07382410000C0804000F0B07003F2F1B 003C38340030201000103039003F1500 #2:MAIN CHARACTERS 00000000000000000000000000000000 0020040000440002FFDFFFFFFFFBFFFF FFFFFF77220088DD00000088DDFFFFFF C0E0E8E8E2C8E0C23F1F17171D371F3D 01094B0B030B0123FFFFFFFFFFFFFFFF 030C12204044808000030F1F3F3F7F7F 8082404820100C03FFFF7F7F3F1F0F03 0002000010000000FFFFFFFFFFFFFFFF 0083443800834438FFFFFFFFFFFFFFFF 0083443800834438000083C7FFFFFFFF 08080808088B442818181818DBFF7C38 080808080808080818181A1C18583818 30382743C2E41C180000183C3C180000 00000000001044550000000000000000 7E81A3818985A17E007F5D7F777F7F7E 10220C3870C40802FFFDF3C78F3FFFFD 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 030C10204040808000030F1F3F3F7F7F 8080404020100C03FFFF7F7F3F1F0F03 0000000000000000FFFFFFFFFFFFFFFF 00000000000000FFFFFFFFFFFFFFFFFF 0304041820204080000303071F1F3F7F 804030080402020180C0F0F8FCFEFEFF 0000000000000000FFFFFFFFFFFFFFFF 007300E600700238FFFCFF1FFFFFFFC7 00801800010C2000FF7FFFFFFFF3FFFF 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 0307070F1F1F07050000010407070302 E0F8F0FCFCF8F0C00000B0FCDC80F020 000307070F1F1F070000000104070703 00E0F8F0FCFCF8F0000000B0FCDC80F0 000307070F1F1F070000000104070703 00E0F8F0FCFCF8F0000000B0FCDC80F0 070F0F1F3F3F0F1F000003090F0F0700 C0F0E0F8F8F0E040000060F8B800E080 00070F0F1F3F3F0F00000003090F0F03 0EDEFEFEFEFCFCF80E160E60F8B800E0 00000000000307070000000000000001 0000000000E0F8F000000000000000B0 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 0D1D1E1C0C000E1E0202031F0F0F0000 D0D83C1C1800383C2020E0FCF8780000 0F1F38301818180C00003F3707070000 5C5C983838380000BCBCF0C0C0800000 0B13033030302000040C1C0F0F0C0000 00D8FCFC80007078C0181C1C60E00000 1707000001050E0708181F0F0E020000 C0E0600080C040004060E0E060000000 3E3E3C1C6663604001091F1B191C1E00 B0A08206068600004050F0F8F8780000 0F0F0F1B1F1C0E1E04070307060F0100 FCFCF8FCFC0C1C1EFCDC80F028FCE000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 183C3C3C3C183C181824242424182418 6CFEFE7E240000006C92925A24000000 247EFF7E7EFF7E24245A815A5A815A24 083E7F7E3F7F3E080836414631413608 62F7FE7C3E7FEF4662959A742E59A946 1C3E7E7EFFFE7F3A1C224A46919A453A 183C3C78300000001824244830000000 0C1E3C78783C1E0C0C1224484824120C 30783C1E1E3C78303048241212244830 00247E7EFF7E7E2400245A6681665A24 00183C7EFF7E3C180018246681662418 000000183C3C78300000001824244830 0000007EFF7E00000000007E817E0000 00000000183C3C180000000018242418 060F1E3C78F0E040060912244890A040 3C7EFFFFFFFF7E3C3C4299918999423C 183C7C3C3C7EFF7E182444242466817E 3C7EFF7E3C7EFF7E3C429972244E817E 3C7EFF7E6FFF7E3C3C4299726999423C 66FFFFFF7F0F0F066699998179090906 7EFFFEFE7F7FFE7C7E819E827979827C 1C3E7CFEFFFF7E3C1C224C829999423C 7EFF7F1E3C7878307E81791224484830 3C7EFF7EFFFF7E3C3C4299429999423C 3C7EFF7F7FFF7E3C3C4299417999423C 0000183C183C18000000182418241800 0000183C183C78300000182418244830 000C1E3C783C1E0C000C12244824120C 00007EFF7EFF7E0000007E817E817E00 0030783C1E3C78300030482412244830 3C7EFF7E3C183C183C42997224182418 3C7EFFFFFFFE7E3C3C429991919E423C 183C7EFFFFFFFF661824429981999966 7CFEFFFEFFFFFE7C7C8299829999827C 3C7EFFF6F6FF7E3C3C4299969699423C 78FCFEFFFFFEFC787884929999928478 7EFFFEFCF8FEFF7E7E819E84989E817E 7EFFFEFCF8F0F0607E819E8498909060 3C7EFEFFFFFF7E3C3C429E919999423C 66FFFFFFFFFFFF666699998199999966 3C7E3C3C3C3C7E3C3C4224242424423C 1E3F1F0F6FFF7E3C1E2119096999423C 66FFFEFCFCFEFF666699928484929966 60F0F0F0F0FEFF7E60909090909E817E 42E7FFFFFFFFFF6642A5998181999966 66FFFFFFFFFFFF666699898191999966 3C7EFFFFFFFF7E3C3C4299999999423C 7CFEFFFEFCF0F0607C8299829C909060 3C7EFFFFFFFE7F3E3C4299999592413E 7CFEFFFEFCFEFF667C82998284929966 3E7FFE7E3F7FFE7C3E419E423979827C 7EFF7E3C3C3C3C187E81662424242418 66FFFFFFFFFF7E3C669999999999423C 66FFFFFFFF7E3C186699999999422418 66FFFFFFFFFFE742669999818199A542 66FF7E3C7EFFFF666699422442999966 66FFFF7E3C3C3C186699994224242418 7EFF7E3C78FEFF7E7E817224489E817E 3C7E7C78787C7E3C3C424C48484C423C 60F0783C1E0F07026090482412090502 3C7E3E1E1E3E7E3C3C4232121232423C 183C7EFF660000001824429966000000 00000000007EFF7E00000000007E817E #3:MAIN BG 00002020000000000501070905090000 00200000000000000000000000000000 00000000000000000000000000000000 05010701070105090000000000000000 00000000000005010701070107010529 05210729052900200020000000000000 00000000000000000000000000000521 07210721072107210529000000000000 00000000050107010701070107010721 07290729072905290020000000000000 00000000000000000000000000000721 07210721072107210721000000000000 00000000070107010701070107010721 07290729072907290529000000000000 00000000000000000000000000000621 07210721072107210629000000000000 00000000060107010701070107010729 0729072907290729062900000E000E00 0E000E00000000000000000000000000 062107210721062900000E000E000E00 00000000000006010701070106090721 07290729072906290000000000000000 00000000000000000000000000000000 000003A004A000000000000000000000 00000000000000000300040000000621 07A107A106290E000E00000000000000 00000000000000000000000000000000 000003A004A000000000000000000000 00000000000000000300040000000000 03A004A0000000000000000000000000 00000CA4000000000000000000000000 000003A004A000000000000000000000 0E000E00000000000300040000000080 03A004A0000000000000000000000000 0C040BA100000C040000000000000000 000003A004A000000000000000000000 000000000D810D81030004000DA10DA1 03A004A00DA10DA100000DA10DA10D21 0B010BA10D010B010D210D210D210D21 0D2103A004A000200DA10DA100200000 00000000020102010201020102010201 02010201020102010201020102010201 02010201020102010201020102010201 02010201020102010201020102010201 02010201010001000100010001000100 01000100010001000100010001000100 01000100010001000100010001000100 01000100010001000100010001000100 01000100010001000100010001000100 01000100010001000100010001000100 010001000F0001000100010001000100 01000100010001000100010001000100 0F000100010001000F00010001000100 01000100010001000100010001000100 01000100010001000100010001000100 01000F00010001000100010001000100 01000100010001000100010001000100 01000100010001000100010001000100 01000100010001000100010001000100 010001000100010001000F0001000100 01000100010001000100010001000100 0100010001000F000100010001000100 01000100010001000F00010001000100 01000100010001000100010001000100 01000100000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 0000000B000B00000000000000000000 00000000000000000000000000000000 00000000000000000000000000004003 4203400B000000000000000000000000 400342034203400B0000000000000000 00000000000000000000000000000000 00000000000000000000000040034203 420342034203400B0000000000000000 410343034303410B0000000000000000 00000000000000000000000000000000 00000000000000000000000041034303 430343034303410B0000000000000000 00000000000000000000000000000000 000000000000000040034203400B0000 00000000000000000000000000000000 00000000000000000000000000004003 4203400B000000000000000000000000 0000000000004003420342034203400B 00000000000000000000000000000000 00000000000000000000000040034203 42034203400B00000000000000000000 0000000000004103430343034303410B 00000000000000000000000000000000 00000000000000000000000041034303 43034303410B00000000000000000000 00000000470247024702470247024702 47024702470247024702470247024702 47024702470247024702470247024702 47024702470247024702470247024702 47024702480248024802480248024802 48024802480248024702480246024802 48024702460248024802480247024802 4802460A4802460A460A460A460A4802 480248024802460A4802460248024802 460A4602480248024802460A48024802 48024602460A460A4802460A460A4802 460A48024602460A48024802460A4802 460248024602460A460A480246024802 460A460A48024602460A48024802460A 4802460A460A4802460A480246024802 4802460A46024802460A460A460A4802 480248024802460A4802460248024802 460A4602480248024802460A48024802 480246024702460A4802460248024802 460A48024602460A48024802460A4802 4602480246024602460202A102A14602 46024602460246024602460246024602 46024602460246024602460246024602 46024602460202A102A1460246024602 4602460246024602460203A004A04602 46024602460202A102A1460246024602 4602460202A102A14602460246024602 46024602460203A004A0460246024602 4602460246024602460203A004A04602 46024602460203A004A0460246024602 4602460203A004A046024602460202A1 02A14602460203A004A0460246024602 4602460246024602460203A004A04602 46024602460203A004A0460246024602 4602460203A004A046024602460203A0 04A04602460203A004A0460246024602 46024602 #5:WATER COLOR PALETTES 070F0B07000F0B07000F0B07003F2F1B 000F0B07000B07030003070F000F0601 ================================================ FILE: programs test/Files.nx ================================================ FILES FOR I=0 TO 15 NUMBER 0,I,I,2 TEXT 3,I,FILE$(I) NEXT I ================================================ FILE: programs test/Scrolling Map 0.3.nx ================================================ '** SCROLLING MAP 0.3 ** 'GET MAP SIZE MAPW=PEEK(ROM(3)+2) MAPH=PEEK(ROM(3)+3) 'INITIAL CAMERA POSITION CAMX=176 CAMY=188 MAPCOL=INT(CAMX/8) MAPROW=INT(CAMY/8) 'COPY VISIBLE AREA FROM MAP MEMORY TO BACKGROUND BG COPY MAPCOL,MAPROW,21,17 TO MAPCOL,MAPROW GAMEPAD 1 DO 'MOVE CAMERA IF UP(0) AND CAMY>0 THEN CAMY=CAMY-1 IF DOWN(0) AND CAMY0 THEN CAMX=CAMX-1 IF RIGHT(0) AND CAMXMAPCOL THEN BG COPY COL+20,ROW,1,17 TO COL+20,ROW MAPCOL=COL ELSE IF COLMAPROW THEN BG COPY COL,ROW+16,21,1 TO COL,ROW+16 MAPROW=ROW ELSE IF ROW"]" THEN TEXT 0,OFY+ROW,T$ ROW=ROW+1 UNTIL T$="]" FOR I=0 TO 180 FXT=I/180 WAIT VBL NEXT I FXT=0 SCY=SCY+128 DO IF TAP THEN GOTO NEXTPAGE WAIT VBL LOOP SUB FX T=MIN(MAX(0,FXT*1.25-RASTER/384),1) F=T*T*(3-2*T) X=SIN(F*PI*2)*8 Y=F*128+SCY SCROLL 0,X,Y END SUB SLIDES: DATA "" DATA "" DATA "" DATA "" DATA "" DATA "" DATA "********************" DATA " LOWRES NX" DATA "********************" DATA "]" DATA "********************" DATA " LOWRES NX" DATA "********************" DATA "" DATA " - FANTASY CONSOLE" DATA "" DATA " - PROGRAMMABLE IN" DATA " BASIC" DATA "" DATA " - AUTHENTIC 8-BIT" DATA " SYSTEM" DATA "]" DATA "********************" DATA " FANTASY CONSOLE" DATA "********************" DATA "" DATA " - HANDHELD CONSOLE" DATA "" DATA " - CARTRIDGES" DATA "" DATA " - TOUCHSCREEN" DATA " (160X128)" DATA "" DATA " - TWO GAME" DATA " CONTROLLERS" DATA "" DATA " - KEYBOARD" DATA "]" DATA "********************" DATA " PLAY" DATA "********************" DATA "" DATA " - OPEN NX FILE" DATA "" DATA " - GAMEPAD:" DATA " CURSOR KEYS" DATA " BUTTON A: Z" DATA " BUTTON B: X" DATA "]" DATA "********************" DATA " HELLO WORLD" DATA "********************" DATA "" DATA " TEXT FILE:" DATA " HELLO WORLD.NX" DATA "" DATA " - PRINT" DATA "" DATA " - INPUT" DATA "]" DATA "********************" DATA " ASSETS" DATA "********************" DATA "" DATA " SPRITE FOR" DATA " HELLO WORLD" DATA "" DATA " GRAPHICS BASED ON" DATA " 8x8-PIXEL" DATA " CHARACTERS" DATA "" DATA " CHARACTER" DATA " DESIGNER" DATA "]" DATA "********************" DATA " NX FILES" DATA "********************" DATA "" DATA " INCLUDE:" DATA "" DATA " - SOURCE CODE" DATA "" DATA " - ASSETS" DATA " (16 ROM ENTRIES)" DATA "]" DATA "********************" DATA " HELLO WORLD" DATA "********************" DATA "" DATA " SHOW SPRITE:" DATA "" DATA " - SPRITE N,X,Y,C" DATA "]" DATA "********************" DATA " HELLO WORLD" DATA "********************" DATA "" DATA " ANIMATE:" DATA "" DATA " - DO/LOOP" DATA "" DATA " - WAIT VBL" DATA "]" DATA "********************" DATA " HELLO WORLD" DATA "********************" DATA "" DATA " MOVE:" DATA "" DATA " - GAMEPAD N" DATA "" DATA " - UP(P)" DATA " - DOWN(P)" DATA " - LEFT(P)" DATA " - RIGHT(P)" DATA "]" DATA "********************" DATA " ASSETS" DATA "********************" DATA "" DATA " BACKGROUND FOR" DATA " HELLO WORLD" DATA "" DATA " MORE CHARACTERS..." DATA "]" DATA "********************" DATA " ASSETS" DATA "********************" DATA "" DATA " BACKGROUND" DATA " DESIGNER" DATA "" DATA " - MAPS AND COLORS" DATA "]" DATA "********************" DATA " HELLO WORLD" DATA "********************" DATA "" DATA " SHOW BACKGROUND:" DATA "" DATA " - BG N" DATA "" DATA " - BG COPY X,Y,W,H" DATA " TO X,Y" DATA "]" DATA "********************" DATA " SCREEN LAYERS" DATA "********************" DATA "" DATA " - NO PIXEL BUFFER" DATA "" DATA " +-BACKDROP--+" DATA " +-BG1-------+ I" DATA " +-BG0-------+ I I" DATA " +-SPRITES---+ I I I" DATA " I I I I I" DATA " I I I I I" DATA " I I I I-+" DATA " I I I-+" DATA " I I-+" DATA " +-----------+" DATA "]" DATA "********************" DATA " HELLO WORLD" DATA "********************" DATA "" DATA " SCROLL BACKGROUND:" DATA "" DATA " - DISPLAY BG,X,Y" DATA "]" DATA "********************" DATA " ASSETS" DATA "********************" DATA "" DATA " BACKGROUND" DATA " DESIGNER" DATA "" DATA " - PRIORITIES" DATA "]" DATA "********************" DATA " MORE" DATA "********************" DATA "" DATA " DOCS/" DATA " INTRODUCTION.TXT" DATA "" DATA " TWITTER:" DATA " @TIMO_INUTILIS" DATA "]" DATA "]" ================================================ FILE: programs test/Sprites with Background 0.3.nx ================================================ '** SPRITES WITH BACKGROUND 0.3 ** 'COPY MAP TO BACKGROUND BG COPY 0,0,20,16 TO 0,0 'INIT SPRITE PALETTES AND SIZES SPRITE.A 0,(6,,,,0) SPRITE.A 1,(6,,,,3) PX=76 PY=88 MX=64 MY=8 GAMEPAD 1 DO TICK=TICK+1 'PLAYER CONTROL IF UP(0) AND PY>0 THEN PY=PY-1 IF DOWN(0) AND PY<120 THEN PY=PY+1 IF LEFT(0) AND PX>0 THEN PX=PX-1 SPRITE.A 0,(,1) END IF IF RIGHT(0) AND PX<152 THEN PX=PX+1 SPRITE.A 0,(,0) END IF 'PLAYER ANIMATION IF UP(0) OR DOWN(0) OR LEFT(0) OR RIGHT(0) THEN FRAME=34+(TICK/4) MOD 3 ELSE FRAME=32+(TICK/16) MOD 2 END IF SPRITE 0,PX,PY,FRAME 'MONSTER UPDATE IF TICK MOD 40=0 THEN MY=MY+1 SPRITE.A 1,(,0) END IF IF TICK MOD 40=20 THEN SPRITE.A 1,(,1) SPRITE 1,MX,MY,12 WAIT 1 LOOP #1:MAIN PALETTES 182D0424003D3824003F2A14003F1B06 00242A150034202A003E3414003F2A15 #2:MAIN CHARACTERS 00000000000000000000000000000000 00000000000000000808400200202101 030F1E1F3B3F2E05000001000400113A C0E0C060E8C090400010389C143C6CBC 4200210008004200FDFFDFFFFFFFFFFF BBBBBBBBBBBBBBBB4444444444444444 BBBFA7BFFFFFFFBB447C7C7C7E7E7E44 00000000000000000000000000000000 001870E0C141861C3C668F1F3FBFFE1C 1E2359D0B0A00141FFFFE7EFCFDFFFBF 0098F4E603C1F010FFFFEFDFFFFFFFFF 00000000000000000000000000000000 00000000000000000000000000000000 0000000000000703000003070F0F1F1D 00000000000070600000E0F0F8F8FCDC 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000001408800200A04404 1200010003070F072D3F1E0F03070F07 80000000C0E0F0E07CFCF8F0C0E0F0E0 FE8181818181817F017F7F7F7F7F7FFF 11FF22FF88FF22FFFFFFFFFFFFFFFFFF 7FFF7EFFFEFF7EFFFF818181B1A18181 11FF7EFFFEFF22FFFF81BDBD81FFFFFF 00400060000401000020F06000020F00 02325E4EA4C08080FFFFFFFFDFBFFFFF D88C0486030301013F7FFF7FFFFFFFFF 00000000000000000000000000000000 00000000000103020000000107060C0D 00000B09048301000F0F0FFEFF7FFFFF 0000E84810E0C000F8F8F8BFFFFFFFFF 000000000000804000000080E0E070B0 30300000007C286C3000387C7C38286C 18180000007C28681800387C7C382868 1818000004384A641800183838384A64 18180000107850181800183828785018 18180000103C283018001838283C2830 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000202000000000F0F0F0F07070F0F 0000202008078080FFFFFFFFBFBF9F9F 000002020A700101FFFFFFFFFFFDFDF9 0000404000000000F0F0F0F0F0F0F0F0 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000000000000000000000000000 00000000040503000F0F0F1F1F1F1F0E 00040203023240523F7F7F7F3E3E7E2E 002040C0440E014AFCFEFEFE7C7E7F74 0000004040A0A040E0F0F0F8F8FCF870 #3:MAIN BG 00001410000000000000000018020000 02200320010001000000080200001802 00000000000000000220032018020000 02200320010001001200130001000100 11000100000001000100000000000000 12001300000008021200130011000100 00000100000001000100010001000100 02200320000008020100000000000100 00000000000018020000010000000000 00000100000000001200130000000000 01000000180200000100000008020000 00000000000001000000010001000100 01000100110000000100180200000220 03200000010011000000000000000000 01000100000001000100110001000100 00000000000012001300000000000100 01000000000001000000000000000000 00000000010001000100000000000000 00000100000000000000010000000100 00000000000000000000010000000100 00000000010001000000000001000000 01000000000000000000000011000000 00000100010000000000180218020100 01000000000000000000000000000100 01000000000000000000010001000000 00000000000000000100010001000000 00000000000000000100010001000100 00000000010011000802000008020000 01000100110001000000000000000000 00000000010011000100180200000100 01000000000018020000000001000100 01000000000000000100000000000100 08020100000002200320010000000100 01000220032000000100180200000000 01000100000018020000000000001200 13000100000002200320120013000000 00000100010000000000010001000000 02200320010001000100000000001200 13001100010018020000000001000802 18020000000001001200130000000100 01001802 ================================================ FILE: programs test/Subs 1.nx ================================================ CALL HELLO CALL GREET("LOWRES",3) NUM=10 CALL INCR(NUM,10) PRINT NUM DIM ARR(10) ARR(2)=5 CALL INCR(ARR(2),5) PRINT ARR(2) SUB HELLO PRINT "HELLO" END SUB SUB GREET(NAME$,COUNT) FOR I=1 TO COUNT PRINT "HI",NAME$ NEXT I END SUB SUB INCR(X,Y) X=X+Y END SUB ================================================ FILE: programs test/Subs 2.nx ================================================ DIM GLOBAL ARR(7) CALL RANDOM(ARR()) CALL PRINTARR SUB RANDOM(X()) FOR I=0 TO 7 X(I)=INT(RND*100) NEXT I END SUB SUB PRINTARR FOR I=0 TO 7 PRINT ARR(I) NEXT I END SUB ================================================ FILE: programs test/drawing.nx ================================================ REM ** FILL DRAWING AREA WITH CHARACTERS I=8 ATTR (0,0,0,0,0) FOR CY=0 TO 8 FOR CX=0 TO 19 CELL CX,CY+3,I I=I+1 NEXT CX NEXT CY REM ** SOME TEXT TEXT 0,0, " DRAWING WITH TILES?" TEXT 0,15," NO PROBLEM!" REM ** ANIMATE! GLOBAL RY,I I=0 ON RASTER CALL RASTFX DO FILL $8080,2880,0 CALL DRAWSINE(40+SIN(I*0.02)*20,0) CALL DRAWSINE(30+SIN(I*0.03)*10,1) I=I+1 RY=INT(48-SIN(I*0.04)*48) WAIT VBL LOOP SUB RASTFX REM ** SCROLL TOP AND BOTTOM TEXT LINE IF RASTER<8 OR RASTER>=120 THEN SCROLL 0,I,0 ELSE SCROLL 0,0,0 REM ** PALETTE FX IF RASTER=0 OR RASTER=RY+33 THEN PALETTE 0,%000001,%110000,%001100,%110100 IF RASTER=RY THEN PALETTE 0,%000010,%001111,%110011,%111100 END SUB SUB DRAWSINE(DIV,BP) FOR X=0 TO 159 Y=36+SIN(X*PI/DIV)*35 REM ** GET CHARACTER CX=INT(X/8) CY=INT(Y/8) CI=CY*20+CX REM ** GET BIT BI=2^(7-(X MOD 8)) REM ** WRITE IN CHARACTER RAM A=$8080+CI*16+(Y MOD 8)+BP*8 POKE A,PEEK(A) OR BI NEXT X END SUB ================================================ FILE: programs test/gamepad.nx ================================================ REM INIT SPRITES SPRITE.A 0,(6,,,,1) SPRITE.A 1,(1,,,,1) DIM X(1),Y(1) X(0)=60 Y(0)=64 X(1)=100 Y(1)=64 GAMEPAD 2 DO FOR I=0 TO 1 SPRITE I,,,130 IF UP(I) THEN Y(I)=Y(I)-1 IF DOWN(I) THEN Y(I)=Y(I)+1 IF LEFT(I) THEN X(I)=X(I)-1 IF RIGHT(I) THEN X(I)=X(I)+1 IF BUTTON(I,0) THEN SPRITE I,,,132 IF BUTTON(I,1) THEN SPRITE I,,,134 SPRITE I,X(I)+24,Y(I)+24, NEXT I WAIT VBL LOOP #1:MAIN PALETTES 07 10 24 38 00 04 08 0C 00 07 0B 0F 00 1B 2F 3F 00 34 38 3C 00 10 20 30 00 10 30 39 00 3F 00 00 #2:MAIN CHARACTERS 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 04 00 00 44 00 02 FF DF FF FF FF FB FF FF FF FF FF 77 22 00 88 DD FF FF FF FF FF FF 77 22 C0 E0 E8 E8 E2 C8 E0 C2 FF FF FF FF FF FF FF FF 01 09 4B 0B 03 0B 01 23 FE F6 B4 F4 FC F4 FE DC 03 0C 12 20 40 44 80 80 03 0F 1D 3F 7F 7B FF FF 80 82 40 48 20 10 0C 03 7F 7D 3F 37 1F 0F 03 00 00 02 00 00 10 00 00 00 FF FD FF FF EF FF FF FF 00 83 44 38 00 83 44 38 FF 7C BB C7 FF 7C BB C7 00 83 44 38 00 83 44 38 00 83 C7 FF FF 7C BB C7 08 08 08 08 08 8B 44 28 10 10 10 10 D3 74 38 10 08 08 08 08 08 08 08 08 10 10 12 14 10 50 30 10 30 38 27 43 C2 E4 1C 18 30 38 3F 7F FE FC 1C 18 00 00 00 00 00 10 44 55 00 00 00 00 00 10 44 55 7E 81 A3 81 89 85 A1 7E 7E FE FE FE FE FA DE 00 10 22 0C 38 70 C4 08 02 EF DF FF FF FF FB F7 FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 0C 10 20 40 40 80 80 03 0F 1F 3F 7F 7F FF FF 80 80 40 40 20 10 0C 03 7F 7F 3F 3F 1F 0F 03 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 03 04 04 18 20 20 40 80 03 07 07 1F 3F 3F 7F FF 80 40 30 08 04 02 02 01 00 80 C0 F0 F8 FC FC FE 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 73 00 E6 00 70 02 38 FF FC FF 1F FF FF FF C7 00 80 18 00 01 0C 20 00 FF 7F FF FF FF F3 FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 07 07 0F 1F 1F 07 05 00 00 01 04 07 07 03 02 E0 F8 F0 FC FC F8 F0 C0 00 00 B0 FC DC 80 F0 20 00 03 07 07 0F 1F 1F 07 00 00 00 01 04 07 07 03 00 E0 F8 F0 FC FC F8 F0 00 00 00 B0 FC DC 80 F0 00 03 07 07 0F 1F 1F 07 00 00 00 01 04 07 07 03 00 E0 F8 F0 FC FC F8 F0 00 00 00 B0 FC DC 80 F0 07 0F 0F 1F 3F 3F 0F 1F 00 00 03 09 0F 0F 07 00 C0 F0 E0 F8 F8 F0 E0 40 00 00 60 F8 B8 00 E0 80 00 07 0F 0F 1F 3F 3F 0F 00 00 00 03 09 0F 0F 03 0E DE FE FE FE FC FC F8 0E 16 0E 60 F8 B8 00 E0 00 00 00 00 00 03 07 07 00 00 00 00 00 00 00 01 00 00 00 00 00 E0 F8 F0 00 00 00 00 00 00 00 B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0D 1D 1E 1C 0C 00 0E 1E 02 02 03 1F 0F 0F 00 00 D0 D8 3C 1C 18 00 38 3C 20 20 E0 FC F8 78 00 00 0F 1F 38 30 18 18 18 0C 00 00 3F 37 07 07 00 00 5C 5C 98 38 38 38 00 00 BC BC F0 C0 C0 80 00 00 0B 13 03 30 30 30 20 00 04 0C 1C 0F 0F 0C 00 00 00 D8 FC FC 80 00 70 78 C0 18 1C 1C 60 E0 00 00 17 07 00 00 01 05 0E 07 08 18 1F 0F 0E 02 00 00 C0 E0 60 00 80 C0 40 00 40 60 E0 E0 60 00 00 00 3E 3E 3C 1C 66 63 60 40 01 09 1F 1B 19 1C 1E 00 B0 A0 82 06 06 86 00 00 40 50 F0 F8 F8 78 00 00 0F 0F 0F 1B 1F 1C 0E 1E 04 07 03 07 06 0F 01 00 FC FC F8 FC FC 0C 1C 1E FC DC 80 F0 28 FC E0 00 ================================================ FILE: programs test/hello world.nx ================================================ PRINT "HELLO WORLD" GAMEPAD 1 BG 1 BG COPY 0,0,32,32 TO 0,0 X=32 Y=32 DO BGX=BGX+1 SCROLL 1,BGX,0 IF UP(0) THEN Y=Y-1 IF DOWN(0) THEN Y=Y+1 IF LEFT(0) THEN X=X-1 IF RIGHT(0) THEN X=X+1 SPRITE 0,X,Y,TIMER/8 MOD 2 + 1 WAIT VBL LOOP #1:MAIN PALETTES 05 3F 2F 00 00 38 34 00 00 0C 24 10 00 3F 2A 15 00 00 00 00 00 00 00 00 00 00 00 00 00 3F 2A 15 #2:MAIN CHARACTERS 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 18 18 81 00 00 18 18 3C 18 00 7E 18 18 18 18 3C 18 18 00 00 00 99 18 3C 18 00 18 3C 5A 18 18 3C FF FF 5E 04 00 44 FF FF 00 00 A1 FB FF FF FF FF 3C 3C 3C 3C 3C 3C 3C 3C 3C 3C 3C 3C 3C 3C 3C 3C 3C 7E FF FF FF FF FE 7C 00 00 00 00 00 00 00 00 10 08 08 88 49 2E 1C 1C 00 00 00 00 00 00 00 00 #3:MAIN BG 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 02 00 00 00 00 05 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 02 03 02 03 02 03 02 03 02 03 02 00 00 00 00 00 00 00 00 00 00 05 22 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 02 03 02 03 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 22 03 22 03 22 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 22 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 02 00 00 06 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 22 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 02 03 02 03 02 03 02 03 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 22 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 02 06 02 00 00 00 00 00 00 00 00 00 00 05 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 22 03 22 03 22 03 22 00 00 00 00 00 00 00 00 00 00 05 22 00 00 03 02 03 02 03 02 03 02 00 00 00 00 00 00 00 00 05 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 22 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 22 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 2A 00 00 04 23 00 00 06 2A 00 00 00 00 00 00 00 00 06 12 00 00 04 02 00 00 06 1A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 22 00 00 00 00 00 00 00 00 00 00 00 00 03 22 03 22 03 22 03 22 03 22 03 22 03 22 00 00 00 00 00 00 03 02 03 02 03 02 03 02 03 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 22 03 22 03 22 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 03 03 03 03 03 03 03 00 00 00 00 00 00 00 00 00 00 00 00 03 03 03 03 03 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ================================================ FILE: programs test/sprite collision.nx ================================================ PALETTE 1,0,4,8,16 REM INIT SPRITES SPRITE.A 0,(0,1,0,,3) SPRITE.A 1,(1,0,1,,3) SPRITE.A 2,(1,0,0,,0) SPRITE.A 3,(1,0,0,,0) DIM X(1),Y(1) X=42 Y=32 SPRITE 0,X,Y,3 SPRITE 1,88,48,3 SPRITE 2,18,38,1 SPRITE 3,128,98,2 GAMEPAD 1 DO IF UP(0) THEN Y=Y-1 IF DOWN(0) THEN Y=Y+1 IF LEFT(0) THEN X=X-1 IF RIGHT(0) THEN X=X+1 SPRITE 0,X,Y, IF SPRITE HIT(0) THEN TEXT 0,0,"HIT "+STR$(HIT) ELSE TEXT 0,0," " WAIT VBL LOOP #2:MAIN CHARACTERS 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 1C 3E FF 00 60 60 60 08 1C 3E 00 FF 60 60 60 D8 08 00 08 08 09 00 01 E0 00 08 08 08 0E 01 01 80 60 38 1F 0B 09 08 08 00 00 00 00 04 06 07 06 00 00 00 81 C3 77 7C 00 00 00 00 00 00 80 83 FF 00 00 F0 00 00 00 00 00 00 00 00 FF FF 81 01 00 00 00 00 00 00 00 00 00 00 00 00 00 C0 E0 F8 7C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 10 18 0E 02 03 01 01 0C 0C 06 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 80 C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 02 1E 0F 07 03 03 03 03 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 03 06 38 20 20 60 40 00 00 01 07 1C 10 10 20 00 00 00 00 00 00 00 00 C0 80 00 00 00 00 00 00 00 00 00 00 00 03 07 06 00 00 00 00 00 03 07 06 06 0E 1C 3C F8 F8 F0 00 06 0E 1C 3C F8 F8 F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 60 60 30 38 1F 07 00 00 00 00 00 00 00 00 00 00 00 00 00 C0 E0 F0 1C 0F 06 0E 1C 1C 1C 38 F0 E0 06 0E 1C 1C 1C 38 F0 E0 ================================================ FILE: programs test/touch.nx ================================================ REM INIT SPRITE FOR I=0 TO 15 SPRITE I,-32,-32,130 SPRITE.A I,(INT(I/2),,,,1) NEXT I DO FOR I=15 TO 1 STEP -1 SPRITE I,SPRITE.X(I-1),SPRITE.Y(I-1), NEXT I IF TOUCH THEN TEXT 0,0,"DRAG!" SPRITE 0,TOUCH.X-8,TOUCH.Y-8, ELSE TEXT 0,0,"TOUCH" SPRITE OFF 0 END IF WAIT VBL LOOP #1: MAIN PALETTES 07 10 24 38 00 04 08 0C 00 07 0B 0F 00 1B 2F 3F 00 34 38 3C 00 10 20 30 00 10 30 39 00 3F 00 00 #2: MAIN CHARACTERS 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 04 00 00 44 00 02 FF DF FF FF FF FB FF FF FF FF FF 77 22 00 88 DD FF FF FF FF FF FF 77 22 C0 E0 E8 E8 E2 C8 E0 C2 FF FF FF FF FF FF FF FF 01 09 4B 0B 03 0B 01 23 FE F6 B4 F4 FC F4 FE DC 03 0C 12 20 40 44 80 80 03 0F 1D 3F 7F 7B FF FF 80 82 40 48 20 10 0C 03 7F 7D 3F 37 1F 0F 03 00 00 02 00 00 10 00 00 00 FF FD FF FF EF FF FF FF 00 83 44 38 00 83 44 38 FF 7C BB C7 FF 7C BB C7 00 83 44 38 00 83 44 38 00 83 C7 FF FF 7C BB C7 08 08 08 08 08 8B 44 28 10 10 10 10 D3 74 38 10 08 08 08 08 08 08 08 08 10 10 12 14 10 50 30 10 30 38 27 43 C2 E4 1C 18 30 38 3F 7F FE FC 1C 18 00 00 00 00 00 10 44 55 00 00 00 00 00 10 44 55 7E 81 A3 81 89 85 A1 7E 7E FE FE FE FE FA DE 00 10 22 0C 38 70 C4 08 02 EF DF FF FF FF FB F7 FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 0C 10 20 40 40 80 80 03 0F 1F 3F 7F 7F FF FF 80 80 40 40 20 10 0C 03 7F 7F 3F 3F 1F 0F 03 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 03 04 04 18 20 20 40 80 03 07 07 1F 3F 3F 7F FF 80 40 30 08 04 02 02 01 00 80 C0 F0 F8 FC FC FE 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 73 00 E6 00 70 02 38 FF FC FF 1F FF FF FF C7 00 80 18 00 01 0C 20 00 FF 7F FF FF FF F3 FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 07 07 0F 1F 1F 07 05 00 00 01 04 07 07 03 02 E0 F8 F0 FC FC F8 F0 C0 00 00 B0 FC DC 80 F0 20 00 03 07 07 0F 1F 1F 07 00 00 00 01 04 07 07 03 00 E0 F8 F0 FC FC F8 F0 00 00 00 B0 FC DC 80 F0 00 03 07 07 0F 1F 1F 07 00 00 00 01 04 07 07 03 00 E0 F8 F0 FC FC F8 F0 00 00 00 B0 FC DC 80 F0 07 0F 0F 1F 3F 3F 0F 1F 00 00 03 09 0F 0F 07 00 C0 F0 E0 F8 F8 F0 E0 40 00 00 60 F8 B8 00 E0 80 00 07 0F 0F 1F 3F 3F 0F 00 00 00 03 09 0F 0F 03 0E DE FE FE FE FC FC F8 0E 16 0E 60 F8 B8 00 E0 00 00 00 00 00 03 07 07 00 00 00 00 00 00 00 01 00 00 00 00 00 E0 F8 F0 00 00 00 00 00 00 00 B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0D 1D 1E 1C 0C 00 0E 1E 02 02 03 1F 0F 0F 00 00 D0 D8 3C 1C 18 00 38 3C 20 20 E0 FC F8 78 00 00 0F 1F 38 30 18 18 18 0C 00 00 3F 37 07 07 00 00 5C 5C 98 38 38 38 00 00 BC BC F0 C0 C0 80 00 00 0B 13 03 30 30 30 20 00 04 0C 1C 0F 0F 0C 00 00 00 D8 FC FC 80 00 70 78 C0 18 1C 1C 60 E0 00 00 17 07 00 00 01 05 0E 07 08 18 1F 0F 0E 02 00 00 C0 E0 60 00 80 C0 40 00 40 60 E0 E0 60 00 00 00 3E 3E 3C 1C 66 63 60 40 01 09 1F 1B 19 1C 1E 00 B0 A0 82 06 06 86 00 00 40 50 F0 F8 F8 78 00 00 0F 0F 0F 1B 1F 1C 0E 1E 04 07 03 07 06 0F 01 00 FC FC F8 FC FC 0C 1C 1E FC DC 80 F0 28 FC E0 00 ================================================ FILE: scripts/export_characters.py ================================================ import sys from PIL import Image if len(sys.argv) >= 2: filename = sys.argv[1] else: filename = "../assets/characters.png" im = Image.open(filename) print im.format, im.size, im.mode print "{", for row in range(16): for column in range(16): print "{", for bit in range(2): for charY in range(8): y = row*8+charY val = 0 for charX in range(8): pixel = im.getpixel((column*8+charX, y))[0] / 64 pcolor = 0 if pixel > 0: pcolor = 4 - pixel pbit = (pcolor >> bit) & 0x01 val |= (pbit << (7-charX)) print str(val & 0xFF)+ ",", print "}," print "}" ================================================ FILE: scripts/export_characters_hex.py ================================================ import sys from PIL import Image if len(sys.argv) >= 2: filename = sys.argv[1] else: filename = "../assets/characters.png" im = Image.open(filename) print im.format, im.size, im.mode for row in range(16): for column in range(16): for bit in range(2): for charY in range(8): y = row*8+charY val = 0 for charX in range(8): pixel = im.getpixel((column*8+charX, y))[0] / 64 pcolor = 0 if pixel > 0: pcolor = 4 - pixel pbit = (pcolor >> bit) & 0x01 val |= (pbit << (7-charX)) print "%0.2X" % (val & 0xFF), print "" ================================================ FILE: sdl/config.h ================================================ // // Copyright 2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef config_h #define config_h #ifdef __EMSCRIPTEN__ #define DEV_MENU 0 #define SCREENSHOTS 0 #define HOT_KEYS 0 #define SETTINGS_FILE 0 #else #define DEV_MENU 1 #define SCREENSHOTS 1 #define HOT_KEYS 1 #define SETTINGS_FILE 1 #endif #endif /* config_h */ ================================================ FILE: sdl/dev_menu.c ================================================ // // Copyright 2017-2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "config.h" #if DEV_MENU #include "dev_menu.h" #include "main.h" #include "dev_menu_data.h" #include "text_lib.h" #include "string_utils.h" #include "system_paths.h" #include "utils.h" #include "sdl_include.h" #include #include #include #define MENU_SIZE 5 struct DevButton { int cx; int cy; }; struct DevButton devButtons[] = { {1,4}, {3,4}, {5,4}, {7,4}, {9,4}, {17,4} }; void dev_showInfo(struct DevMenu *devMenu); void dev_showError(struct DevMenu *devMenu, struct CoreError error); void dev_updateButtons(struct DevMenu *devMenu); void dev_onButtonTap(struct DevMenu *devMenu); void dev_showToolsMenu(struct DevMenu *devMenu); void dev_showClearRamMenu(struct DevMenu *devMenu); void dev_showMenu(struct DevMenu *devMenu, const char *message, const char *buttons[], int numButtons, int numRemoveButtons); void dev_clearPersistentRam(struct DevMenu *devMenu); void dev_init(struct DevMenu *devMenu, struct Runner *runner, struct Settings *settings) { memset(devMenu, 0, sizeof(struct DevMenu)); devMenu->runner = runner; devMenu->settings = settings; } void dev_show(struct DevMenu *devMenu, bool reload) { if (reload) { devMenu->lastError = runner_loadProgram(devMenu->runner, getMainProgramFilename()); } devMenu->currentMenu = DevModeMenuMain; devMenu->currentButton = -1; devMenu->lastTouch = false; struct Core *core = devMenu->runner->core; struct TextLib *textLib = &devMenu->textLib; textLib->core = core; itp_endProgram(core); machine_reset(core, true); overlay_reset(core); core->machine->ioRegisters.attr.touchEnabled = 1; core->machineInternals->isEnergySaving = true; txtlib_clearScreen(textLib); textLib->fontCharOffset = 192; textLib->windowY = 7; textLib->windowHeight = 9; memcpy(&core->machine->colorRegisters, dev_colors, sizeof(dev_colors)); memcpy(&core->machine->videoRam.characters, dev_characters, sizeof(dev_characters)); memcpy(&core->machine->cartridgeRom, dev_bg, sizeof(dev_bg)); textLib->sourceAddress = 4; textLib->sourceWidth = core->machine->cartridgeRom[2]; txtlib_copyBackground(textLib, 0, 0, 20, 16, 0, 0); dev_updateButtons(devMenu); textLib->charAttr.palette = 1; txtlib_writeText(textLib, "DEVELOPMENT MENU", 2, 0); textLib->charAttr.palette = 0; char progName[19]; displayName(getMainProgramFilename(), progName, 19); txtlib_writeText(textLib, progName, 1, 2); if (devMenu->lastError.code != ErrorNone) { dev_showError(devMenu, devMenu->lastError); } else { dev_showInfo(devMenu); } setMouseEnabled(true); } void dev_update(struct DevMenu *devMenu, struct CoreInput *input) { struct Core *core = devMenu->runner->core; struct TextLib *textLib = &devMenu->textLib; core_handleInput(core, input); bool touch = core->machine->ioRegisters.status.touch; int cx = core->machine->ioRegisters.touchX / 8; int cy = core->machine->ioRegisters.touchY / 8; if (devMenu->currentMenu == DevModeMenuMain) { if (devMenu->currentButton >= 0) { int bcx = devButtons[devMenu->currentButton].cx; int bcy = devButtons[devMenu->currentButton].cy; bool isInside = (cx >= bcx && cy >= bcy && cx <= bcx + 1 && cy <= bcy + 1); if (!touch || !isInside) { txtlib_setCellsAttr(textLib, bcx, bcy, bcx + 1, bcy + 1, 0, -1, -1, -1); if (isInside) { dev_onButtonTap(devMenu); } devMenu->currentButton = -1; } } else if (touch && !devMenu->lastTouch) { for (int i = 0; i < 6; i++) { int bcx = devButtons[i].cx; int bcy = devButtons[i].cy; if (cx >= bcx && cy >= bcy && cx <= bcx + 1 && cy <= bcy + 1) { txtlib_setCellsAttr(textLib, bcx, bcy, bcx + 1, bcy + 1, 1, -1, -1, -1); devMenu->currentButton = i; } } } } else { if (devMenu->currentButton >= 0) { int bcy = 1 + devMenu->currentButton * 3; bool isInside = (cy >= bcy && cy <= bcy + 2); if (!touch || !isInside) { txtlib_setCellsAttr(textLib, 0, bcy, 19, bcy + 2, 0, -1, -1, -1); if (isInside) { dev_onButtonTap(devMenu); } devMenu->currentButton = -1; } } else if (touch && !devMenu->lastTouch) { int button = (cy - 1) / 3; if (button >= 0 && button < devMenu->currentMenuSize) { int bcy = 1 + button * 3; txtlib_setCellsAttr(textLib, 0, bcy, 19, bcy + 2, 1, -1, -1, -1); devMenu->currentButton = button; } } } devMenu->lastTouch = core->machine->ioRegisters.status.touch; overlay_draw(core, false); } bool dev_handleDropFile(struct DevMenu *devMenu, const char *filename) { if (devMenu->currentMenu == DevModeMenuTools) { if (settings_addTool(devMenu->settings, filename)) { settings_save(devMenu->settings); dev_showToolsMenu(devMenu); } else { overlay_message(devMenu->runner->core, "NO EMPTY SPACE"); } return true; } return false; } void dev_showInfo(struct DevMenu *devMenu) { struct Core *core = devMenu->runner->core; struct TextLib *textLib = &devMenu->textLib; char info[21]; textLib->charAttr.palette = 5; txtlib_writeText(textLib, "TOKENS:", 0, 7); txtlib_writeText(textLib, "ROM:", 0, 8); textLib->charAttr.palette = 0; sprintf(info, "%d/%d", core->interpreter->tokenizer.numTokens, MAX_TOKENS); txtlib_writeText(textLib, info, 20 - (int)strlen(info), 7); sprintf(info, "%d/%d", data_currentSize(&core->interpreter->romDataManager), DATA_SIZE); txtlib_writeText(textLib, info, 20 - (int)strlen(info), 8); textLib->charAttr.palette = 4; txtlib_writeText(textLib, "READY TO RUN", 4, 14); } void dev_showError(struct DevMenu *devMenu, struct CoreError error) { struct Core *core = devMenu->runner->core; struct TextLib *textLib = &devMenu->textLib; textLib->charAttr.palette = 0; txtlib_clearWindow(textLib); textLib->charAttr.palette = 2; txtlib_printText(textLib, err_getString(error.code)); txtlib_printText(textLib, "\n"); if (error.sourcePosition >= 0 && core->interpreter->sourceCode) { textLib->charAttr.palette = 0; int number = lineNumber(core->interpreter->sourceCode, error.sourcePosition); char lineNumberText[30]; sprintf(lineNumberText, "IN LINE %d:\n", number); txtlib_printText(textLib, lineNumberText); const char *line = lineString(core->interpreter->sourceCode, error.sourcePosition); if (line) { textLib->charAttr.palette = 5; txtlib_printText(textLib, "\n"); txtlib_printText(textLib, line); free((void *)line); } } } void dev_updateButtons(struct DevMenu *devMenu) { struct Plane *bg = &devMenu->runner->core->machine->videoRam.planeA; if (devMenu->runner->core->interpreter->debug) { bg->cells[5][7].character = 30; bg->cells[5][8].character = 31; } else { bg->cells[5][7].character = 46; bg->cells[5][8].character = 47; } } void dev_onButtonTap(struct DevMenu *devMenu) { int button = devMenu->currentButton; if (devMenu->currentMenu == DevModeMenuMain) { if (button == 0) { // Run runMainProgram(); } else if (button == 1) { // Check dev_show(devMenu, true); } else if (button == 2) { dev_showToolsMenu(devMenu); } else if (button == 3) { // Debug On/Off devMenu->runner->core->interpreter->debug = !devMenu->runner->core->interpreter->debug; dev_updateButtons(devMenu); } else if (button == 4) { dev_showClearRamMenu(devMenu); } else if (button == 5) { // Eject rebootNX(); } } else if (devMenu->currentMenu == DevModeMenuTools) { if (devMenu->currentButton < devMenu->settings->numTools) { int cx = devMenu->runner->core->machine->ioRegisters.touchX / 8; if (cx >= 18) { settings_removeTool(devMenu->settings, devMenu->currentButton); settings_save(devMenu->settings); dev_showToolsMenu(devMenu); } else { runToolProgram(devMenu->settings->tools[devMenu->currentButton]); } } else { dev_show(devMenu, false); } } else if (devMenu->currentMenu == DevModeMenuClearRam) { if (devMenu->currentButton == 0) { dev_clearPersistentRam(devMenu); } dev_show(devMenu, false); } } void dev_showToolsMenu(struct DevMenu *devMenu) { struct TextLib *textLib = &devMenu->textLib; devMenu->currentMenu = DevModeMenuTools; const char *menu[MENU_SIZE]; int count = 0; for (int i = 0; i < devMenu->settings->numTools; i++) { menu[count++] = devMenu->settings->toolNames[i]; } menu[count++] = "CANCEL"; dev_showMenu(devMenu, "EDIT ROM WITH TOOL", menu, count, count - 1); if (count < MENU_SIZE) { textLib->charAttr.palette = 5; txtlib_writeText(textLib, "DRAG & DROP PROGRAM", 0, 14); txtlib_writeText(textLib, "TO ADD AS TOOL", 3, 15); } } void dev_showClearRamMenu(struct DevMenu *devMenu) { struct TextLib *textLib = &devMenu->textLib; devMenu->currentMenu = DevModeMenuClearRam; const char *menu[MENU_SIZE]; menu[0] = "CLEAR"; menu[1] = "CANCEL"; dev_showMenu(devMenu, "CLEAR PERSIST. RAM?", menu, 2, 0); textLib->charAttr.palette = 5; txtlib_writeText(textLib, "MAY DELETE DATA LIKE", 0, 12); txtlib_writeText(textLib, "GAME STATE OR", 3, 13); txtlib_writeText(textLib, "HIGH SCORES", 4, 14); txtlib_writeText(textLib, "OF THIS PROGRAM", 2, 15); } void dev_showMenu(struct DevMenu *devMenu, const char *message, const char *buttons[], int numButtons, int numRemoveButtons) { struct TextLib *textLib = &devMenu->textLib; textLib->charAttr.palette = 0; txtlib_setCells(textLib, 0, 0, 19, 15, 1); textLib->charAttr.palette = 1; txtlib_setCells(textLib, 0, 0, 19, 0, 192); txtlib_writeText(textLib, message, (int)(20 - strlen(message))/2, 0); textLib->charAttr.palette = 0; for (int i = 0; i < numButtons; i++) { int y = 1 + i * 3; txtlib_setCells(textLib, 0, y, 19, y, 3); txtlib_setCells(textLib, 0, y + 2, 19, y + 2, 5); int tx = (int)(20 - strlen(buttons[i])) / 2; if (tx < 0) tx = 0; txtlib_writeText(textLib, buttons[i], tx, y + 1); if (i < numRemoveButtons) { txtlib_setCell(textLib, 19, y, 20); } } devMenu->currentMenuSize = numButtons; } void dev_clearPersistentRam(struct DevMenu *devMenu) { char ramFilename[FILENAME_MAX]; getRamFilename(ramFilename); remove(ramFilename); } #endif ================================================ FILE: sdl/dev_menu.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef dev_menu_h #define dev_menu_h #include "config.h" #if DEV_MENU #include #include #include "core.h" #include "settings.h" #include "runner.h" #include "text_lib.h" enum DevModeMenu { DevModeMenuMain, DevModeMenuTools, DevModeMenuClearRam }; struct DevMenu { struct Runner *runner; struct Settings *settings; bool lastTouch; enum DevModeMenu currentMenu; int currentButton; int currentMenuSize; struct CoreError lastError; struct TextLib textLib; }; void dev_init(struct DevMenu *devMenu, struct Runner *runner, struct Settings *settings); void dev_show(struct DevMenu *devMenu, bool reload); void dev_update(struct DevMenu *devMenu, struct CoreInput *input); bool dev_handleDropFile(struct DevMenu *devMenu, const char *filename); #endif #endif /* dev_menu_h */ ================================================ FILE: sdl/dev_menu_data.h ================================================ // // Copyright 2017 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef dev_menu_data_h #define dev_menu_data_h #include "config.h" #if DEV_MENU const char dev_colors[] = { 0x05, 0x3F, 0x2A, 0x15, 0x00, 0x0F, 0x05, 0x00, 0x3C, 0x30, 0x2A, 0x00, 0x00, 0x3C, 0x2A, 0x00, 0x00, 0x08, 0x2A, 0x15, 0x00, 0x15, 0x2A, 0x15, 0x00, 0x3F, 0x2A, 0x15, 0x00, 0x3F, 0x2A, 0x15, }; const char dev_characters[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xB2, 0xAA, 0xB2, 0xA9, 0x80, 0x80, 0x00, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFE, 0x01, 0xB1, 0xA9, 0xA9, 0xA9, 0x01, 0x61, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xBB, 0xB2, 0xA2, 0xBB, 0x80, 0x80, 0x00, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFE, 0x01, 0x01, 0x81, 0x81, 0x01, 0x01, 0x01, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x9A, 0xA3, 0xA2, 0x9A, 0x80, 0x80, 0x00, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFE, 0x01, 0xA9, 0xB1, 0xA9, 0xA9, 0x01, 0x0D, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xBB, 0xB1, 0xA1, 0xBA, 0x80, 0x80, 0x00, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFE, 0x01, 0x65, 0x69, 0x49, 0x65, 0x01, 0x21, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xB3, 0xAB, 0xAA, 0xB3, 0x80, 0x81, 0x00, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFE, 0x01, 0xB1, 0x31, 0x29, 0xB1, 0x01, 0xF9, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0x71, 0x79, 0x7D, 0x79, 0x71, 0x61, 0x01, 0xFF, 0xDF, 0xCF, 0xC7, 0xCF, 0xDF, 0xFF, 0xFF, 0xFF, 0x80, 0x9C, 0xBE, 0xBE, 0xBE, 0x94, 0x80, 0x7F, 0x7F, 0x63, 0x55, 0x41, 0x41, 0x6B, 0x7F, 0xFF, 0x0D, 0x1D, 0x39, 0x71, 0xE1, 0xC1, 0x01, 0xFF, 0xFF, 0xF7, 0xEF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x81, 0x83, 0x81, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x7F, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0x1D, 0x39, 0xB9, 0xF1, 0xF1, 0x61, 0x01, 0xFF, 0xF7, 0xEF, 0xEF, 0x5F, 0x9F, 0xFF, 0xFF, 0xFF, 0x80, 0x80, 0x81, 0x81, 0x80, 0x81, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0x71, 0xF9, 0xFD, 0xFD, 0x01, 0xFD, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0x83, 0x83, 0x83, 0x83, 0x81, 0x80, 0x7F, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0xFF, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xF9, 0x01, 0xFF, 0x07, 0x17, 0xA7, 0x47, 0x07, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x9A, 0xA2, 0xA2, 0x9B, 0x80, 0xFF, 0x00, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFE, 0x01, 0x31, 0x29, 0x31, 0xA9, 0x01, 0xFD, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0x05, 0x05, 0x05, 0x05, 0x05, 0xF9, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xD5, 0xCC, 0xD5, 0xFF, 0xAA, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x55, 0x7F, 0xFF, 0xC5, 0x45, 0x55, 0x55, 0xFD, 0xA9, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x57, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00, 0xFF, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, 0xE7, 0xFF, 0x00, 0x6C, 0x6C, 0x24, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x93, 0x93, 0xDB, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x24, 0x7E, 0x24, 0x24, 0x7E, 0x24, 0x00, 0xFF, 0xDB, 0x81, 0xDB, 0xDB, 0x81, 0xDB, 0xFF, 0x00, 0x08, 0x3E, 0x38, 0x0E, 0x3E, 0x08, 0x00, 0xFF, 0xF7, 0xC1, 0xC7, 0xF1, 0xC1, 0xF7, 0xFF, 0x00, 0x62, 0x64, 0x08, 0x10, 0x26, 0x46, 0x00, 0xFF, 0x9D, 0x9B, 0xF7, 0xEF, 0xD9, 0xB9, 0xFF, 0x00, 0x1C, 0x34, 0x38, 0x6E, 0x64, 0x3A, 0x00, 0xFF, 0xE3, 0xCB, 0xC7, 0x91, 0x9B, 0xC5, 0xFF, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE7, 0xE7, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0C, 0x18, 0x30, 0x30, 0x18, 0x0C, 0x00, 0xFF, 0xF3, 0xE7, 0xCF, 0xCF, 0xE7, 0xF3, 0xFF, 0x00, 0x30, 0x18, 0x0C, 0x0C, 0x18, 0x30, 0x00, 0xFF, 0xCF, 0xE7, 0xF3, 0xF3, 0xE7, 0xCF, 0xFF, 0x00, 0x00, 0x24, 0x18, 0x7E, 0x18, 0x24, 0x00, 0xFF, 0xFF, 0xDB, 0xE7, 0x81, 0xE7, 0xDB, 0xFF, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0xFF, 0xFF, 0xE7, 0xE7, 0x81, 0xE7, 0xE7, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xCF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xFF, 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00, 0xFF, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0xBF, 0xFF, 0x00, 0x3C, 0x66, 0x6E, 0x76, 0x66, 0x3C, 0x00, 0xFF, 0xC3, 0x99, 0x91, 0x89, 0x99, 0xC3, 0xFF, 0x00, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7E, 0x00, 0xFF, 0xE7, 0xC7, 0xE7, 0xE7, 0xE7, 0x81, 0xFF, 0x00, 0x3C, 0x66, 0x0C, 0x18, 0x30, 0x7E, 0x00, 0xFF, 0xC3, 0x99, 0xF3, 0xE7, 0xCF, 0x81, 0xFF, 0x00, 0x3C, 0x66, 0x0C, 0x06, 0x66, 0x3C, 0x00, 0xFF, 0xC3, 0x99, 0xF3, 0xF9, 0x99, 0xC3, 0xFF, 0x00, 0x66, 0x66, 0x7E, 0x06, 0x06, 0x06, 0x00, 0xFF, 0x99, 0x99, 0x81, 0xF9, 0xF9, 0xF9, 0xFF, 0x00, 0x7E, 0x60, 0x7C, 0x06, 0x06, 0x7C, 0x00, 0xFF, 0x81, 0x9F, 0x83, 0xF9, 0xF9, 0x83, 0xFF, 0x00, 0x1C, 0x30, 0x7C, 0x66, 0x66, 0x3C, 0x00, 0xFF, 0xE3, 0xCF, 0x83, 0x99, 0x99, 0xC3, 0xFF, 0x00, 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x00, 0xFF, 0x81, 0xF9, 0xF3, 0xE7, 0xCF, 0xCF, 0xFF, 0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0xFF, 0xC3, 0x99, 0xC3, 0x99, 0x99, 0xC3, 0xFF, 0x00, 0x3C, 0x66, 0x3E, 0x06, 0x66, 0x3C, 0x00, 0xFF, 0xC3, 0x99, 0xC1, 0xF9, 0x99, 0xC3, 0xFF, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0xE7, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x30, 0x00, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0xE7, 0xCF, 0xFF, 0x00, 0x00, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x00, 0xFF, 0xFF, 0xF3, 0xE7, 0xCF, 0xE7, 0xF3, 0xFF, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0x81, 0xFF, 0xFF, 0x00, 0x00, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x00, 0xFF, 0xFF, 0xCF, 0xE7, 0xF3, 0xE7, 0xCF, 0xFF, 0x00, 0x3C, 0x66, 0x0C, 0x18, 0x00, 0x18, 0x00, 0xFF, 0xC3, 0x99, 0xF3, 0xE7, 0xFF, 0xE7, 0xFF, 0x00, 0x3C, 0x66, 0x6E, 0x6E, 0x60, 0x3C, 0x00, 0xFF, 0xC3, 0x99, 0x91, 0x91, 0x9F, 0xC3, 0xFF, 0x00, 0x18, 0x3C, 0x66, 0x7E, 0x66, 0x66, 0x00, 0xFF, 0xE7, 0xC3, 0x99, 0x81, 0x99, 0x99, 0xFF, 0x00, 0x7C, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0xFF, 0x83, 0x99, 0x83, 0x99, 0x99, 0x83, 0xFF, 0x00, 0x3C, 0x66, 0x60, 0x60, 0x66, 0x3C, 0x00, 0xFF, 0xC3, 0x99, 0x9F, 0x9F, 0x99, 0xC3, 0xFF, 0x00, 0x78, 0x6C, 0x66, 0x66, 0x6C, 0x78, 0x00, 0xFF, 0x87, 0x93, 0x99, 0x99, 0x93, 0x87, 0xFF, 0x00, 0x7E, 0x60, 0x78, 0x60, 0x60, 0x7E, 0x00, 0xFF, 0x81, 0x9F, 0x87, 0x9F, 0x9F, 0x81, 0xFF, 0x00, 0x7E, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00, 0xFF, 0x81, 0x9F, 0x87, 0x9F, 0x9F, 0x9F, 0xFF, 0x00, 0x3C, 0x60, 0x6E, 0x66, 0x66, 0x3C, 0x00, 0xFF, 0xC3, 0x9F, 0x91, 0x99, 0x99, 0xC3, 0xFF, 0x00, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0xFF, 0x99, 0x99, 0x81, 0x99, 0x99, 0x99, 0xFF, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0xFF, 0xC3, 0xE7, 0xE7, 0xE7, 0xE7, 0xC3, 0xFF, 0x00, 0x1E, 0x06, 0x06, 0x06, 0x66, 0x3C, 0x00, 0xFF, 0xE1, 0xF9, 0xF9, 0xF9, 0x99, 0xC3, 0xFF, 0x00, 0x66, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0x00, 0xFF, 0x99, 0x93, 0x87, 0x87, 0x93, 0x99, 0xFF, 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00, 0xFF, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x81, 0xFF, 0x00, 0x42, 0x66, 0x7E, 0x7E, 0x66, 0x66, 0x00, 0xFF, 0xBD, 0x99, 0x81, 0x81, 0x99, 0x99, 0xFF, 0x00, 0x66, 0x76, 0x7E, 0x6E, 0x66, 0x66, 0x00, 0xFF, 0x99, 0x89, 0x81, 0x91, 0x99, 0x99, 0xFF, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0xFF, 0xC3, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xFF, 0x00, 0x7C, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00, 0xFF, 0x83, 0x99, 0x83, 0x9F, 0x9F, 0x9F, 0xFF, 0x00, 0x3C, 0x66, 0x66, 0x6A, 0x6C, 0x3E, 0x00, 0xFF, 0xC3, 0x99, 0x99, 0x95, 0x93, 0xC1, 0xFF, 0x00, 0x7C, 0x66, 0x7C, 0x78, 0x6C, 0x66, 0x00, 0xFF, 0x83, 0x99, 0x83, 0x87, 0x93, 0x99, 0xFF, 0x00, 0x3E, 0x60, 0x3C, 0x06, 0x06, 0x7C, 0x00, 0xFF, 0xC1, 0x9F, 0xC3, 0xF9, 0xF9, 0x83, 0xFF, 0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0xFF, 0x81, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0xFF, 0x99, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xFF, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, 0xFF, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xE7, 0xFF, 0x00, 0x66, 0x66, 0x7E, 0x7E, 0x66, 0x42, 0x00, 0xFF, 0x99, 0x99, 0x81, 0x81, 0x99, 0xBD, 0xFF, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, 0xFF, 0x99, 0xC3, 0xE7, 0xC3, 0x99, 0x99, 0xFF, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00, 0xFF, 0x99, 0x99, 0xC3, 0xE7, 0xE7, 0xE7, 0xFF, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00, 0xFF, 0x81, 0xF3, 0xE7, 0xCF, 0x9F, 0x81, 0xFF, 0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, 0xFF, 0xC3, 0xCF, 0xCF, 0xCF, 0xCF, 0xC3, 0xFF, 0x00, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00, 0xFF, 0x9F, 0xCF, 0xE7, 0xF3, 0xF9, 0xFD, 0xFF, 0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00, 0xFF, 0xC3, 0xF3, 0xF3, 0xF3, 0xF3, 0xC3, 0xFF, 0x00, 0x18, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE7, 0xC3, 0x99, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF }; const char dev_bg[] = { 0x00, 0x00, 0x14, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x01, 0x00, 0x04, 0x00, 0x06, 0x00, 0x07, 0x00, 0x0A, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x09, 0x00, 0x0E, 0x00, 0x0F, 0x00, 0x26, 0x00, 0x27, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x02, 0x00, 0x04, 0x00, 0x16, 0x00, 0x17, 0x00, 0x1A, 0x00, 0x1B, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1E, 0x00, 0x1F, 0x00, 0x36, 0x00, 0x37, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x1C, 0x00, 0x1D, 0x00, 0x02, 0x00, 0x05, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x05, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00 }; #endif #endif /* dev_menu_data_h */ ================================================ FILE: sdl/main.c ================================================ // // Copyright 2017-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "config.h" #include "main.h" #include "core.h" #include "runner.h" #include "dev_menu.h" #include "settings.h" #include "system_paths.h" #include "utils.h" #include "boot_intro.h" #include "sdl_include.h" #if SCREENSHOTS #include "screenshot.h" #endif #ifdef __EMSCRIPTEN__ #include #endif #include #include const char *defaultDisk = "Disk.nx"; const int defaultWindowScale = 4; const int joyAxisThreshold = 16384; const int keyboardControls[2][2][8] = { // mapping 0 { // up, down, left, right, button A, button B, alt. button A, alt. button B {SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_N, SDL_SCANCODE_M}, {SDL_SCANCODE_E, SDL_SCANCODE_D, SDL_SCANCODE_S, SDL_SCANCODE_F, SDL_SCANCODE_TAB, SDL_SCANCODE_Q, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_A} }, // mapping 1 { // up, down, left, right, button A, button B, alt. button A, alt. button B {SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_J, SDL_SCANCODE_K, SDL_SCANCODE_I, SDL_SCANCODE_U}, {SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_H, SDL_SCANCODE_L, SDL_SCANCODE_O, SDL_SCANCODE_Y} } }; void update(void *arg); void updateScreenRect(int winW, int winH); void configureJoysticks(void); void closeJoysticks(void); void setTouchPosition(int windowX, int windowY); void toggleZoom(void); void changeVolume(int delta); void audioCallback(void *userdata, Uint8 *stream, int len); void saveScreenshot(void *pixels, int scale); #ifdef __EMSCRIPTEN__ void onloaded(const char *filename); void onerror(const char *filename); #endif SDL_Window *window = NULL; SDL_Renderer *renderer = NULL; SDL_Texture *texture = NULL; SDL_AudioDeviceID audioDevice = 0; SDL_AudioSpec audioSpec; struct Runner runner; #if DEV_MENU struct DevMenu devMenu; #endif struct Settings settings; struct CoreInput coreInput; enum MainState mainState = MainStateUndefined; char mainProgramFilename[FILENAME_MAX] = ""; int numJoysticks = 0; SDL_Joystick *joysticks[2] = {NULL, NULL}; SDL_Rect screenRect; bool quit = false; bool releasedTouch = false; bool audioStarted = false; bool mouseEnabled = false; int messageNumber = 0; bool hasUsedInputLastUpdate = false; int screenshotRequestedWithScale = 0; int volume = 0; // 0 = max, it's a bit shift int main(int argc, const char * argv[]) { memset(&coreInput, 0, sizeof(struct CoreInput)); settings_init(&settings, mainProgramFilename, argc, argv); runner_init(&runner); #if DEV_MENU dev_init(&devMenu, &runner, &settings); #endif if (runner_isOkay(&runner)) { SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK); SDL_EventState(SDL_DROPFILE, SDL_ENABLE); SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_DROPFILE: { strncpy(mainProgramFilename, event.drop.file, FILENAME_MAX - 1); SDL_free(event.drop.file); break; } } } Uint32 windowFlags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE; if (settings.session.fullscreen) { windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP; } const char *windowTitle = "LowRes NX"; window = SDL_CreateWindow(windowTitle, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH * defaultWindowScale, SCREEN_HEIGHT * defaultWindowScale, windowFlags); renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH, SCREEN_HEIGHT); SDL_AudioSpec desiredAudioSpec = { .freq = 44100, .format = AUDIO_S16, .channels = NUM_CHANNELS, #ifdef __EMSCRIPTEN__ .samples = 2048, // sample FRAMES #else .samples = 1470, // sample FRAMES #endif .userdata = runner.core, .callback = audioCallback }; audioDevice = SDL_OpenAudioDevice(NULL, 0, &desiredAudioSpec, &audioSpec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); configureJoysticks(); bootNX(); if (mainProgramFilename[0] != 0) { machine_poke(runner.core, bootIntroStateAddress, BootIntroStateProgramAvailable); } int width, height; SDL_GetWindowSize(window, &width, &height); updateScreenRect(width, height); #ifdef __EMSCRIPTEN__ emscripten_set_main_loop_arg(update, NULL, -1, true); #else while (!quit) { Uint32 ticks = SDL_GetTicks(); update(NULL); if (!settings.session.disabledelay || runner.core->machineInternals->isEnergySaving) { // limit to 60 FPS Uint32 ticksDelta = SDL_GetTicks() - ticks; if (ticksDelta < 16) { SDL_Delay(16 - ticksDelta); } } } core_willSuspendProgram(runner.core); #endif } closeJoysticks(); SDL_CloseAudioDevice(audioDevice); SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); runner_deinit(&runner); return 0; } void bootNX() { mainState = MainStateBootIntro; struct CoreError error = core_compileProgram(runner.core, bootIntroSourceCode, true); if (error.code != ErrorNone) { core_traceError(runner.core, error); } runner.core->interpreter->debug = false; core_willRunProgram(runner.core, SDL_GetTicks() / 1000); } void rebootNX() { core_willSuspendProgram(runner.core); mainProgramFilename[0] = 0; bootNX(); } bool hasProgram() { return mainProgramFilename[0] != 0; } const char *getMainProgramFilename() { return mainProgramFilename; } void selectProgram(const char *filename) { strncpy(mainProgramFilename, filename, FILENAME_MAX - 1); if (mainState == MainStateBootIntro) { machine_poke(runner.core, bootIntroStateAddress, BootIntroStateProgramAvailable); } else { runMainProgram(); } } void runMainProgram() { core_willSuspendProgram(runner.core); struct CoreError error = runner_loadProgram(&runner, mainProgramFilename); #if DEV_MENU devMenu.lastError = error; #endif if (error.code != ErrorNone) { #if DEV_MENU showDevMenu(); #else core_traceError(runner.core, error); #endif } else { core_willRunProgram(runner.core, SDL_GetTicks() / 1000); mainState = MainStateRunningProgram; } } void runToolProgram(const char *filename) { core_willSuspendProgram(runner.core); struct CoreError error = runner_loadProgram(&runner, filename); if (error.code == ErrorNone) { mainState = MainStateRunningTool; runner.core->interpreter->debug = false; core_willRunProgram(runner.core, SDL_GetTicks() / 1000); } else { core_traceError(runner.core, error); } } void showDevMenu() { #if DEV_MENU core_willSuspendProgram(runner.core); bool reload = (mainState == MainStateRunningTool); mainState = MainStateDevMenu; dev_show(&devMenu, reload); #endif } bool usesMainProgramAsDisk() { return (mainState == MainStateRunningTool); } void getDiskFilename(char *outputString) { if (usesMainProgramAsDisk()) { strncpy(outputString, mainProgramFilename, FILENAME_MAX - 1); } else { strncpy(outputString, mainProgramFilename, FILENAME_MAX - 1); char *separator = strrchr(outputString, PATH_SEPARATOR_CHAR); if (separator) { separator++; *separator = 0; strncat(outputString, defaultDisk, FILENAME_MAX - 1); } else { strncpy(outputString, defaultDisk, FILENAME_MAX - 1); } } } void getRamFilename(char *outputString) { char *prefPath = SDL_GetPrefPath("Inutilis Software", "LowRes NX"); if (prefPath) { strncpy(outputString, prefPath, FILENAME_MAX - 1); char *separator = strrchr(mainProgramFilename, PATH_SEPARATOR_CHAR); if (separator) { separator++; strncat(outputString, separator, FILENAME_MAX - 1); } else { strncat(outputString, mainProgramFilename, FILENAME_MAX - 1); } char *postfix = strrchr(outputString, '.'); if (postfix) { *postfix = 0; } strncat(outputString, ".dat", FILENAME_MAX - 1); } else { outputString[0] = 0; } } void updateMouseMode() { if (!mouseEnabled && (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP)) { SDL_ShowCursor(SDL_DISABLE); } else { SDL_ShowCursor(SDL_ENABLE); } } void setMouseEnabled(bool enabled) { mouseEnabled = enabled; updateMouseMode(); } void update(void *arg) { SDL_Event event; bool hasInput = false; bool forceRender = false; if (releasedTouch) { coreInput.touch = false; releasedTouch = false; } while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: quit = true; break; case SDL_WINDOWEVENT: switch (event.window.event) { case SDL_WINDOWEVENT_RESIZED: { updateScreenRect(event.window.data1, event.window.data2); forceRender = true; break; } } break; case SDL_DROPFILE: { if (hasPostfix(event.drop.file, ".nx") || hasPostfix(event.drop.file, ".NX")) { #if DEV_MENU bool handled = (mainState == MainStateDevMenu && dev_handleDropFile(&devMenu, event.drop.file)); if (!handled) { selectProgram(event.drop.file); } #else selectProgram(event.drop.file); #endif forceRender = true; } else { overlay_message(runner.core, "NOT NX FORMAT"); } SDL_free(event.drop.file); break; } case SDL_KEYDOWN: { SDL_Keycode keycode = event.key.keysym.sym; SDL_Scancode scancode = event.key.keysym.scancode; if (event.key.keysym.mod == 0) { hasInput = true; } // text input if (keycode == SDLK_RETURN) { coreInput.key = CoreInputKeyReturn; } else if (keycode == SDLK_BACKSPACE) { coreInput.key = CoreInputKeyBackspace; } else if (scancode == SDL_SCANCODE_UP) { coreInput.key = CoreInputKeyUp; } else if (scancode == SDL_SCANCODE_DOWN) { coreInput.key = CoreInputKeyDown; } else if (scancode == SDL_SCANCODE_LEFT) { coreInput.key = CoreInputKeyLeft; } else if (scancode == SDL_SCANCODE_RIGHT) { coreInput.key = CoreInputKeyRight; } // console buttons if (keycode == SDLK_RETURN || keycode == SDLK_p) { coreInput.pause = true; } #if HOT_KEYS // system if (event.key.keysym.mod & KMOD_CTRL) { if (keycode == SDLK_d) { core_setDebug(runner.core, !core_getDebug(runner.core)); if (core_getDebug(runner.core)) { overlay_message(runner.core, "DEBUG ON"); } else { overlay_message(runner.core, "DEBUG OFF"); } } else if (keycode == SDLK_f) { if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) { SDL_SetWindowFullscreen(window, 0); } else { SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); } updateMouseMode(); forceRender = true; } else if (keycode == SDLK_r) { if (hasProgram()) { runMainProgram(); overlay_message(runner.core, "RELOADED"); } } else if (keycode == SDLK_e) { rebootNX(); } else if (keycode == SDLK_s) { screenshotRequestedWithScale = (event.key.keysym.mod & KMOD_SHIFT) ? 1 : 4; forceRender = true; } else if (keycode == SDLK_z) { toggleZoom(); forceRender = true; } else if (keycode == SDLK_PLUS) { changeVolume(-1); } else if (keycode == SDLK_MINUS) { changeVolume(+1); } } else if (keycode == SDLK_ESCAPE) { if (settings.session.disabledev) { quit = true; } #if DEV_MENU else if (hasProgram()) { if (mainState != MainStateDevMenu) { showDevMenu(); } } #endif } else if (settings.session.mapping == 1 && !core_isKeyboardEnabled(runner.core)) { if (keycode == SDLK_SPACE) { toggleZoom(); forceRender = true; hasInput = false; } else if (scancode == SDL_SCANCODE_KP_PLUS) { changeVolume(-1); } else if (scancode == SDL_SCANCODE_KP_MINUS) { changeVolume(+1); } } #endif break; } case SDL_TEXTINPUT: { char key = event.text.text[0]; hasInput = true; if (key >= ' ' && key <= '_') { coreInput.key = key; } else if (key >= 'a' && key <= 'z') { coreInput.key = key - 32; } break; } case SDL_MOUSEBUTTONDOWN: { hasInput = true; setTouchPosition(event.button.x, event.button.y); coreInput.touch = true; break; } case SDL_MOUSEBUTTONUP: { releasedTouch = true; break; } case SDL_MOUSEMOTION: { setTouchPosition(event.motion.x, event.motion.y); break; } case SDL_JOYDEVICEADDED: case SDL_JOYDEVICEREMOVED: { configureJoysticks(); break; } case SDL_JOYBUTTONDOWN: { hasInput = true; if (event.jbutton.button == 2) { coreInput.pause = true; } break; } } } const Uint8 *state = SDL_GetKeyboardState(NULL); for (int i = 0; i < 2; i++) { struct CoreInputGamepad *gamepad = &coreInput.gamepads[i]; if (i < numJoysticks) { SDL_Joystick *joy = joysticks[i]; Uint8 hat = SDL_JoystickGetHat(joy, 0); Sint16 axisX = SDL_JoystickGetAxis(joy, 0); Sint16 axisY = SDL_JoystickGetAxis(joy, 1); gamepad->up = (hat & SDL_HAT_UP) != 0 || axisY < -joyAxisThreshold; gamepad->down = (hat & SDL_HAT_DOWN) != 0 || axisY > joyAxisThreshold; gamepad->left = (hat & SDL_HAT_LEFT) != 0 || axisX < -joyAxisThreshold; gamepad->right = (hat & SDL_HAT_RIGHT) != 0 || axisX > joyAxisThreshold; gamepad->buttonA = SDL_JoystickGetButton(joy, 0); gamepad->buttonB = SDL_JoystickGetButton(joy, 1); } else { int ci = i - numJoysticks; int m = settings.session.mapping; gamepad->up = state[keyboardControls[m][ci][0]]; gamepad->down = state[keyboardControls[m][ci][1]]; gamepad->left = state[keyboardControls[m][ci][2]]; gamepad->right = state[keyboardControls[m][ci][3]]; gamepad->buttonA = state[keyboardControls[m][ci][4]] || state[keyboardControls[m][ci][6]]; gamepad->buttonB = state[keyboardControls[m][ci][5]] || state[keyboardControls[m][ci][7]]; } } switch (mainState) { case MainStateUndefined: break; case MainStateBootIntro: if (hasInput && !hasProgram()) { // user hint overlay_message(runner.core, "DRAG .NX INTO WINDOW"); } core_update(runner.core, &coreInput); if (machine_peek(runner.core, bootIntroStateAddress) == BootIntroStateReadyToRun) { machine_poke(runner.core, bootIntroStateAddress, BootIntroStateDone); #ifdef __EMSCRIPTEN__ emscripten_async_wget(mainProgramFilename, mainProgramFilename, onloaded, onerror); #else runMainProgram(); #endif } break; case MainStateRunningProgram: case MainStateRunningTool: core_update(runner.core, &coreInput); if (hasInput) { if (runner.core->interpreter->state == StateEnd) { overlay_message(runner.core, "END OF PROGRAM"); } else if (!coreInput.out_hasUsedInput && !hasUsedInputLastUpdate) { // user hints for controls union IOAttributes attr = runner.core->machine->ioRegisters.attr; if (attr.touchEnabled && !attr.keyboardEnabled) { overlay_message(runner.core, "TOUCH/MOUSE"); } if (attr.keyboardEnabled && !attr.touchEnabled) { overlay_message(runner.core, "KEYBOARD"); } if (attr.gamepadsEnabled && !attr.keyboardEnabled && settings.session.mapping == 0) { if (attr.gamepadsEnabled == 2) { if (messageNumber % 2 == 1) { overlay_message(runner.core, "P2 ]:ESDF [:TAB \\:Q"); } else { overlay_message(runner.core, "P1 ]:ARROWS [:N \\:M"); } messageNumber++; } else { overlay_message(runner.core, "]:ARROWS [:Z \\:X"); } } } } break; case MainStateDevMenu: #if DEV_MENU dev_update(&devMenu, &coreInput); #endif break; } hasUsedInputLastUpdate = coreInput.out_hasUsedInput; if (!audioStarted && audioDevice) { audioStarted = true; SDL_PauseAudioDevice(audioDevice, 0); } if (core_shouldRender(runner.core) || forceRender) { SDL_RenderClear(renderer); void *pixels = NULL; int pitch = 0; SDL_LockTexture(texture, NULL, &pixels, &pitch); video_renderScreen(runner.core, pixels); if (screenshotRequestedWithScale > 0) { saveScreenshot(pixels, screenshotRequestedWithScale); screenshotRequestedWithScale = 0; } SDL_UnlockTexture(texture); SDL_RenderCopy(renderer, texture, NULL, &screenRect); SDL_RenderPresent(renderer); } } void updateScreenRect(int winW, int winH) { switch (settings.session.zoom) { case ZoomPixelPerfect: { int factor = fmax(1, fmin(winW / SCREEN_WIDTH, winH / SCREEN_HEIGHT)); int nxScreenW = SCREEN_WIDTH * factor; int nxScreenH = SCREEN_HEIGHT * factor; screenRect.w = nxScreenW; screenRect.h = nxScreenH; screenRect.x = (winW - nxScreenW) / 2; screenRect.y = (winH - nxScreenH) / 2; break; } case ZoomLarge: { float factor = fmax(1, fmin(winW / (float)SCREEN_WIDTH, winH / (float)SCREEN_HEIGHT)); int nxScreenW = SCREEN_WIDTH * factor; int nxScreenH = SCREEN_HEIGHT * factor; screenRect.w = nxScreenW; screenRect.h = nxScreenH; screenRect.x = (winW - nxScreenW) / 2; screenRect.y = (winH - nxScreenH) / 2; break; } case ZoomOverscan: { float factor = fmax(winW / (float)SCREEN_WIDTH, winH / (float)SCREEN_HEIGHT); int nxScreenW = SCREEN_WIDTH * factor; int nxScreenH = SCREEN_HEIGHT * factor; screenRect.w = nxScreenW; screenRect.h = nxScreenH; screenRect.x = (winW - nxScreenW) / 2; screenRect.y = (winH - nxScreenH) / 2; break; } case ZoomSqueeze: screenRect.w = winW; screenRect.h = winH; screenRect.x = 0; screenRect.y = 0; break; } SDL_SetTextInputRect(&screenRect); } void configureJoysticks() { closeJoysticks(); numJoysticks = SDL_NumJoysticks(); if (numJoysticks > 2) { numJoysticks = 2; } for (int i = 0; i < numJoysticks; i++) { joysticks[i] = SDL_JoystickOpen(i); } } void closeJoysticks() { for (int i = 0; i < numJoysticks; i++) { SDL_JoystickClose(joysticks[i]); joysticks[i] = NULL; } numJoysticks = 0; } void setTouchPosition(int windowX, int windowY) { coreInput.touchX = (windowX - screenRect.x) * SCREEN_WIDTH / screenRect.w; coreInput.touchY = (windowY - screenRect.y) * SCREEN_HEIGHT / screenRect.h; } void toggleZoom() { settings.session.zoom = (settings.session.zoom + 1) % 4; int width, height; SDL_GetWindowSize(window, &width, &height); updateScreenRect(width, height); } void changeVolume(int delta) { volume += delta; if (volume < 0) volume = 0; if (volume > 6) volume = 6; char message[16]; sprintf(message, "VOLUME %d%%", 100 >> volume); overlay_message(runner.core, message); } void audioCallback(void *userdata, Uint8 *stream, int len) { int16_t *samples = (int16_t *)stream; int numSamples = len / NUM_CHANNELS; audio_renderAudio(userdata, samples, numSamples, audioSpec.freq, volume); } void saveScreenshot(void *pixels, int scale) { #if SCREENSHOTS bool succeeded = screenshot_save(pixels, scale); if (succeeded) { overlay_message(runner.core, "SCREENSHOT SAVED"); } else { overlay_message(runner.core, "SCREENSHOT ERROR"); } #endif } #ifdef __EMSCRIPTEN__ void onloaded(const char *filename) { runMainProgram(); } void onerror(const char *filename) { } #endif ================================================ FILE: sdl/main.h ================================================ // // Copyright 2017-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef main_h #define main_h #include enum MainState { MainStateUndefined, MainStateBootIntro, MainStateRunningProgram, MainStateRunningTool, MainStateDevMenu, }; enum Zoom { ZoomPixelPerfect, ZoomLarge, ZoomOverscan, ZoomSqueeze, }; void bootNX(void); void rebootNX(void); bool hasProgram(void); const char *getMainProgramFilename(void); void selectProgram(const char *filename); void runMainProgram(void); void runToolProgram(const char *filename); void showDevMenu(void); bool usesMainProgramAsDisk(void); void getDiskFilename(char *outputString); void getRamFilename(char *outputString); void setMouseEnabled(bool enabled); #endif /* main_h */ ================================================ FILE: sdl/runner.c ================================================ // // Copyright 2017-2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "runner.h" #include "main.h" #include "sdl_include.h" #include "system_paths.h" #include #include void interpreterDidFail(void *context, struct CoreError coreError); bool diskDriveWillAccess(void *context, struct DataManager *diskDataManager); void diskDriveDidSave(void *context, struct DataManager *diskDataManager); void diskDriveIsFull(void *context, struct DataManager *diskDataManager); void controlsDidChange(void *context, struct ControlsInfo controlsInfo); void persistentRamWillAccess(void *context, uint8_t *destination, int size); void persistentRamDidChange(void *context, uint8_t *data, int size); void runner_init(struct Runner *runner) { memset(runner, 0, sizeof(struct Runner)); struct Core *core = calloc(1, sizeof(struct Core)); if (core) { core_init(core); runner->coreDelegate.context = runner; runner->coreDelegate.interpreterDidFail = interpreterDidFail; runner->coreDelegate.diskDriveWillAccess = diskDriveWillAccess; runner->coreDelegate.diskDriveDidSave = diskDriveDidSave; runner->coreDelegate.diskDriveIsFull = diskDriveIsFull; runner->coreDelegate.controlsDidChange = controlsDidChange; runner->coreDelegate.persistentRamWillAccess = persistentRamWillAccess; runner->coreDelegate.persistentRamDidChange = persistentRamDidChange; core_setDelegate(core, &runner->coreDelegate); runner->core = core; } } void runner_deinit(struct Runner *runner) { if (runner->core) { core_deinit(runner->core); free(runner->core); runner->core = NULL; } } bool runner_isOkay(struct Runner *runner) { return (runner->core != NULL); } struct CoreError runner_loadProgram(struct Runner *runner, const char *filename) { struct CoreError error = err_noCoreError(); FILE *file = fopen_utf8(filename, "rb"); if (file) { fseek(file, 0, SEEK_END); long size = ftell(file); fseek(file, 0, SEEK_SET); char *sourceCode = calloc(1, size + 1); // +1 for terminator if (sourceCode) { fread(sourceCode, size, 1, file); error = core_compileProgram(runner->core, sourceCode, true); free(sourceCode); } else { error = err_makeCoreError(ErrorOutOfMemory, -1); } fclose(file); } else { error = err_makeCoreError(ErrorCouldNotOpenProgram, -1); } return error; } /** Called on error */ void interpreterDidFail(void *context, struct CoreError coreError) { struct Runner *runner = context; core_traceError(runner->core, coreError); } /** Returns true if the disk is ready, false if not. In case of not, core_diskLoaded must be called when ready. */ bool diskDriveWillAccess(void *context, struct DataManager *diskDataManager) { struct Runner *runner = context; if (!runner->messageShownUsingDisk && !usesMainProgramAsDisk()) { #ifdef __EMSCRIPTEN__ overlay_message(runner->core, "NO DISK"); #else overlay_message(runner->core, "USING DISK.NX"); #endif runner->messageShownUsingDisk = true; } #ifndef __EMSCRIPTEN__ char diskFilename[FILENAME_MAX]; getDiskFilename(diskFilename); FILE *file = fopen_utf8(diskFilename, "rb"); if (file) { fseek(file, 0, SEEK_END); long size = ftell(file); fseek(file, 0, SEEK_SET); char *sourceCode = calloc(1, size + 1); // +1 for terminator if (sourceCode) { fread(sourceCode, size, 1, file); struct CoreError error = data_import(diskDataManager, sourceCode, true); free(sourceCode); if (error.code != ErrorNone) { core_traceError(runner->core, error); } } else { struct TextLib *lib = &runner->core->overlay->textLib; txtlib_printText(lib, "NOT ENOUGH MEMORY\n"); } fclose(file); } #endif return true; } /** Called when a disk data entry was saved */ void diskDriveDidSave(void *context, struct DataManager *diskDataManager) { struct Runner *runner = context; #ifdef __EMSCRIPTEN__ overlay_message(runner->core, "NO DISK"); #else char *output = data_export(diskDataManager); if (output) { char diskFilename[FILENAME_MAX]; getDiskFilename(diskFilename); FILE *file = fopen_utf8(diskFilename, "wb"); if (file) { fwrite(output, 1, strlen(output), file); fclose(file); } else { struct TextLib *lib = &runner->core->overlay->textLib; txtlib_printText(lib, "COULD NOT SAVE:\n"); txtlib_printText(lib, diskFilename); txtlib_printText(lib, "\n"); } free(output); } #endif } /** Called when a disk data entry was tried to be saved, but the disk is full */ void diskDriveIsFull(void *context, struct DataManager *diskDataManager) { struct Runner *runner = context; overlay_message(runner->core, "DISK IS FULL"); } /** Called when keyboard or gamepad settings changed */ void controlsDidChange(void *context, struct ControlsInfo controlsInfo) { if ( controlsInfo.keyboardMode == KeyboardModeOn || (controlsInfo.keyboardMode == KeyboardModeOptional && !SDL_HasScreenKeyboardSupport()) ) { if (!SDL_IsTextInputActive()) { SDL_StartTextInput(); } } else if (SDL_IsTextInputActive()) { SDL_StopTextInput(); } setMouseEnabled(controlsInfo.isTouchEnabled); } /** Called when persistent RAM will be accessed the first time */ void persistentRamWillAccess(void *context, uint8_t *destination, int size) { #ifndef __EMSCRIPTEN__ char ramFilename[FILENAME_MAX]; getRamFilename(ramFilename); FILE *file = fopen_utf8(ramFilename, "rb"); if (file) { fread(destination, sizeof(uint8_t), size, file); fclose(file); } #endif } /** Called when persistent RAM should be saved */ void persistentRamDidChange(void *context, uint8_t *data, int size) { #ifndef __EMSCRIPTEN__ struct Runner *runner = context; char ramFilename[FILENAME_MAX]; getRamFilename(ramFilename); FILE *file = fopen_utf8(ramFilename, "wb"); if (file) { fwrite(data, 1, size, file); fclose(file); } else { struct TextLib *lib = &runner->core->overlay->textLib; txtlib_printText(lib, "COULD NOT SAVE:\n"); txtlib_printText(lib, ramFilename); txtlib_printText(lib, "\n"); } #endif } ================================================ FILE: sdl/runner.h ================================================ // // Copyright 2017-2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef runner_h #define runner_h #include #include #include "core.h" struct Runner { struct Core *core; struct CoreDelegate coreDelegate; bool messageShownUsingDisk; }; void runner_init(struct Runner *runner); void runner_deinit(struct Runner *runner); bool runner_isOkay(struct Runner *runner); struct CoreError runner_loadProgram(struct Runner *runner, const char *filename); #endif /* runner_h */ ================================================ FILE: sdl/screenshot.c ================================================ // // Copyright 2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "config.h" #if SCREENSHOTS #include "screenshot.h" #include "system_paths.h" #include "core.h" #include #include #include #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" bool writeImage(const char *filename, int width, int height, uint32_t *pixels, int scale) { uint8_t *data = malloc(width * height * 3 * scale * scale); if (data) { int i = 0; for (int y = 0; y < height; y++) { for (int ys = 0; ys < scale; ys++) { for (int x = 0; x < width; x++) { uint32_t pixel = pixels[y * width + x]; for (int xs = 0; xs < scale; xs++) { data[i++] = (pixel) & 0xFF; data[i++] = (pixel >> 8) & 0xFF; data[i++] = (pixel >> 16) & 0xFF; } } } } int result = stbi_write_png(filename, width * scale, height * scale, 3, data, width * 3 * scale); free(data); return (result != 0); } return false; } bool screenshot_save(uint32_t *pixels, int scale) { char filename[FILENAME_MAX]; desktop_path(filename, FILENAME_MAX); size_t len = strlen(filename); time_t rawtime; time(&rawtime); struct tm *timeinfo = localtime(&rawtime); strftime(&filename[len], FILENAME_MAX - len - 1, "LowRes NX %Y-%m-%d %H_%M_%S.png", timeinfo); return writeImage(filename, SCREEN_WIDTH, SCREEN_HEIGHT, pixels, scale); } #endif ================================================ FILE: sdl/screenshot.h ================================================ // // Copyright 2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef screenshot_h #define screenshot_h #include "config.h" #if SCREENSHOTS #include #include #include bool screenshot_save(uint32_t *pixels, int scale); #endif #endif /* screenshot_h */ ================================================ FILE: sdl/sdl_include.h ================================================ // // Copyright 2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #if defined(_WIN32) #include #else #include #endif ================================================ FILE: sdl/settings.c ================================================ // // Copyright 2017-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "config.h" #include "settings.h" #include "system_paths.h" #include "utils.h" #include "sdl_include.h" #include const char *optionYes = "yes"; const char *optionNo = "no"; bool settings_filename(char *destination); void settings_setParameter(struct Parameters *parameters, const char *key, const char *value); void settings_saveAs(struct Settings *settings, const char *filename); void settings_init(struct Settings *settings, char *filenameOut, int argc, const char * argv[]) { memset(settings, 0, sizeof(struct Settings)); #if SETTINGS_FILE // load settings file char filename[FILENAME_MAX]; if (settings_filename(filename)) { FILE *file = fopen_utf8(filename, "r"); if (file) { char line[FILENAME_MAX]; while (fgets(line, FILENAME_MAX, file)) { if (line[0] != '#') { char *space = strchr(line, ' '); if (space) { *space = 0; // separate into two strings char *value = space + 1; // remove EOL characters char *eolChar = strchr(value, '\n'); if (eolChar) { *eolChar = 0; } eolChar = strchr(value, '\r'); if (eolChar) { *eolChar = 0; } if (strcmp(line, "tool") == 0) { settings_addTool(settings, value); } else { settings_setParameter(&settings->file, line, value); } } } } fclose(file); } else { // write default settings file settings_saveAs(settings, filename); } // copy file parameters to session parameters memcpy(&settings->session, &settings->file, sizeof(struct Parameters)); } #endif // parse arguments for (int i = 1; i < argc; i++) { const char *arg = argv[i]; if (*arg == '-') { i++; if (i < argc) { settings_setParameter(&settings->session, arg + 1, argv[i]); } else { printf("missing value for parameter %s\n", arg); } } else { strncpy(filenameOut, arg, FILENAME_MAX - 1); } } } bool settings_filename(char *destination) { #if SETTINGS_FILE char *prefPath = SDL_GetPrefPath("Inutilis Software", "LowRes NX"); if (prefPath) { strncpy(destination, prefPath, FILENAME_MAX - 1); strncat(destination, "settings.txt", FILENAME_MAX - 1); SDL_free((void *)prefPath); return true; } #endif return false; } void settings_setParameter(struct Parameters *parameters, const char *key, const char *value) { if (strcmp(key, "fullscreen") == 0) { if (strcmp(value, optionYes) == 0) { parameters->fullscreen = true; } else if (strcmp(value, optionNo) == 0) { parameters->fullscreen = false; } } else if (strcmp(key, "disabledev") == 0) { if (strcmp(value, optionYes) == 0) { parameters->disabledev = true; } else if (strcmp(value, optionNo) == 0) { parameters->disabledev = false; } } else if (strcmp(key, "mapping") == 0) { int i = atoi(value); if (i >= 0 && i <= 1) { parameters->mapping = i; } } else if (strcmp(key, "disabledelay") == 0) { if (strcmp(value, optionYes) == 0) { parameters->disabledelay = true; } else if (strcmp(value, optionNo) == 0) { parameters->disabledelay = false; } } else if (strcmp(key, "zoom") == 0) { int i = atoi(value); if (i >= 0 && i <= 3) { parameters->zoom = i; } } else { printf("unknown parameter %s\n", key); } } void settings_save(struct Settings *settings) { #if SETTINGS_FILE char filename[FILENAME_MAX]; if (settings_filename(filename)) { settings_saveAs(settings, filename); } #endif } void settings_saveAs(struct Settings *settings, const char *filename) { #if SETTINGS_FILE FILE *file = fopen_utf8(filename, "w"); if (file) { fputs("# Start the application in fullscreen mode.\n# fullscreen yes/no\n", file); fputs("fullscreen ", file); fputs(settings->file.fullscreen ? optionYes : optionNo, file); fputs("\n\n", file); fputs("# Start the application in zoom mode: 0 = pixel perfect, 1 = large, 2 = overscan, 3 = squeeze.\n# zoom 0-3\n", file); fprintf(file, "zoom %d\n\n", settings->file.zoom); fputs("# Disable the Development Menu, Esc key quits LowRes NX.\n# disabledev yes/no\n", file); fputs("disabledev ", file); fputs(settings->file.disabledev ? optionYes : optionNo, file); fputs("\n\n", file); fputs("# Set the key mapping. 0 = standard, 1 = GameShell.\n# mapping 0-1\n", file); fprintf(file, "mapping %d\n\n", settings->file.mapping); fputs("# Disable the delay for too short frames.\n# disabledelay yes/no\n", file); fputs("disabledelay ", file); fputs(settings->file.disabledelay ? optionYes : optionNo, file); fputs("\n\n", file); fputs("# Add tools for the Edit ROM menu (max 4).\n# tool My Tool.nx\n", file); for (int i = 0; i < settings->numTools; i++) { fputs("tool ", file); fputs(settings->tools[i], file); fputs("\n", file); } fclose(file); } #endif } bool settings_addTool(struct Settings *settings, const char *filename) { int index = settings->numTools; if (index < MAX_TOOLS) { strncpy(settings->tools[index], filename, FILENAME_MAX - 1); displayName(filename, settings->toolNames[index], TOOL_NAME_SIZE); settings->numTools++; return true; } return false; } void settings_removeTool(struct Settings *settings, int index) { if (index < settings->numTools) { for (int i = index; i < MAX_TOOLS - 1; i++) { strncpy(settings->tools[i], settings->tools[i + 1], FILENAME_MAX - 1); strncpy(settings->toolNames[i], settings->toolNames[i + 1], TOOL_NAME_SIZE - 1); } settings->tools[MAX_TOOLS - 1][0] = 0; settings->toolNames[MAX_TOOLS - 1][0] = 0; settings->numTools--; } } ================================================ FILE: sdl/settings.h ================================================ // // Copyright 2017-2020 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef settings_h #define settings_h #include #include #define MAX_TOOLS 4 #define TOOL_NAME_SIZE 21 struct Parameters { bool fullscreen; int zoom; bool disabledev; int mapping; int disabledelay; }; struct Settings { struct Parameters file; struct Parameters session; int numTools; char tools[MAX_TOOLS][FILENAME_MAX]; char toolNames[MAX_TOOLS][TOOL_NAME_SIZE]; }; void settings_init(struct Settings *settings, char *filenameOut, int argc, const char * argv[]); void settings_save(struct Settings *settings); bool settings_addTool(struct Settings *settings, const char *filename); void settings_removeTool(struct Settings *settings, int index); #endif /* settings_h */ ================================================ FILE: sdl/stb_image_write.h ================================================ /* stb_image_write - v1.09 - public domain - http://nothings.org/stb/stb_image_write.h writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 no warranty implied; use at your own risk Before #including, #define STB_IMAGE_WRITE_IMPLEMENTATION in the file that you want to have the implementation. Will probably not work correctly with strict-aliasing optimizations. If using a modern Microsoft Compiler, non-safe versions of CRT calls may cause compilation warnings or even errors. To avoid this, also before #including, #define STBI_MSC_SECURE_CRT ABOUT: This header file is a library for writing images to C stdio. It could be adapted to write to memory or a general streaming interface; let me know. The PNG output is not optimal; it is 20-50% larger than the file written by a decent optimizing implementation; though providing a custom zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. This library is designed for source code compactness and simplicity, not optimal image file size or run-time performance. BUILDING: You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace malloc,realloc,free. You can #define STBIW_MEMMOVE() to replace memmove() You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function for PNG compression (instead of the builtin one), it must have the following signature: unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); The returned data will be freed with STBIW_FREE() (free() by default), so it must be heap allocated with STBIW_MALLOC() (malloc() by default), USAGE: There are five functions, one for each image file format: int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically There are also five equivalent functions that use an arbitrary write function. You are expected to open/close your file-equivalent before and after calling these: int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); where the callback is: void stbi_write_func(void *context, void *data, int size); You can configure it with these global variables: int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode You can define STBI_WRITE_NO_STDIO to disable the file variant of these functions, so the library will not use stdio.h at all. However, this will also disable HDR writing, because it requires stdio for formatted output. Each function returns 0 on failure and non-0 on success. The functions create an image file defined by the parameters. The image is a rectangle of pixels stored from left-to-right, top-to-bottom. Each pixel contains 'comp' channels of data stored interleaved with 8-bits per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. The *data pointer points to the first byte of the top-left-most pixel. For PNG, "stride_in_bytes" is the distance in bytes from the first byte of a row of pixels to the first byte of the next row of pixels. PNG creates output files with the same number of components as the input. The BMP format expands Y to RGB in the file format and does not output alpha. PNG supports writing rectangles of data even when the bytes storing rows of data are not consecutive in memory (e.g. sub-rectangles of a larger image), by supplying the stride between the beginning of adjacent rows. The other formats do not. (Thus you cannot write a native-format BMP through the BMP writer, both because it is in BGR order and because it may have padding at the end of the line.) PNG allows you to set the deflate compression level by setting the global variable 'stbi_write_png_compression_level' (it defaults to 8). HDR expects linear float data. Since the format is always 32-bit rgb(e) data, alpha (if provided) is discarded, and for monochrome data it is replicated across all three channels. TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed data, set the global variable 'stbi_write_tga_with_rle' to 0. JPEG does ignore alpha channels in input data; quality is between 1 and 100. Higher quality looks better but results in a bigger image. JPEG baseline (no JPEG progressive). CREDITS: Sean Barrett - PNG/BMP/TGA Baldur Karlsson - HDR Jean-Sebastien Guay - TGA monochrome Tim Kelsey - misc enhancements Alan Hickman - TGA RLE Emmanuel Julien - initial file IO callback implementation Jon Olick - original jo_jpeg.cpp code Daniel Gibson - integrate JPEG, allow external zlib Aarni Koskela - allow choosing PNG filter bugfixes: github:Chribba Guillaume Chereau github:jry2 github:romigrou Sergio Gonzalez Jonas Karlsson Filip Wasil Thatcher Ulrich github:poppolopoppo Patrick Boettcher github:xeekworx Cap Petschulat Simon Rodriguez Ivan Tikhonov github:ignotion Adam Schackart LICENSE See end of file for license information. */ #ifndef INCLUDE_STB_IMAGE_WRITE_H #define INCLUDE_STB_IMAGE_WRITE_H // if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' #ifndef STBIWDEF #ifdef STB_IMAGE_WRITE_STATIC #define STBIWDEF static #else #ifdef __cplusplus #define STBIWDEF extern "C" #else #define STBIWDEF extern #endif #endif #endif #ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations extern int stbi_write_tga_with_rle; extern int stbi_write_png_compression_level; extern int stbi_write_force_png_filter; #endif #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); #endif typedef void stbi_write_func(void *context, void *data, int size); STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); #endif//INCLUDE_STB_IMAGE_WRITE_H #ifdef STB_IMAGE_WRITE_IMPLEMENTATION #ifdef _WIN32 #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #ifndef _CRT_NONSTDC_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE #endif #endif #ifndef STBI_WRITE_NO_STDIO #include #endif // STBI_WRITE_NO_STDIO #include #include #include #include #if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) // ok #elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) // ok #else #error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." #endif #ifndef STBIW_MALLOC #define STBIW_MALLOC(sz) malloc(sz) #define STBIW_REALLOC(p,newsz) realloc(p,newsz) #define STBIW_FREE(p) free(p) #endif #ifndef STBIW_REALLOC_SIZED #define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) #endif #ifndef STBIW_MEMMOVE #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) #endif #ifndef STBIW_ASSERT #include #define STBIW_ASSERT(x) assert(x) #endif #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) #ifdef STB_IMAGE_WRITE_STATIC static int stbi__flip_vertically_on_write=0; static int stbi_write_png_compression_level = 8; static int stbi_write_tga_with_rle = 1; static int stbi_write_force_png_filter = -1; #else int stbi_write_png_compression_level = 8; int stbi__flip_vertically_on_write=0; int stbi_write_tga_with_rle = 1; int stbi_write_force_png_filter = -1; #endif STBIWDEF void stbi_flip_vertically_on_write(int flag) { stbi__flip_vertically_on_write = flag; } typedef struct { stbi_write_func *func; void *context; } stbi__write_context; // initialize a callback-based context static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) { s->func = c; s->context = context; } #ifndef STBI_WRITE_NO_STDIO static void stbi__stdio_write(void *context, void *data, int size) { fwrite(data,1,size,(FILE*) context); } static int stbi__start_write_file(stbi__write_context *s, const char *filename) { FILE *f; #ifdef STBI_MSC_SECURE_CRT if (fopen_s(&f, filename, "wb")) f = NULL; #else f = fopen(filename, "wb"); #endif stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); return f != NULL; } static void stbi__end_write_file(stbi__write_context *s) { fclose((FILE *)s->context); } #endif // !STBI_WRITE_NO_STDIO typedef unsigned int stbiw_uint32; typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) { while (*fmt) { switch (*fmt++) { case ' ': break; case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); s->func(s->context,&x,1); break; } case '2': { int x = va_arg(v,int); unsigned char b[2]; b[0] = STBIW_UCHAR(x); b[1] = STBIW_UCHAR(x>>8); s->func(s->context,b,2); break; } case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4]; b[0]=STBIW_UCHAR(x); b[1]=STBIW_UCHAR(x>>8); b[2]=STBIW_UCHAR(x>>16); b[3]=STBIW_UCHAR(x>>24); s->func(s->context,b,4); break; } default: STBIW_ASSERT(0); return; } } } static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) { va_list v; va_start(v, fmt); stbiw__writefv(s, fmt, v); va_end(v); } static void stbiw__putc(stbi__write_context *s, unsigned char c) { s->func(s->context, &c, 1); } static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) { unsigned char arr[3]; arr[0] = a; arr[1] = b; arr[2] = c; s->func(s->context, arr, 3); } static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) { unsigned char bg[3] = { 255, 0, 255}, px[3]; int k; if (write_alpha < 0) s->func(s->context, &d[comp - 1], 1); switch (comp) { case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case case 1: if (expand_mono) stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp else s->func(s->context, d, 1); // monochrome TGA break; case 4: if (!write_alpha) { // composite against pink background for (k = 0; k < 3; ++k) px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); break; } /* FALLTHROUGH */ case 3: stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); break; } if (write_alpha > 0) s->func(s->context, &d[comp - 1], 1); } static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) { stbiw_uint32 zero = 0; int i,j, j_end; if (y <= 0) return; if (stbi__flip_vertically_on_write) vdir *= -1; if (vdir < 0) { j_end = -1; j = y-1; } else { j_end = y; j = 0; } for (; j != j_end; j += vdir) { for (i=0; i < x; ++i) { unsigned char *d = (unsigned char *) data + (j*x+i)*comp; stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); } s->func(s->context, &zero, scanline_pad); } } static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) { if (y < 0 || x < 0) { return 0; } else { va_list v; va_start(v, fmt); stbiw__writefv(s, fmt, v); va_end(v); stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); return 1; } } static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) { int pad = (-x*3) & 3; return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, "11 4 22 4" "4 44 22 444444", 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header } STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { stbi__write_context s; stbi__start_write_callbacks(&s, func, context); return stbi_write_bmp_core(&s, x, y, comp, data); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) { stbi__write_context s; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_bmp_core(&s, x, y, comp, data); stbi__end_write_file(&s); return r; } else return 0; } #endif //!STBI_WRITE_NO_STDIO static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) { int has_alpha = (comp == 2 || comp == 4); int colorbytes = has_alpha ? comp-1 : comp; int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 if (y < 0 || x < 0) return 0; if (!stbi_write_tga_with_rle) { return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); } else { int i,j,k; int jend, jdir; stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); if (stbi__flip_vertically_on_write) { j = 0; jend = y; jdir = 1; } else { j = y-1; jend = -1; jdir = -1; } for (; j != jend; j += jdir) { unsigned char *row = (unsigned char *) data + j * x * comp; int len; for (i = 0; i < x; i += len) { unsigned char *begin = row + i * comp; int diff = 1; len = 1; if (i < x - 1) { ++len; diff = memcmp(begin, row + (i + 1) * comp, comp); if (diff) { const unsigned char *prev = begin; for (k = i + 2; k < x && len < 128; ++k) { if (memcmp(prev, row + k * comp, comp)) { prev += comp; ++len; } else { --len; break; } } } else { for (k = i + 2; k < x && len < 128; ++k) { if (!memcmp(begin, row + k * comp, comp)) { ++len; } else { break; } } } } if (diff) { unsigned char header = STBIW_UCHAR(len - 1); s->func(s->context, &header, 1); for (k = 0; k < len; ++k) { stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); } } else { unsigned char header = STBIW_UCHAR(len - 129); s->func(s->context, &header, 1); stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); } } } } return 1; } STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { stbi__write_context s; stbi__start_write_callbacks(&s, func, context); return stbi_write_tga_core(&s, x, y, comp, (void *) data); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) { stbi__write_context s; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); stbi__end_write_file(&s); return r; } else return 0; } #endif // ************************************************************************************************* // Radiance RGBE HDR writer // by Baldur Karlsson #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) { int exponent; float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); if (maxcomp < 1e-32f) { rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; } else { float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; rgbe[0] = (unsigned char)(linear[0] * normalize); rgbe[1] = (unsigned char)(linear[1] * normalize); rgbe[2] = (unsigned char)(linear[2] * normalize); rgbe[3] = (unsigned char)(exponent + 128); } } void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) { unsigned char lengthbyte = STBIW_UCHAR(length+128); STBIW_ASSERT(length+128 <= 255); s->func(s->context, &lengthbyte, 1); s->func(s->context, &databyte, 1); } void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) { unsigned char lengthbyte = STBIW_UCHAR(length); STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code s->func(s->context, &lengthbyte, 1); s->func(s->context, data, length); } void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) { unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; unsigned char rgbe[4]; float linear[3]; int x; scanlineheader[2] = (width&0xff00)>>8; scanlineheader[3] = (width&0x00ff); /* skip RLE for images too small or large */ if (width < 8 || width >= 32768) { for (x=0; x < width; x++) { switch (ncomp) { case 4: /* fallthrough */ case 3: linear[2] = scanline[x*ncomp + 2]; linear[1] = scanline[x*ncomp + 1]; linear[0] = scanline[x*ncomp + 0]; break; default: linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; break; } stbiw__linear_to_rgbe(rgbe, linear); s->func(s->context, rgbe, 4); } } else { int c,r; /* encode into scratch buffer */ for (x=0; x < width; x++) { switch(ncomp) { case 4: /* fallthrough */ case 3: linear[2] = scanline[x*ncomp + 2]; linear[1] = scanline[x*ncomp + 1]; linear[0] = scanline[x*ncomp + 0]; break; default: linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; break; } stbiw__linear_to_rgbe(rgbe, linear); scratch[x + width*0] = rgbe[0]; scratch[x + width*1] = rgbe[1]; scratch[x + width*2] = rgbe[2]; scratch[x + width*3] = rgbe[3]; } s->func(s->context, scanlineheader, 4); /* RLE each component separately */ for (c=0; c < 4; c++) { unsigned char *comp = &scratch[width*c]; x = 0; while (x < width) { // find first run r = x; while (r+2 < width) { if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) break; ++r; } if (r+2 >= width) r = width; // dump up to first run while (x < r) { int len = r-x; if (len > 128) len = 128; stbiw__write_dump_data(s, len, &comp[x]); x += len; } // if there's a run, output it if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd // find next byte after run while (r < width && comp[r] == comp[x]) ++r; // output run up to r while (x < r) { int len = r-x; if (len > 127) len = 127; stbiw__write_run_data(s, len, comp[x]); x += len; } } } } } } static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) { if (y <= 0 || x <= 0 || data == NULL) return 0; else { // Each component is stored separately. Allocate scratch space for full output scanline. unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); int i, len; char buffer[128]; char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; s->func(s->context, header, sizeof(header)-1); #ifdef STBI_MSC_SECURE_CRT len = sprintf_s(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #else len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #endif s->func(s->context, buffer, len); for(i=0; i < y; i++) stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)*x); STBIW_FREE(scratch); return 1; } } STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) { stbi__write_context s; stbi__start_write_callbacks(&s, func, context); return stbi_write_hdr_core(&s, x, y, comp, (float *) data); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) { stbi__write_context s; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); stbi__end_write_file(&s); return r; } else return 0; } #endif // STBI_WRITE_NO_STDIO ////////////////////////////////////////////////////////////////////////////// // // PNG writer // #ifndef STBIW_ZLIB_COMPRESS // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() #define stbiw__sbraw(a) ((int *) (a) - 2) #define stbiw__sbm(a) stbiw__sbraw(a)[0] #define stbiw__sbn(a) stbiw__sbraw(a)[1] #define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) #define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) #define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) #define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) #define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) #define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) { int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); STBIW_ASSERT(p); if (p) { if (!*arr) ((int *) p)[1] = 0; *arr = (void *) ((int *) p + 2); stbiw__sbm(*arr) = m; } return *arr; } static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) { while (*bitcount >= 8) { stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); *bitbuffer >>= 8; *bitcount -= 8; } return data; } static int stbiw__zlib_bitrev(int code, int codebits) { int res=0; while (codebits--) { res = (res << 1) | (code & 1); code >>= 1; } return res; } static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) { int i; for (i=0; i < limit && i < 258; ++i) if (a[i] != b[i]) break; return i; } static unsigned int stbiw__zhash(unsigned char *data) { stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); hash ^= hash << 3; hash += hash >> 5; hash ^= hash << 4; hash += hash >> 17; hash ^= hash << 25; hash += hash >> 6; return hash; } #define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) #define stbiw__zlib_add(code,codebits) \ (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) #define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) // default huffman tables #define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) #define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) #define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) #define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) #define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) #define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) #define stbiw__ZHASH 16384 #endif // STBIW_ZLIB_COMPRESS unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) { #ifdef STBIW_ZLIB_COMPRESS // user provided a zlib compress implementation, use that return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); #else // use builtin static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; unsigned int bitbuf=0; int i,j, bitcount=0; unsigned char *out = NULL; unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); if (hash_table == NULL) return NULL; if (quality < 5) quality = 5; stbiw__sbpush(out, 0x78); // DEFLATE 32K window stbiw__sbpush(out, 0x5e); // FLEVEL = 1 stbiw__zlib_add(1,1); // BFINAL = 1 stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman for (i=0; i < stbiw__ZHASH; ++i) hash_table[i] = NULL; i=0; while (i < data_len-3) { // hash next 3 bytes of data to be compressed int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; unsigned char *bestloc = 0; unsigned char **hlist = hash_table[h]; int n = stbiw__sbcount(hlist); for (j=0; j < n; ++j) { if (hlist[j]-data > i-32768) { // if entry lies within window int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); if (d >= best) { best=d; bestloc=hlist[j]; } } } // when hash table entry is too long, delete half the entries if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); stbiw__sbn(hash_table[h]) = quality; } stbiw__sbpush(hash_table[h],data+i); if (bestloc) { // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); hlist = hash_table[h]; n = stbiw__sbcount(hlist); for (j=0; j < n; ++j) { if (hlist[j]-data > i-32767) { int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); if (e > best) { // if next match is better, bail on current match bestloc = NULL; break; } } } } if (bestloc) { int d = (int) (data+i - bestloc); // distance back STBIW_ASSERT(d <= 32767 && best <= 258); for (j=0; best > lengthc[j+1]-1; ++j); stbiw__zlib_huff(j+257); if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); for (j=0; d > distc[j+1]-1; ++j); stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); i += best; } else { stbiw__zlib_huffb(data[i]); ++i; } } // write out final bytes for (;i < data_len; ++i) stbiw__zlib_huffb(data[i]); stbiw__zlib_huff(256); // end of block // pad with 0 bits to byte boundary while (bitcount) stbiw__zlib_add(0,1); for (i=0; i < stbiw__ZHASH; ++i) (void) stbiw__sbfree(hash_table[i]); STBIW_FREE(hash_table); { // compute adler32 on input unsigned int s1=1, s2=0; int blocklen = (int) (data_len % 5552); j=0; while (j < data_len) { for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } s1 %= 65521; s2 %= 65521; j += blocklen; blocklen = 5552; } stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); stbiw__sbpush(out, STBIW_UCHAR(s2)); stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); stbiw__sbpush(out, STBIW_UCHAR(s1)); } *out_len = stbiw__sbn(out); // make returned pointer freeable STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); return (unsigned char *) stbiw__sbraw(out); #endif // STBIW_ZLIB_COMPRESS } static unsigned int stbiw__crc32(unsigned char *buffer, int len) { static unsigned int crc_table[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; unsigned int crc = ~0u; int i; for (i=0; i < len; ++i) crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; return ~crc; } #define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) #define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); #define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) static void stbiw__wpcrc(unsigned char **data, int len) { unsigned int crc = stbiw__crc32(*data - len - 4, len+4); stbiw__wp32(*data, crc); } static unsigned char stbiw__paeth(int a, int b, int c) { int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); if (pb <= pc) return STBIW_UCHAR(b); return STBIW_UCHAR(c); } // @OPTIMIZE: provide an option that always forces left-predict or paeth predict static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) { static int mapping[] = { 0,1,2,3,4 }; static int firstmap[] = { 0,1,0,5,6 }; int *mymap = (y != 0) ? mapping : firstmap; int i; int type = mymap[filter_type]; unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; for (i = 0; i < n; ++i) { switch (type) { case 0: line_buffer[i] = z[i]; break; case 1: line_buffer[i] = z[i]; break; case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; case 5: line_buffer[i] = z[i]; break; case 6: line_buffer[i] = z[i]; break; } } for (i=n; i < width*n; ++i) { switch (type) { case 0: line_buffer[i] = z[i]; break; case 1: line_buffer[i] = z[i] - z[i-n]; break; case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; } } } unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) { int force_filter = stbi_write_force_png_filter; int ctype[5] = { -1, 0, 4, 2, 6 }; unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; unsigned char *out,*o, *filt, *zlib; signed char *line_buffer; int j,zlen; if (stride_bytes == 0) stride_bytes = x * n; if (force_filter >= 5) { force_filter = -1; } filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } for (j=0; j < y; ++j) { int filter_type; if (force_filter > -1) { filter_type = force_filter; stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, force_filter, line_buffer); } else { // Estimate the best filter by running through all of them: int best_filter = 0, best_filter_val = 0x7fffffff, est, i; for (filter_type = 0; filter_type < 5; filter_type++) { stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, filter_type, line_buffer); // Estimate the entropy of the line using this filter; the less, the better. est = 0; for (i = 0; i < x*n; ++i) { est += abs((signed char) line_buffer[i]); } if (est < best_filter_val) { best_filter_val = est; best_filter = filter_type; } } if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, best_filter, line_buffer); filter_type = best_filter; } } // when we get here, filter_type contains the filter type, and line_buffer contains the data filt[j*(x*n+1)] = (unsigned char) filter_type; STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); } STBIW_FREE(line_buffer); zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); STBIW_FREE(filt); if (!zlib) return 0; // each tag requires 12 bytes of overhead out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); if (!out) return 0; *out_len = 8 + 12+13 + 12+zlen + 12; o=out; STBIW_MEMMOVE(o,sig,8); o+= 8; stbiw__wp32(o, 13); // header length stbiw__wptag(o, "IHDR"); stbiw__wp32(o, x); stbiw__wp32(o, y); *o++ = 8; *o++ = STBIW_UCHAR(ctype[n]); *o++ = 0; *o++ = 0; *o++ = 0; stbiw__wpcrc(&o,13); stbiw__wp32(o, zlen); stbiw__wptag(o, "IDAT"); STBIW_MEMMOVE(o, zlib, zlen); o += zlen; STBIW_FREE(zlib); stbiw__wpcrc(&o, zlen); stbiw__wp32(o,0); stbiw__wptag(o, "IEND"); stbiw__wpcrc(&o,0); STBIW_ASSERT(o == out + *out_len); return out; } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) { FILE *f; int len; unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); if (png == NULL) return 0; #ifdef STBI_MSC_SECURE_CRT if (fopen_s(&f, filename, "wb")) f = NULL; #else f = fopen(filename, "wb"); #endif if (!f) { STBIW_FREE(png); return 0; } fwrite(png, 1, len, f); fclose(f); STBIW_FREE(png); return 1; } #endif STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) { int len; unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); if (png == NULL) return 0; func(context, png, len); STBIW_FREE(png); return 1; } /* *************************************************************************** * * JPEG writer * * This is based on Jon Olick's jo_jpeg.cpp: * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html */ static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { int bitBuf = *bitBufP, bitCnt = *bitCntP; bitCnt += bs[1]; bitBuf |= bs[0] << (24 - bitCnt); while(bitCnt >= 8) { unsigned char c = (bitBuf >> 16) & 255; stbiw__putc(s, c); if(c == 255) { stbiw__putc(s, 0); } bitBuf <<= 8; bitCnt -= 8; } *bitBufP = bitBuf; *bitCntP = bitCnt; } static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; float z1, z2, z3, z4, z5, z11, z13; float tmp0 = d0 + d7; float tmp7 = d0 - d7; float tmp1 = d1 + d6; float tmp6 = d1 - d6; float tmp2 = d2 + d5; float tmp5 = d2 - d5; float tmp3 = d3 + d4; float tmp4 = d3 - d4; // Even part float tmp10 = tmp0 + tmp3; // phase 2 float tmp13 = tmp0 - tmp3; float tmp11 = tmp1 + tmp2; float tmp12 = tmp1 - tmp2; d0 = tmp10 + tmp11; // phase 3 d4 = tmp10 - tmp11; z1 = (tmp12 + tmp13) * 0.707106781f; // c4 d2 = tmp13 + z1; // phase 5 d6 = tmp13 - z1; // Odd part tmp10 = tmp4 + tmp5; // phase 2 tmp11 = tmp5 + tmp6; tmp12 = tmp6 + tmp7; // The rotator is modified from fig 4-8 to avoid extra negations. z5 = (tmp10 - tmp12) * 0.382683433f; // c6 z2 = tmp10 * 0.541196100f + z5; // c2-c6 z4 = tmp12 * 1.306562965f + z5; // c2+c6 z3 = tmp11 * 0.707106781f; // c4 z11 = tmp7 + z3; // phase 5 z13 = tmp7 - z3; *d5p = z13 + z2; // phase 6 *d3p = z13 - z2; *d1p = z11 + z4; *d7p = z11 - z4; *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; } static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { int tmp1 = val < 0 ? -val : val; val = val < 0 ? val-1 : val; bits[1] = 1; while(tmp1 >>= 1) { ++bits[1]; } bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { } // end0pos = first element in reverse order !=0 if(end0pos == 0) { stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); return DU[0]; } for(i = 1; i <= end0pos; ++i) { int startpos = i; int nrzeroes; unsigned short bits[2]; for (; DU[i]==0 && i<=end0pos; ++i) { } nrzeroes = i-startpos; if ( nrzeroes >= 16 ) { int lng = nrzeroes>>4; int nrmarker; for (nrmarker=1; nrmarker <= lng; ++nrmarker) stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); nrzeroes &= 15; } stbiw__jpg_calcBits(DU[i], bits); stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); } if(end0pos != 63) { stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); } return DU[0]; } static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { // Constants that don't pollute global namespace static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; static const unsigned char std_ac_luminance_values[] = { 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa }; static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; static const unsigned char std_ac_chrominance_values[] = { 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa }; // Huffman tables static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; static const unsigned short YAC_HT[256][2] = { {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} }; static const unsigned short UVAC_HT[256][2] = { {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} }; static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; int row, col, i, k; float fdtbl_Y[64], fdtbl_UV[64]; unsigned char YTable[64], UVTable[64]; if(!data || !width || !height || comp > 4 || comp < 1) { return 0; } quality = quality ? quality : 90; quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; quality = quality < 50 ? 5000 / quality : 200 - quality * 2; for(i = 0; i < 64; ++i) { int uvti, yti = (YQT[i]*quality+50)/100; YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); uvti = (UVQT[i]*quality+50)/100; UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); } for(row = 0, k = 0; row < 8; ++row) { for(col = 0; col < 8; ++col, ++k) { fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); } } // Write Headers { static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), 3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; s->func(s->context, (void*)head0, sizeof(head0)); s->func(s->context, (void*)YTable, sizeof(YTable)); stbiw__putc(s, 1); s->func(s->context, UVTable, sizeof(UVTable)); s->func(s->context, (void*)head1, sizeof(head1)); s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); stbiw__putc(s, 0x10); // HTYACinfo s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); stbiw__putc(s, 1); // HTUDCinfo s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); stbiw__putc(s, 0x11); // HTUACinfo s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); s->func(s->context, (void*)head2, sizeof(head2)); } // Encode 8x8 macroblocks { static const unsigned short fillBits[] = {0x7F, 7}; const unsigned char *imageData = (const unsigned char *)data; int DCY=0, DCU=0, DCV=0; int bitBuf=0, bitCnt=0; // comp == 2 is grey+alpha (alpha is ignored) int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; int x, y, pos; for(y = 0; y < height; y += 8) { for(x = 0; x < width; x += 8) { float YDU[64], UDU[64], VDU[64]; for(row = y, pos = 0; row < y+8; ++row) { for(col = x; col < x+8; ++col, ++pos) { int p = (stbi__flip_vertically_on_write ? height-1-row : row)*width*comp + col*comp; float r, g, b; if(row >= height) { p -= width*comp*(row+1 - height); } if(col >= width) { p -= comp*(col+1 - width); } r = imageData[p+0]; g = imageData[p+ofsG]; b = imageData[p+ofsB]; YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128; UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b; VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b; } } DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); } } // Do the bit alignment of the EOI marker stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); } // EOI stbiw__putc(s, 0xFF); stbiw__putc(s, 0xD9); return 1; } STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) { stbi__write_context s; stbi__start_write_callbacks(&s, func, context); return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) { stbi__write_context s; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); stbi__end_write_file(&s); return r; } else return 0; } #endif #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history 1.09 (2018-02-11) fix typo in zlib quality API, improve STB_I_W_STATIC in C++ 1.08 (2018-01-29) add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter 1.07 (2017-07-24) doc fix 1.06 (2017-07-23) writing JPEG (using Jon Olick's code) 1.05 ??? 1.04 (2017-03-03) monochrome BMP expansion 1.03 ??? 1.02 (2016-04-02) avoid allocating large structures on the stack 1.01 (2016-01-16) STBIW_REALLOC_SIZED: support allocators with no realloc support avoid race-condition in crc initialization minor compile issues 1.00 (2015-09-14) installable file IO function 0.99 (2015-09-13) warning fixes; TGA rle support 0.98 (2015-04-08) added STBIW_MALLOC, STBIW_ASSERT etc 0.97 (2015-01-18) fixed HDR asserts, rewrote HDR rle logic 0.96 (2015-01-17) add HDR output fix monochrome BMP 0.95 (2014-08-17) add monochrome TGA output 0.94 (2014-05-31) rename private functions to avoid conflicts with stb_image.h 0.93 (2014-05-27) warning fixes 0.92 (2010-08-01) casts to unsigned char to fix warnings 0.91 (2010-07-17) first public release 0.90 first internal release */ /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ ================================================ FILE: sdl/system_paths.c ================================================ // // Copyright 2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "system_paths.h" #include #include #if defined(_WIN32) #include #endif void desktop_path(char *buffer, size_t size) { #if defined(__APPLE__) && defined(__MACH__) strncpy(buffer, getenv("HOME"), size - 1); strncat(buffer, "/Desktop/", size - 1); #elif defined(_WIN32) strncpy(buffer, getenv("USERPROFILE"), size - 1); strncat(buffer, "\\Desktop\\", size - 1); #elif defined(__EMSCRIPTEN__) strncpy(buffer, "", size - 1); #elif defined(__LINUX__) strncpy(buffer, getenv("HOME"), size - 1); strncat(buffer, "/Desktop/", size - 1); #else #error Not implemented yet #endif } FILE* fopen_utf8(const char* filename, const char* mode) { #if defined(_WIN32) WCHAR nameW[FILENAME_MAX] = { 0 }; WCHAR modeW[16] = { 0 }; int len = MultiByteToWideChar(CP_UTF8, 0, filename, -1, nameW, FILENAME_MAX); if (len > 0 && MultiByteToWideChar(CP_UTF8, 0, mode, -1, modeW, 16) > 0) { FILE* ret = NULL; if (_wfopen_s(&ret, nameW, modeW) == 0) return ret; } return NULL; #else return fopen(filename, mode); #endif } ================================================ FILE: sdl/system_paths.h ================================================ // // Copyright 2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef system_paths_h #define system_paths_h #include #ifdef _WIN32 #define PATH_SEPARATOR "\\" #define PATH_SEPARATOR_CHAR '\\' #else #define PATH_SEPARATOR "/" #define PATH_SEPARATOR_CHAR '/' #endif void desktop_path(char *buffer, size_t size); FILE* fopen_utf8(const char* filename, const char* mode); #endif /* system_paths_h */ ================================================ FILE: sdl/utils.c ================================================ // // Copyright 2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #include "utils.h" #include "system_paths.h" #include void displayName(const char *filename, char *destination, size_t size) { memset(destination, 0, size); const char *nameStart = filename; char *slash = strrchr(filename, PATH_SEPARATOR_CHAR); if (slash) { nameStart = slash + 1; } strncpy(destination, nameStart, size - 1); char *dot = strrchr(nameStart, '.'); if (dot) { int dotIndex = (int)(dot - nameStart); if (dotIndex < size) { destination[dotIndex] = 0; } } } bool hasPostfix(const char *string, const char *postfix) { size_t stringLen = strlen(string); size_t postfixLen = strlen(postfix); if (postfixLen <= stringLen) { string = string + stringLen - postfixLen; return strcmp(string, postfix) == 0; } return false; } ================================================ FILE: sdl/utils.h ================================================ // // Copyright 2018 Timo Kloss // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source distribution. // #ifndef utils_h #define utils_h #include #include void displayName(const char *filename, char *destination, size_t size); bool hasPostfix(const char *string, const char *postfix); #endif /* utils_h */