├── .gitignore ├── .gitmodules ├── .vscode ├── c_cpp_properties.json ├── launch.json └── tasks.json ├── Docs ├── WonderSwanTech.txt └── wsrtc01.txt ├── History.txt ├── Makefile ├── NitroSwan.code-workspace ├── NitroSwan.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── xcshareddata │ └── xcschemes │ └── NitroSwan.xcscheme ├── NitroSwan_todo.txt ├── README.md ├── WSLogo.bmp ├── WonderWitch.md ├── copytodsx.bat ├── graphics ├── PCV2Border.grit ├── PCV2Border.png ├── SCBorder.grit ├── SCBorder.png ├── SCBottom.grit ├── SCBottom.png ├── WSBorder.grit ├── WSBorder.png ├── WSBottom.grit ├── WSBottom.png ├── WSCBorder.grit ├── WSCBorder.png ├── WSCBottom.grit └── WSCBottom.png ├── include └── wsroms │ ├── wc_irom.bin │ ├── ws_irom.bin │ └── wsc_irom.bin ├── logo.png └── source ├── Cart.h ├── Cart.s ├── Emubase.h ├── FileHandling.c ├── FileHandling.h ├── Gfx.h ├── Gfx.s ├── Gui.c ├── Gui.h ├── InternalEEPROM.h ├── Main.c ├── Main.h ├── Memory.h ├── Memory.s ├── Sound.h ├── Sound.s ├── SpeedHacks.s ├── WonderSwan.c ├── WonderSwan.h ├── WonderWitch.c ├── WonderWitch.h ├── cpu.h ├── cpu.s ├── io.h └── io.s /.gitignore: -------------------------------------------------------------------------------- 1 | NitroSwan.xcodeproj/xcuserdata 2 | NitroSwan.xcodeproj/project.xcworkspace/xcuserdata 3 | contents.xcworkspacedata 4 | build 5 | *.elf 6 | *.nds 7 | *.zip 8 | *.ws 9 | *.wsc 10 | *.rom 11 | *.pc2 12 | *.eeprom 13 | *.ram 14 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "source/Shared"] 2 | path = source/Shared 3 | url = https://github.com/FluBBaOfWard/NDS_Shared.git 4 | [submodule "source/ARMV30MZ"] 5 | path = source/ARMV30MZ 6 | url = https://github.com/FluBBaOfWard/ARMV30MZ.git 7 | [submodule "source/Sphinx"] 8 | path = source/Sphinx 9 | url=https://github.com/FluBBaOfWard/Sphinx.git 10 | [submodule "source/WSCart"] 11 | path = source/WSCart 12 | url = https://github.com/FluBBaOfWard/WSCart.git 13 | [submodule "source/WSEEPROM"] 14 | path = source/WSEEPROM 15 | url = https://github.com/FluBBaOfWard/WSEEPROM.git 16 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Mac", 5 | "includePath": [ 6 | "/usr/local/include", 7 | "${workspaceFolder}", 8 | "${workspaceFolder}/include", 9 | "${workspaceFolder}/build", 10 | "/opt/devkitPro/libnds/include", 11 | "/opt/devkitPro/devkitARM/arm-none-eabi/include", 12 | "/opt/devkitPro/devkitARM/lib/gcc/arm-none-eabi/10.2.0/include" 13 | ], 14 | "defines": [], 15 | "intelliSenseMode": "clang-x64", 16 | "browse": { 17 | "path": [ 18 | "/usr/local/include", 19 | "${workspaceFolder}", 20 | "${workspaceFolder}/include", 21 | "/opt/devkitPro/libnds/include", 22 | "/opt/devkitPro/devkitARM/arm-none-eabi/include", 23 | "/opt/devkitPro/devkitARM/lib/gcc/arm-none-eabi/10.2.0/include" 24 | ], 25 | "limitSymbolsToIncludedHeaders": true, 26 | "databaseFilename": "" 27 | }, 28 | "macFrameworkPath": [ 29 | "/System/Library/Frameworks", 30 | "/Library/Frameworks" 31 | ] 32 | }, 33 | { 34 | "name": "Linux", 35 | "includePath": [ 36 | "/usr/include", 37 | "/usr/local/include", 38 | "${workspaceFolder}/include" 39 | ], 40 | "defines": [], 41 | "intelliSenseMode": "clang-x64", 42 | "browse": { 43 | "path": [ 44 | "/usr/include", 45 | "/usr/local/include", 46 | "${workspaceFolder}/include" 47 | ], 48 | "limitSymbolsToIncludedHeaders": true, 49 | "databaseFilename": "" 50 | } 51 | }, 52 | { 53 | "name": "Win32", 54 | "includePath": [ 55 | "${workspaceFolder}", 56 | "${workspaceFolder}/include", 57 | "C:/devkitPro/devkitARM/arm-none-eabi/include", 58 | "C:/devkitPro/devkitARM/lib/gcc/arm-none-eabi/10.2.0/include" 59 | ], 60 | "defines": [ 61 | "_DEBUG", 62 | "UNICODE", 63 | "_UNICODE", 64 | "ARM7", 65 | "WIN32" 66 | ], 67 | "intelliSenseMode": "msvc-x64", 68 | "browse": { 69 | "path": [ 70 | "${workspaceFolder}", 71 | "${workspaceFolder}/include", 72 | "C:/devkitPro/devkitARM/arm-none-eabi/include", 73 | "C:/devkitPro/devkitARM/lib/gcc/arm-none-eabi/10.2.0/include" 74 | ], 75 | "limitSymbolsToIncludedHeaders": true, 76 | "databaseFilename": "" 77 | }, 78 | "cStandard": "c11", 79 | "cppStandard": "c++17" 80 | } 81 | ], 82 | "version": 4 83 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "2.0.0", 6 | "configurations": [ 7 | { 8 | "name": "(gdb) Launch", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "preLaunchTask": "gdb-debug", 12 | "postDebugTask": "stop emulation", 13 | "serverLaunchTimeout": 10000, 14 | "stopAtEntry": true, 15 | "program": "${workspaceFolder}/${workspaceFolderBasename}.elf", 16 | "MIMode": "gdb", 17 | "externalConsole": true, 18 | "cwd": "${workspaceFolder}", 19 | "targetArchitecture": "arm", 20 | "miDebuggerServerAddress": "localhost:3333", 21 | "windows": { 22 | "miDebuggerPath": "C:/devkitPro/devkitARM/bin/arm-none-eabi-gdb.exe" 23 | }, 24 | "osx":{ 25 | "miDebuggerPath": "/opt/devkitPro/devkitARM/bin/arm-none-eabi-gdb" 26 | }, 27 | "setupCommands": [ 28 | { 29 | "description": "Enable pretty-printing for gdb", 30 | "ignoreFailures": true, 31 | "text": "file ${workspaceFolder}/${workspaceFolderBasename}.elf -enable-pretty-printing" 32 | } 33 | ] 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "stop emulation", 8 | "type": "shell", 9 | "windows": { 10 | "command": "taskkill /im mGBA.exe /F" 11 | }, 12 | "osx": { 13 | "command": "killall melonDS" 14 | } 15 | }, 16 | { 17 | "label": "make debug", 18 | "type": "process", 19 | "command": "make", 20 | "args": [ 21 | "DEBUG=1" 22 | ], 23 | "problemMatcher": [] 24 | }, 25 | { 26 | "label": "make release", 27 | "type": "process", 28 | "command": "make", 29 | "args": [ 30 | "DEBUG=0" 31 | ], 32 | "problemMatcher": [], 33 | "group": { 34 | "kind": "build", 35 | "isDefault": true 36 | } 37 | }, 38 | { 39 | "label": "clean", 40 | "type": "shell", 41 | "command": "make clean" 42 | }, 43 | { 44 | "label": "gdb-debug", 45 | "type": "shell", 46 | "dependsOn": [ 47 | "make debug" 48 | ], 49 | "isBackground": false, 50 | "windows": { 51 | "command": "C:/mGBA/mGBA.exe -g ${workspaceFolder}/${workspaceFolderBasename}.nds;sleep 5;echo debuggerReady" 52 | }, 53 | "osx": { 54 | "command": "/Applications/melonDS.app/Contents/MacOS/melonDS", 55 | "args": [ 56 | "${workspaceFolder}/${workspaceFolderBasename}.nds" 57 | ] 58 | }, 59 | "presentation": { 60 | "clear": true, 61 | "reveal": "always", 62 | "panel": "new" 63 | }, 64 | "command": "debuggerReady", 65 | "problemMatcher": { 66 | "pattern": [ 67 | { 68 | "regexp": ".", 69 | "file": 1, 70 | "location": 2, 71 | "message": 3 72 | } 73 | ], 74 | "background": { 75 | "activeOnStart": true, 76 | "beginsPattern": "^.*debuggerReady.*$", 77 | "endsPattern": "^.*debuggerReady.*$" 78 | } 79 | } 80 | }, 81 | { 82 | "label": "run", 83 | "type": "shell", 84 | "dependsOn": [ 85 | "make release" 86 | ], 87 | "isBackground": true, 88 | "windows": { 89 | "command": "C:/NO$GBA/NO$GBA.exe ${workspaceFolder}/${workspaceFolderBasename}.nds", 90 | }, 91 | "osx": { 92 | "command": "/Applications/melonDS.app/Contents/MacOS/melonDS ${workspaceFolder}/${workspaceFolderBasename}.nds", 93 | }, 94 | "problemMatcher": [] 95 | } 96 | ] 97 | } -------------------------------------------------------------------------------- /Docs/wsrtc01.txt: -------------------------------------------------------------------------------- 1 | How to Program the Wonderswan's Real Time Clock (e.g. w/ Wonderwitch) 2 | v0.1 3 | 4 | by zalas 5 | 6 | Overview: 7 | 8 | The RTC for the Wonderswan contains information about the current date/time, etc. 9 | 10 | Ports: 11 | 12 | 0xCA - this is the command port, when commands get written here, they get executed immediately 13 | when read, this port will yield an asserted high bit for acknowledgement, the argument 14 | byte must be in 0xCB upon writing a command to 0xCA or else it will miss it 15 | 0xCB - this is the data port, for passing arguments and for reading, etc. 16 | 17 | Commands: 18 | 19 | 0x10 - Reset 20 | 21 | Write 0x10 to port 0xCA 22 | Wait for ACK from 0xCA 23 | 24 | 0x12 - Write Timer Settings 25 | 26 | Write timer setting byte to 0xCB 27 | Write 0x12 to port 0xCA 28 | Wait for ACK from 0xCA 29 | 30 | 0x13 - Read Timer Settings 31 | 32 | Write 0x13 to port 0xCA 33 | Wait for ACK from 0xCA 34 | Read timer settings byte from 0xCB 35 | 36 | 0x14 - Set Time/Date 37 | 38 | Write first byte of time/date structure into 0xCB 39 | Write 0x14 into 0xCA 40 | Wait for ACK 41 | Write second byte into 0xCB 42 | Wait for ACK 43 | repeat until all 7 bytes have been entered 44 | 45 | 0x15 - Get Time/Date 46 | 47 | Write 0x15 into 0xCA 48 | Wait for ACK 49 | Read byte from 0xCB 50 | Repeat above 2 steps 7 times 51 | 52 | Data Structure: 53 | 54 | Time Settings Byte: 55 | MSB LSB 56 | xwaxxxxx 57 | w - should always be set? 58 | a - should be set to enable RTC alarm (in addition to interrupt enable) 59 | 60 | Time/Date Structure (all of the following fields are in BCD format) 61 | 62 | 0000 - Year (in addition to 2000) 63 | 0001 - Month 64 | 0002 - Day 65 | 0003 - Day of the Week 66 | 0004 - Hour (high bit denotes PM, e.g. 0x00 to 0x11 for 00:00 to 11:00 and 0x92 to 0xA3 for 12:00 to 23:00) 67 | 0005 - Minute 68 | 0006 - Second -------------------------------------------------------------------------------- /History.txt: -------------------------------------------------------------------------------- 1 | NitroSwan revision history 2 | -=-=-=-=-=-=-=-=-=-=-=-=-=- 3 | 4 | V0.7.2 - 2025-05-25 (FluBBa) 5 | Added support for Karnak mapper chip (PCV2). 6 | Added XModem support for BootFriend. 7 | Fixed serial port read. 8 | Fixed IO registers startup values. 9 | Fixed IO registers writable bits. 10 | Fixed a glitch on the small dot icon. 11 | Fixed Branch Far & Call Far. 12 | Optimized some opcodes. 13 | 14 | V0.7.1 - 2025-03-17 (FluBBa) 15 | Fixed a regression in the noise. 16 | 17 | V0.7.0 - 2025-03-16 (FluBBa) 18 | Added support for uploading any file to WonderWitch. 19 | Added new menu graphics. 20 | Fixed sound output control (0x91) register. 21 | Fixed latched icons (0x1A) register. 22 | Fixed LCD sleep (0x14) register. 23 | Fixed power off (0x62/0xAC) registers. 24 | Fixed mov to stack segment register. 25 | Fixed Trap flag handling in EI. 26 | Fixed AM/PM behaviour of RTC. 27 | Optimized sound rendering. 28 | 29 | V0.6.7 - 2025-02-18 (FluBBa) 30 | Added separate file path for WonderWitch. 31 | Fixed ch2 sample playing. 32 | Fixed ch3 sweep frequency. 33 | Fixed some edge cases for EEPROM. 34 | 35 | V0.6.6 - 2024-09-18 (FluBBa) 36 | Added some support for WonderWitch. 37 | Added saving of machine setting. 38 | Added SwanCrystal border. 39 | Fixed LCD icons palette when switching machine. 40 | Fixed some small bugs in the DMA. 41 | Fixed cpu prefix handling. 42 | Optimized some opcodes. 43 | 44 | V0.6.5 - 2024-07-24 (FluBBa) 45 | Added saving of joypad layout. 46 | Added extra RAM on 3DS. 47 | Fixed cache bug in tile map rendering. 48 | Fixed repeating sound DMA. 49 | Fixed HyperVoice scaling/mode. 50 | Fixed EEPROM handling a bit. 51 | Fixed Quit Emulator when started from TWiLight. 52 | 53 | V0.6.4 - 2023-10-25 (FluBBa) 54 | Added alternate joypad layout. 55 | Fixed unsigned mul on ASWAN. 56 | Fixed immediate mul. 57 | Fixed 32/16 division. 58 | Fixed IO register differences between mono/color mode. 59 | Optimized 16-bit signed mul. 60 | 61 | V0.6.3 - 2023-09-24 (FluBBa) 62 | Fixed value of SP when running without boot rom. 63 | Fixed boot rom behavior for PCV2. 64 | Fixed SLOT-2 RAM support for EZ-Flash 3-In-1. 65 | Optimized IO port writing. 66 | Optimized some opcodes. 67 | Tweaked EEPROM handling. 68 | 69 | V0.6.2 - 2023-09-03 (FluBBa) 70 | Added SLOT-2 RAM support. 71 | Added IPS patch functionality. 72 | Added power on/off ability. 73 | Fixed correct frame-rate without "Allow Refresh Change". 74 | Fixed sound mixing. 75 | Optimized mapper writing. 76 | 77 | V0.6.0 - 2023-08-05 (FluBBa) 78 | Added per line emulation of Display Control register. 79 | Added per line emulation of FG Window register. 80 | Added per line emulation of Map Address register. 81 | Added differences for the 2 cart mappers. 82 | Optimized tile map rendering. 83 | Optimized cpu core. 84 | 85 | V0.5.2 - 2023-04-21 (FluBBa) 86 | Fixed controls for PCV2. 87 | Fixed WS sound levels. 88 | Fixed WS sound icon. 89 | Fixed save state. 90 | Added more BnW palettes. 91 | 92 | V0.5.1 - 2023-03-09 (FluBBa) 93 | Fixed noise calculation. 94 | Fixed BnW palettes. 95 | Added setting for border. 96 | 97 | V0.5.0 - 2023-03-01 (FluBBa) 98 | Fixed reset of mapper registers. 99 | Fixed loading of roms that are not power of 2. 100 | Fixed timing of several opcodes. 101 | Fixed timing of interrupts. 102 | Fixed timing of address calculations. 103 | Fixed some cpu opcodes. 104 | Fixed interrupt handling. 105 | Fixed timers. 106 | Fixed sweep on sound channel 3. 107 | Fixed noise feedback selection. 108 | Fixed sound sample playback. 109 | Optimized cpu core a bit. 110 | Added support for serial port interrupts. 111 | Added support for battery low. 112 | Added new debug menu. 113 | Linked NDS battery to WS battery. 114 | Better menu traversing. 115 | 116 | V0.4.1 - 2022-08-15 (FluBBa) 117 | Added border for PCV2 mode. 118 | WS refresh rate can control NDS refresh rate. 119 | 120 | V0.4.0 - 2022-08-11 (FluBBa) 121 | Fixed flag calculation for most opcodes. 122 | Fixed emulation of undefined opcodes. 123 | Fixed settings to disable Bg,Fg & Spr. 124 | Fixed default sound volume on WSC/SC. 125 | Optimized the cpu core. 126 | Tweaked behaviour of timers. 127 | Mute Ch2 when playing samples. 128 | 129 | V0.3.6 - 2022-04-20 (FluBBa) 130 | Fixed sound DMA a bit. 131 | Added savestates. 132 | Added sound button (Select). 133 | 134 | V0.3.5 - 2022-03-09 (FluBBa) 135 | Added RTC emulation. 136 | Added a border to emu screen. 137 | Better mapping of input. 138 | Optimized graphic redraw. 139 | Optimized PC/IP handling in cpu core. 140 | 141 | V0.3.0 - 2022-02-09 (FluBBa) 142 | Added banked SRAM emulation. 143 | Fixed zip decompression buffer size. 144 | Optimized cycle counting. 145 | Optimized Program Counter. 146 | Optimized memory access. 147 | Optimized opcodes. 148 | 149 | V0.2.0 - 2022-01-19 (FluBBa) 150 | Rewrote the whole cpu core in asm. 151 | 152 | V0.1.5 - 2022-01-09 (FluBBa) 153 | Rewrote parts of the cpu core in asm. 154 | 155 | V0.1.2 - 2021-12-18 (FluBBa) 156 | Backgrounds works. 157 | Sprites works. 158 | Mono mode works. 159 | Fixed Interrupts and Timers. 160 | Added EEPROM emulation. 161 | Added loading of bootrom for both WS & WSC. 162 | Added loading/saving of sram/eeprom. 163 | Added sound. 164 | Fixed interrupt routine in CPU. 165 | 166 | V0.1.1 - 2021-10-17 (FluBBa) 167 | Started port to Nintendo DS & devkitPro. 168 | 169 | V0.1.0 - 2006-07-28 (FluBBa) 170 | Initial release for GBA. 171 | 172 | V0.0.0 - 2006-07-23 (FluBBa) 173 | Started coding on GBA version. 174 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITARM)),) 6 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 7 | endif 8 | 9 | GAME_TITLE := NitroSwan 10 | GAME_SUBTITLE1 := WonderSwan Emulator 11 | GAME_SUBTITLE2 := github.com/FluBBaOfWard 12 | GAME_ICON := $(CURDIR)/../WSLogo.bmp 13 | 14 | include $(DEVKITARM)/ds_rules 15 | 16 | #--------------------------------------------------------------------------------- 17 | # TARGET is the name of the output 18 | # BUILD is the directory where object files & intermediate files will be placed 19 | # SOURCES is a list of directories containing source code 20 | # INCLUDES is a list of directories containing extra header files 21 | # DATA is a list of directories containing binary files 22 | # GRAPHICS is a list of directories containing files to be processed by grit 23 | # all directories are relative to this makefile 24 | #--------------------------------------------------------------------------------- 25 | TARGET := $(shell basename $(CURDIR)) 26 | BUILD := build 27 | SOURCES := source \ 28 | source/Shared \ 29 | source/Shared/Unzip \ 30 | source/ARMV30MZ \ 31 | source/Sphinx \ 32 | source/WSCart \ 33 | source/WSCart/WSRTC \ 34 | source/WSEEPROM 35 | DATA := data 36 | INCLUDES := include 37 | GRAPHICS := source/Shared/graphics \ 38 | graphics 39 | 40 | #--------------------------------------------------------------------------------- 41 | # options for code generation 42 | #--------------------------------------------------------------------------------- 43 | ARCH := -march=armv5te -mtune=arm946e-s -mthumb -mthumb-interwork 44 | FLAGS := -DARM9 -DNDS -DWSAUDIO_LOW 45 | 46 | ifeq ($(DEBUG),1) 47 | CFLAGS := -gdwarf-2 -Wall -ffast-math $(ARCH) 48 | else 49 | CFLAGS := -g -Wall -O3 -fomit-frame-pointer -ffast-math $(ARCH) 50 | endif 51 | 52 | CFLAGS += $(INCLUDE) $(FLAGS) 53 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions 54 | 55 | ASFLAGS := -g $(ARCH) $(INCLUDE) $(FLAGS) 56 | LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 57 | 58 | #--------------------------------------------------------------------------------- 59 | # any extra libraries we wish to link with the project 60 | #--------------------------------------------------------------------------------- 61 | LIBS := -lfat -lmm9 -lnds9 62 | 63 | 64 | #--------------------------------------------------------------------------------- 65 | # list of directories containing libraries, this must be the top level containing 66 | # include and lib 67 | #--------------------------------------------------------------------------------- 68 | LIBDIRS := $(LIBNDS) 69 | 70 | #--------------------------------------------------------------------------------- 71 | # no real need to edit anything past this point unless you need to add additional 72 | # rules for different file extensions 73 | #--------------------------------------------------------------------------------- 74 | ifneq ($(BUILD),$(notdir $(CURDIR))) 75 | #--------------------------------------------------------------------------------- 76 | 77 | export OUTPUT := $(CURDIR)/$(TARGET) 78 | 79 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 80 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ 81 | $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) 82 | 83 | 84 | export DEPSDIR := $(CURDIR)/$(BUILD) 85 | 86 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 87 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 88 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 89 | PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png))) 90 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 91 | 92 | #--------------------------------------------------------------------------------- 93 | # use CXX for linking C++ projects, CC for standard C 94 | #--------------------------------------------------------------------------------- 95 | ifeq ($(strip $(CPPFILES)),) 96 | #--------------------------------------------------------------------------------- 97 | export LD := $(CC) 98 | #--------------------------------------------------------------------------------- 99 | else 100 | #--------------------------------------------------------------------------------- 101 | export LD := $(CXX) 102 | #--------------------------------------------------------------------------------- 103 | endif 104 | #--------------------------------------------------------------------------------- 105 | 106 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 107 | $(PNGFILES:.png=.o) \ 108 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 109 | 110 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 111 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 112 | -I$(CURDIR)/$(BUILD) 113 | 114 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 115 | 116 | .PHONY: $(BUILD) clean 117 | 118 | #--------------------------------------------------------------------------------- 119 | $(BUILD): 120 | @[ -d $@ ] || mkdir -p $@ 121 | @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 122 | 123 | #--------------------------------------------------------------------------------- 124 | clean: 125 | @echo clean ... 126 | @rm -fr $(BUILD) $(TARGET).elf $(TARGET).nds $(TARGET).arm9 $(TARGET).ds.gba 127 | 128 | 129 | #--------------------------------------------------------------------------------- 130 | else 131 | 132 | DEPENDS := $(OFILES:.o=.d) 133 | 134 | #--------------------------------------------------------------------------------- 135 | # main targets 136 | #--------------------------------------------------------------------------------- 137 | $(OUTPUT).nds : $(OUTPUT).elf 138 | $(OUTPUT).elf : $(OFILES) 139 | 140 | #--------------------------------------------------------------------------------- 141 | %.pcx.o : %.pcx 142 | #--------------------------------------------------------------------------------- 143 | @echo $(notdir $<) 144 | @$(bin2o) 145 | 146 | #--------------------------------------------------------------------------------- 147 | %.s %.h : %.png %.grit 148 | #--------------------------------------------------------------------------------- 149 | grit $< -fts -o$* 150 | 151 | 152 | -include $(DEPENDS) 153 | 154 | #--------------------------------------------------------------------------------------- 155 | endif 156 | #--------------------------------------------------------------------------------------- 157 | -------------------------------------------------------------------------------- /NitroSwan.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "files.exclude": { 9 | "*.elf": true, 10 | "*.nds": true, 11 | "*.zip": true 12 | }, 13 | "terminal.integrated.env.osx": { 14 | "DEVKITARM": "/opt/devkitPro/devkitARM", 15 | "DEVKITPRO": "/opt/devkitPro" 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /NitroSwan.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /NitroSwan.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /NitroSwan.xcodeproj/xcshareddata/xcschemes/NitroSwan.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 47 | 48 | 49 | 55 | 56 | 57 | 58 | 61 | 62 | 63 | 64 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /NitroSwan_todo.txt: -------------------------------------------------------------------------------- 1 | NitroSwan Todo 2 | 3 | Fix ram behaviour on mono consoles. 4 | Fix RTC: 5 | Corrected values when setting out of range values. 6 | Handle Contrast High/Low for WSC, change Y every other frame. 7 | Add cache for 16MB extra area on 3DS. 8 | Fix better sound? Update twice per raster row. 9 | Map serial port to debug out. Test! 10 | Find speed hacks. 11 | Change cpuReadMem20(W) to return value in r1 instead of r0? 12 | Use r3 as write-back register to begin with. 13 | Run mono games in 4 color mode? 14 | Handle Sprite Window some how. 15 | Sprite priority can't be fixed. 16 | 17 | GFX: 18 | See Sphinx todo. 19 | 20 | GUI: 21 | Eng/Jap: "Digimon Tamers - Battle Spirit" has jumper on the cart. 22 | 23 | Mem: 24 | 25 | CPU: 26 | See ARMV30MZ Todo. 27 | 28 | Sound: 29 | See Sphinx todo. 30 | 31 | Misc: 32 | Armored Unit, menu background shakes. Just as on HW! 33 | Cardcaptor Sakura, timing sensitive. 34 | Dekadence - Vesilintu, very slow. 35 | Mingle Magnet, slow. (67-68fps) 36 | Mr. Driller, also sensitive to timing (fetch opcode, pl vs gt). 37 | Nazo Ou Pocket, slow. (60-61, 62) 38 | Puyo Puyo Tsuu, slow. (62-63, 64) 39 | Side Pocket, slow. (65-66) 40 | Time Bokan Series, slow. (64-65) 41 | 42 | Games Not Working: 43 | 44 | Games Not Working Fixed: 45 | Bakusou Dekotora Densetsu. Unzip craps out. 46 | Chou Denki Card Game, crashes. 47 | * Needs initialization, last item on first page. 48 | ClockTower, doesn't start. 49 | * Needs noise calculation even if ch4 noise is disabled. 50 | Hanjuku Hero, crashes. 51 | * Reads mapper register 0xC0 which was not reset correctly. 52 | Kurupara, crashes. Crashes because of LineCompareIRQ on line 0. 53 | * Fixing interrupt handling in Sphinx fixed it. 54 | Riviera freezes on a black screen after the chapter 1 screen. 55 | * Fixed in V0.4.0. 56 | Tonpuusou. Can't start. 57 | * HBlank interrupt should always be run when value = 1. 58 | Ultraman, division by zero crash. 59 | * If watching the whole intro before starting. 60 | * Fixing interrupt handling in Sphinx fixed it. 61 | With You - Mitsumete Itai, halts at black screen. 62 | * Waiting for EEPROM ready. 63 | Xi Little. Can't start. 64 | * Waiting on sample playing. 65 | 66 | Games With Glitches: 67 | Dicing Knight. shadows are in front of player. 68 | * Sprite priority is wrong for sprites that are above FG. 69 | Digimon - Anode Tamer & Cathode Tamer, missing background gradient in battles. 70 | * Changes palette per line. 71 | Final Fantasy, Sprites show in dialog windows. 72 | * Sprite windows. 73 | Final Lap 2000, road colors incorrect. 74 | * Changes palette per line. 75 | Final Lap Special, road colors incorrect. 76 | * Changes palette per line. 77 | From TV Animation One Piece - Grand Battle Swan Colosseum, incorrect sky color. 78 | * Changes palette per line. 79 | Mahjong Touryuumon, speed too fast. 80 | * Apparently it sets the ROM waitstate to 1(?). 81 | Makaimura, gargoyles in intro should not show up on the right, first boss sprites are glitchy. 82 | * Sprite Window and...? 83 | Neon Genesis Evangelion, sprites overlap avatar images. 84 | * Sprite windows and sprite priority. 85 | Rockman & Forte - Mirai Kara no Chousensha, no background fade in intro. 86 | * Changes palette per line? 87 | Romancing Sa-Ga, sprites overlap text boxes. 88 | * Sprite windows and sprite priority. 89 | Sorobang, incorrect tiles. 90 | * Needs all 1024 tiles in 4color mode. 91 | WonderSwanColor BIOS, incorrect tiles. 92 | * Needs all 1024 tiles in 4color mode. 93 | 94 | Games With Glitches Fixed: 95 | Battle Spirits, missing status bar. 96 | * Changes tilemap address. 97 | Cardcaptor Sakura, book dissolve in intro glitches the second time. 98 | * Requires "correct" noise calculation. 99 | Chaos Gear, Intro not showing. 100 | * Sensitive to serial interrupt. 101 | * IRQ are taken 1 instruction after SEI/STI. 102 | Final Fantasy. Characters when inputing name can glitch. 103 | * Depending on which scan line VBlank is and how many total scan lines. 104 | * Interrupts were not acknowledged during rep instructions. 105 | From TV Animation One Piece - Grand Battle Swan Colosseum, missing status bar. 106 | * Changes tilemap address. 107 | Front Mission, missing some text boxes. 108 | * Changes tilemap address. 109 | Magical Drop, playfield flickers horizontaly. Sensitive to interrupt (and IRET, HALT) timing. 110 | * Fix: Sprite DMA consumes cycles? 111 | Mahjong Touryuumon, no sound. 112 | * Sound fix by emulating serial out buffer empty irq. 113 | Makai Toushi Sa-Ga, textboxes overlap sprites and background. 114 | * Changes tilemap address. 115 | SD Gundam Operation U.C, missing status bar. 116 | * Changes tilemap address. 117 | Ultraman, Logo slide down doesn't reach top of screen as on real HW. 118 | * Interrupts were not acknowledged during rep instructions. 119 | 120 | Needs Large (256kB) SRAM: 121 | Dicing Knight. 122 | Judgement Silversword Rebirth Edition 123 | Tenori-On 124 | WonderWitch 125 | 126 | Needs RTC: 127 | Dicing Knight. 128 | Dokodemo Hamster 3 - O Dekake Saffron 129 | Inu yasha 130 | Judgement Silversword Rebirth Edition 131 | Tenori-On 132 | WonderWitch 133 | 134 | Games which enable NMI: 135 | Cardcaptor Sakura, shows message. 136 | Engacho. 137 | Inuyasha - Kagome no Sengoku Nikki 138 | SD Gundam Operation U.C, pauses like hitting start, but with a message about low battery. 139 | 140 | Speedhacks: 141 | Halt (0xf4). 142 | * Bakusou Dekotora Densetsu. 143 | * Chocobo no Fushigi na Dungeon for WonderSwan 144 | * Chou Aniki - Otoko no Tamafuda 145 | * Engacho for WonderSwan 146 | * Flash Koibito-kun 147 | * Ganso Jajamaru-kun 148 | * Goraku Ou Tango! 149 | * GunPey 150 | * GunPey EX 151 | * Harobots 152 | * Kaze no Klonoa - Moonlight Museum 153 | * Kinnikuman Ilsei - Dream Tag Match 154 | * Langrisser Millennium WS - The Last Century 155 | * Magical Drop for WonderSwan 156 | * Medarot Perfect Edition 157 | * Moero!! Pro Yakyuu Rookies 158 | * Mr. Driller 159 | * Robot Works 160 | * Rockman & Forte - Mirai Kara no Chousensha 161 | * Saint Seya - Ougon Densetsu - Perfect Edition 162 | * Sennou Millennium 163 | * Shin Nippon Pro Wrestling Toukon Retsuden 164 | * Super Robot Taisen Compact 165 | * Tare Panda no GunPey 166 | * Wonder Stadium '99 167 | * Wonder Stadium 168 | * WonderSwan Handy Sonar 169 | * Zakull Bootsplash 170 | 171 | "Guilty Gear Petit" - JR (0x72,0xF9) @0x2002 172 | Mingle Magnet - 0x74,0xF3 173 | Nazo Ou Pocket - 0x74,0xEE 174 | Side Pocket - 0x75,0xF7 175 | Time Bokan Series - 0x74,0xF6 176 | 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NitroSwan V0.7.2 2 | 3 | The WonderSwan logo 4 | 5 | This is a Bandai WonderSwan (Color/Crystal) & Benesse PocketChallenge V2 6 | emulator for the Nintendo DS(i)/3DS. 7 | 8 | ## How to use 9 | 10 | 1. Create a folder named "nitroswan" in either the root of your flash card or 11 | in the data folder. This is where settings and save files end up. 12 | 2. Now put game/bios files into a folder where you have (WonderSwan) roms, max 13 | 768 files per folder, filenames must not be longer than 127 chars. You can use 14 | zip-files (as long as they use the deflate compression). CAUTION! Games that 15 | require SLOT-2 RAM can not be used with zip-files! 16 | 3. Depending on your flashcart you might have to DLDI patch the emulator. 17 | 18 | The save files should be compatible with most other WonderSwan emulators. 19 | 20 | When the emulator starts, you can either press L+R or tap on the screen to open 21 | up the menu. Now you can use the cross or touchscreen to navigate the menus, A 22 | or double tap to select an option, B or the top of the screen to go back a 23 | step. 24 | 25 | To select between the tabs use R & L or the touchscreen. 26 | 27 | Hold Start while starting a game to enter the boot rom settings, the internal 28 | EEPROM is saved when saving settings. 29 | 30 | Since the DS/DS Lite only has 4MB of RAM you will need a SLOT-2/GBA cart with 31 | RAM to play games larger than 2MB. 32 | 33 | ## Menu 34 | 35 | ### File 36 | 37 | * Load Game: Select a game to load. 38 | * Load State: Load a previously saved state of the currently running game. 39 | * Save State: Save a state of the currently running game. 40 | * Load NVRAM: Load non volatile ram (EEPROM/SRAM) for the currently running game. 41 | * Save NVRAM: Save non volatile ram (EEPROM/SRAM) for the currently running game. 42 | * Load Patch: Apply an IPS patch to the currectly loaded rom. 43 | * Save Settings: Save the current settings (and internal EEPROM). 44 | * Reset Game: Reset the currently running game. 45 | 46 | ### Options 47 | 48 | * Controller: 49 | * Autofire: Select if you want autofire. 50 | * Swap A/B: Swap which NDS button is mapped to which WS button. 51 | * Alternate layout: See Controls. 52 | * Display: 53 | * Gamma: Lets you change the gamma ("brightness"). 54 | * Contrast: Lets you change the contrast. 55 | * B&W Palette: Here you can select the palette for B & W games. 56 | * Border: Choose what to show outside the WS screen. 57 | * Machine: 58 | * Machine: Select the emulated machine. 59 | * Select WS Bios: Load a real WS Bios. 60 | * Select WS Color Bios: Load a real WS Color Bios. 61 | * Select WS Crystal Bios: Load a real WS Crystal Bios. 62 | * Import Internal EEPROM: Load a special internal EEPROM. 63 | * Clear Internal EEPROM: Reset internal EEPROM. 64 | * Headphones: Select whether heaphones are connected or not. 65 | * Cpu speed hacks: Allow speed hacks. 66 | * Settings: 67 | * Speed: Switch between speed modes. 68 | * Normal: Game runs at its normal speed. 69 | * 200%: Game can run up to double speed. 70 | * Max: Games can run up to 4 times normal speed. 71 | * 50%: Game runs at half speed. 72 | * Allow Refresh Change: Allow the Wonderswan to change NDS refresh rate. 73 | * Autoload State: Toggle Savestate autoloading. Automagically load the savestate associated with the selected game. 74 | * Autoload NVRAM: Toggle EEPROM/SRAM autoloading. Automagically load the EEPROM/SRAM associated with the selected game. 75 | * Autosave Settings: This will save settings when leaving menu if any changes are made. 76 | * Autopause Game: Toggle if the game should pause when opening the menu. 77 | * Powersave 2nd Screen: If graphics/light should be turned off for the GUI screen when menu is not active. 78 | * Emulator on Bottom: Select if top or bottom screen should be used for emulator, when menu is active emulator screen is allways on top. 79 | * Autosleep: Doesn't work. 80 | * WonderWitch: Tools for interacting with a WonderWitch. 81 | * See WonderWitch.md for more information. 82 | * BootFriend: For uploading/downloading files with BootFriend. 83 | * Debug: 84 | * Debug Output: Show FPS and logged text. 85 | * Disable Foreground: Turn on/off foreground rendering. 86 | * Disable Background: Turn on/off background rendering. 87 | * Disable Sprites: Turn on/off sprite rendering. 88 | * Disable Windows: Turn on/off window effects. 89 | * Step Frame: Emulate one frame. 90 | 91 | ### About 92 | 93 | Some info about the emulator and game... 94 | 95 | ## Controls 96 | 97 | ### WonderSwan 98 | 99 | ```text 100 | Start is mapped to WS Start. 101 | Select is mapped to WS Sound. 102 | In horizontal games the d-pad is mapped to WS X1-X4. A & B buttons are mapped to WS A & B. 103 | Holding L or R maps the dpad to WS Y1-Y4. 104 | 105 | In vertical games the d-pad is mapped to WS Y1-Y4. A, B, X & Y are mapped to WS X1-X4. 106 | 107 | In alternate layout it is the same as normal horizontal, except L, R, X & Y are 108 | mapped to WS Y1-Y4. To open the menu use L+Select. 109 | ``` 110 | 111 | ### Pocket Challenge V2 112 | 113 | ```text 114 | Dpad is mapped to up, down, left & right. 115 | L is mapped to Escape. 116 | R & X is mapped to Voice/View. 117 | A is mapped to Clear. 118 | B is mapped to Circle. 119 | Y is mapped to Pass. 120 | ``` 121 | 122 | ## Games 123 | 124 | There are 3 games that I know of that has serious problems. 125 | 126 | * Beatmania: Game is too large even for the DSi. Can be used with a 16MB SLOT-2 card or on 3DS. 127 | * Chou Denki Card Game: You need to initialize NVRAM, this is the last item on the first page (初期化). 128 | * Mahjong Touryuumon, emulated speed too fast. 129 | 130 | There are a couple of games that have visual glitches. 131 | 132 | * Dicing Knight. shadows are in front of player. 133 | * Digimon - Anode Tamer & Cathode Tamer, missing background gradient in battles. 134 | * Final Fantasy, sprites show in dialog windows. 135 | * Final Lap 2000, incorrect road colors. 136 | * Final Lap Special - GT & Formula Machine, incorrect road colors. 137 | * From TV Animation One Piece - Grand Battle Swan Colosseum, incorrect sky color. 138 | * Makaimura, first boss sprites are glitchy, gargoyles in intro should not show up on the right. 139 | * Neon Genesis Evangelion - Shito Ikusei, sprites overlap avatar images. 140 | * Rockman & Forte - Mirai Kara no Chousensha, no background fade in intro. 141 | * Romancing Sa-Ga, sprites overlap text boxes. 142 | * Sorobang, needs all 1024 tiles in 4color mode. 143 | * WonderSwan Color BIOS, needs all 1024 tiles in 4color mode. 144 | 145 | ## Accuracy 146 | 147 | I've made a few test programs for the WonderSwan to be able to really make sure 148 | it is as accurate as possible. 149 | 150 | * [WSCPUTest](https://github.com/FluBBaOfWard/WSCpuTest) - Tests functions of the NEC V30MZ CPU instructions. 151 | * [WSTimingTest](https://github.com/FluBBaOfWard/WSTimingTest) - Tests timing of the NEC V30MZ CPU instruction. 152 | * [WSHWTest](https://github.com/FluBBaOfWard/WSHWTest) - Tests other HW of the WS SOC. 153 | 154 | Other test programs I have used to get better accuracy. 155 | 156 | * [WS-Test-Suite](https://github.com/asiekierka/ws-test-suite) - Lots of small tests. 157 | * [RTC Test](https://forums.nesdev.org/viewtopic.php?t=21513) Test the RTC in certain cartridges. 158 | 159 | ## Credits 160 | 161 | ```text 162 | Huge thanks to Loopy for the incredible PocketNES, without it this emu would probably never have been made. 163 | Thanks to: 164 | asie for info and inspiration. https://ws.nesdev.org/wiki/WSdev_Wiki 165 | Ed Mandy (Flavor) for WonderSwan info & flashcart. https://www.flashmasta.com 166 | Koyote for WonderSwan info. 167 | Alex Marshall (trap15) for WonderSwan info. http://daifukkat.su/docs/wsman/ 168 | Guy Perfect for WonderSwan info http://perfectkiosk.net/stsws.html 169 | Godzil for the boot rom stubs. https://github.com/Godzil/NewOswan 170 | lidnariq for RTC info. 171 | plasturion for some BnW palettes. 172 | Dwedit for help and inspiration with a lot of things. https://www.dwedit.org 173 | ``` 174 | 175 | Fredrik Ahlström 176 | 177 | 178 | 179 | 180 | 181 | X/Twitter @TheRealFluBBa 182 | -------------------------------------------------------------------------------- /WSLogo.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluBBaOfWard/NitroSwan/dcf08de3fc34ec980ab94a1aee06cd179b9047a2/WSLogo.bmp -------------------------------------------------------------------------------- /WonderWitch.md: -------------------------------------------------------------------------------- 1 | # WonderWitch Documentation 2 | 3 | ## Basics 4 | 5 | There are three screens in the WonderWitch UI. 6 | The first one is where you start the games/applications. 7 | The second is where you can copy files between program-area(ROM) & work-area(SRAM) 8 | The third is where you can transfer files & execute commands via serial. 9 | 10 | ## How to use 11 | 12 | When you first startup WonderWitch there is a flash/sram test, then there is 13 | a small intro animation. Press any button to get to the first screen. 14 | Now press WS Y4 (NDS L+Left) to get to the third screen. 15 | Now open the menu on the DS, go to Options->WonderWitch. 16 | Here you can send files/commands to the WonderWitch. 17 | The first thing you want to do is upload a file. 18 | 19 | Press the "Upload File" button, choose the file to upload and then exit the DS menu. 20 | File names/description will be truncated (but still contain the file extension), 21 | file name 16 bytes, description 24 bytes. 22 | 23 | Exiting the menu after every command is still needed, I will try to fix this later. 24 | 25 | Many games require several files to run, "sound.il" is one such file used by some. 26 | Many games also require a WSColor, so make sure you run the emulator in color mode. 27 | If you want to do any actions on a specific file you have to do "Dir" first to 28 | select the file and then run the command (delete, execute). 29 | After you have deleted a file you have to run defrag to get back the space. 30 | Sometimes things start to get really slow (almost allways if you run execute), 31 | then try a reboot command or reset the console. 32 | 33 | If you want to keep what you have uploaded to the WonderWitch make sure you use the 34 | Save NVRAM in the emulator. 35 | 36 | * WonderWitch: Tools for interacting with a WonderWitch. 37 | * Storage: Select storage, apps need to be located in ROM. 38 | * Upload File: Used for uploading files to WonderWitch. 39 | * Dir: Show contents of a directory, to select a file. 40 | * Execute: Execute an app. (use the WW start screen instead). 41 | * Delete: Delete a file. 42 | * Defrag: Reclaim storage from deleted files. 43 | * Download File: Download a file from WW to the DS. 44 | * NewFS (Formatt): Wipe everything in the selected storage. 45 | * XMODEM Transmit: Used for uploading files to WonderWitch. 46 | * XMODEM Receive: Used for downloading files from WonderWitch. 47 | * Reboot WW: Reboot the WonderWitch software. 48 | * CD: Change directory. 49 | * Interact: Set terminal to interactive mode. 50 | * Stty: Set/Show interactive mode. 51 | * Hello: Show info about WW software. 52 | * Speed: Show communication speed. 53 | 54 | ## Credits 55 | 56 | ```text 57 | Huge thanks to asie for info and inspiration. 58 | ``` 59 | 60 | Fredrik Ahlström 61 | 62 | X/Twitter @TheRealFluBBa 63 | 64 | 65 | -------------------------------------------------------------------------------- /copytodsx.bat: -------------------------------------------------------------------------------- 1 | copy NitroSwan.nds h:\apps -------------------------------------------------------------------------------- /graphics/PCV2Border.grit: -------------------------------------------------------------------------------- 1 | 2 | -gB 8 3 | 4 | # use lz77 compression 5 | -gzl 6 | 7 | # map output, lz77 compression 8 | -mzl 9 | 10 | # standard 8bpp tilereduction 11 | -mR8 12 | -------------------------------------------------------------------------------- /graphics/PCV2Border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluBBaOfWard/NitroSwan/dcf08de3fc34ec980ab94a1aee06cd179b9047a2/graphics/PCV2Border.png -------------------------------------------------------------------------------- /graphics/SCBorder.grit: -------------------------------------------------------------------------------- 1 | 2 | -gB 8 3 | 4 | # use lz77 compression 5 | -gzl 6 | 7 | # map output, lz77 compression 8 | -mzl 9 | 10 | # standard 8bpp tilereduction 11 | -mR8 12 | -------------------------------------------------------------------------------- /graphics/SCBorder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluBBaOfWard/NitroSwan/dcf08de3fc34ec980ab94a1aee06cd179b9047a2/graphics/SCBorder.png -------------------------------------------------------------------------------- /graphics/SCBottom.grit: -------------------------------------------------------------------------------- 1 | 2 | # output 4bit tiles 3 | -gB 4 4 | 5 | # use lz77 compression 6 | -gzl 7 | 8 | # map output, lz77 compression 9 | -mzl 10 | 11 | # standard 4bpp tilereduction 12 | -mR4 13 | -------------------------------------------------------------------------------- /graphics/SCBottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluBBaOfWard/NitroSwan/dcf08de3fc34ec980ab94a1aee06cd179b9047a2/graphics/SCBottom.png -------------------------------------------------------------------------------- /graphics/WSBorder.grit: -------------------------------------------------------------------------------- 1 | 2 | -gB 8 3 | 4 | # use lz77 compression 5 | -gzl 6 | 7 | # map output, lz77 compression 8 | -mzl 9 | 10 | # standard 8bpp tilereduction 11 | -mR8 12 | -------------------------------------------------------------------------------- /graphics/WSBorder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluBBaOfWard/NitroSwan/dcf08de3fc34ec980ab94a1aee06cd179b9047a2/graphics/WSBorder.png -------------------------------------------------------------------------------- /graphics/WSBottom.grit: -------------------------------------------------------------------------------- 1 | 2 | # output 4bit tiles 3 | -gB 4 4 | 5 | # use lz77 compression 6 | -gzl 7 | 8 | # map output, lz77 compression 9 | -mzl 10 | 11 | # standard 4bpp tilereduction 12 | -mR4 13 | -------------------------------------------------------------------------------- /graphics/WSBottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluBBaOfWard/NitroSwan/dcf08de3fc34ec980ab94a1aee06cd179b9047a2/graphics/WSBottom.png -------------------------------------------------------------------------------- /graphics/WSCBorder.grit: -------------------------------------------------------------------------------- 1 | 2 | -gB 8 3 | 4 | # use lz77 compression 5 | -gzl 6 | 7 | # map output, lz77 compression 8 | -mzl 9 | 10 | # standard 8bpp tilereduction 11 | -mR8 12 | -------------------------------------------------------------------------------- /graphics/WSCBorder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluBBaOfWard/NitroSwan/dcf08de3fc34ec980ab94a1aee06cd179b9047a2/graphics/WSCBorder.png -------------------------------------------------------------------------------- /graphics/WSCBottom.grit: -------------------------------------------------------------------------------- 1 | 2 | # output 4bit tiles 3 | -gB 4 4 | 5 | # use lz77 compression 6 | -gzl 7 | 8 | # map output, lz77 compression 9 | -mzl 10 | 11 | # standard 4bpp tilereduction 12 | -mR4 13 | -------------------------------------------------------------------------------- /graphics/WSCBottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluBBaOfWard/NitroSwan/dcf08de3fc34ec980ab94a1aee06cd179b9047a2/graphics/WSCBottom.png -------------------------------------------------------------------------------- /include/wsroms/wc_irom.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluBBaOfWard/NitroSwan/dcf08de3fc34ec980ab94a1aee06cd179b9047a2/include/wsroms/wc_irom.bin -------------------------------------------------------------------------------- /include/wsroms/ws_irom.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluBBaOfWard/NitroSwan/dcf08de3fc34ec980ab94a1aee06cd179b9047a2/include/wsroms/ws_irom.bin -------------------------------------------------------------------------------- /include/wsroms/wsc_irom.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluBBaOfWard/NitroSwan/dcf08de3fc34ec980ab94a1aee06cd179b9047a2/include/wsroms/wsc_irom.bin -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluBBaOfWard/NitroSwan/dcf08de3fc34ec980ab94a1aee06cd179b9047a2/logo.png -------------------------------------------------------------------------------- /source/Cart.h: -------------------------------------------------------------------------------- 1 | #ifndef CART_HEADER 2 | #define CART_HEADER 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | extern u32 maxRomSize; 9 | extern u32 allocatedRomMemSize; 10 | extern u8 gMachine; 11 | extern u8 gMachineSet; 12 | extern u8 gSOC; 13 | extern u8 gLang; 14 | extern u8 gPaletteBank; 15 | 16 | extern u8 wsRAM[0x10000]; 17 | extern u8 biosSpace[0x1000]; 18 | extern u8 biosSpaceColor[0x2000]; 19 | extern u8 biosSpaceCrystal[0x2000]; 20 | extern u8 *allocatedRomMem; 21 | extern const void *g_BIOSBASE_BNW; 22 | extern const void *g_BIOSBASE_COLOR; 23 | extern const void *g_BIOSBASE_CRYSTAL; 24 | 25 | void machineInit(void); 26 | void loadCart(void); 27 | 28 | #ifdef __cplusplus 29 | } // extern "C" 30 | #endif 31 | 32 | #endif // CART_HEADER 33 | -------------------------------------------------------------------------------- /source/Cart.s: -------------------------------------------------------------------------------- 1 | #ifdef __arm__ 2 | 3 | //#define EMBEDDED_ROM 4 | 5 | #include "Sphinx/Sphinx.i" 6 | #include "ARMV30MZ/ARMV30MZ.i" 7 | 8 | .global allocatedRomMem 9 | .global biosBase 10 | .global biosSpace 11 | .global biosSpaceColor 12 | .global biosSpaceCrystal 13 | .global g_BIOSBASE_BNW 14 | .global g_BIOSBASE_COLOR 15 | .global g_BIOSBASE_CRYSTAL 16 | .global wsRAM 17 | .global DIRTYTILES 18 | .global maxRomSize 19 | .global allocatedRomMemSize 20 | .global gMachineSet 21 | .global gMachine 22 | .global gSOC 23 | .global gLang 24 | .global gPaletteBank 25 | 26 | .global machineInit 27 | .global loadCart 28 | .global clearDirtyTiles 29 | 30 | .syntax unified 31 | .arm 32 | 33 | .section .rodata 34 | .align 2 35 | 36 | #ifdef EMBEDDED_ROM 37 | ROM_Space: 38 | // .incbin "wsroms/Anchorz Field (Japan).ws" 39 | // .incbin "wsroms/Crazy Climber (J) [M][!].ws" 40 | // .incbin "wsroms/Chaos Demo V2.1 by Charles Doty (PD).wsc" 41 | // .incbin "wsroms/Dicing Knight. (J).wsc" 42 | // .incbin "wsroms/Guilty Gear Petit (J).wsc" 43 | // .incbin "wsroms/Inuyasha - Kagome no Sengoku Nikki (Japan).wsc" 44 | // .incbin "wsroms/Kaze no Klonoa - Moonlight Museum (Japan).ws" 45 | // .incbin "wsroms/Magical Drop for WonderSwan (Japan).ws" 46 | // .incbin "wsroms/Makaimura for WonderSwan (Japan).ws" 47 | // .incbin "wsroms/Mr. Driller (J) [!].wsc" 48 | // .incbin "wsroms/SD Gundam - Operation U.C. (Japan).wsc" 49 | // .incbin "wsroms/Tetris (Japan).wsc" 50 | // .incbin "wsroms/Tonpuusou (Japan).wsc" 51 | // .incbin "wsroms/WONDERPR.WSC" 52 | // .incbin "wsroms/WonderWitch [FreyaOS 1.2.0].ws" 53 | // .incbin "wsroms/WSCpuTest.wsc" 54 | // .incbin "wsroms/XI Little (Japan).wsc" 55 | ROM_SpaceEnd: 56 | WS_BIOS_INTERNAL: 57 | .incbin "wsroms/boot.rom" 58 | WSC_BIOS_INTERNAL: 59 | .incbin "wsroms/boot1.rom" 60 | SC_BIOS_INTERNAL: 61 | .incbin "wsroms/boot2.rom" 62 | #else 63 | WS_BIOS_INTERNAL: 64 | .incbin "wsroms/ws_irom.bin" 65 | WSC_BIOS_INTERNAL: 66 | SC_BIOS_INTERNAL: 67 | .incbin "wsroms/wc_irom.bin" 68 | #endif 69 | 70 | .align 2 71 | ;@---------------------------------------------------------------------------- 72 | machineInit: ;@ Called from C 73 | .type machineInit STT_FUNC 74 | ;@---------------------------------------------------------------------------- 75 | stmfd sp!,{r4-r11,lr} 76 | 77 | #ifdef EMBEDDED_ROM 78 | ldr r0,=romSize 79 | mov r1,#ROM_SpaceEnd-ROM_Space 80 | str r1,[r0] 81 | ldr r0,=romSpacePtr 82 | ldr r7,=ROM_Space 83 | str r7,[r0] 84 | #endif 85 | 86 | bl memoryInit 87 | bl gfxInit 88 | // bl ioInit 89 | bl soundInit 90 | bl cpuInit 91 | 92 | ldmfd sp!,{r4-r11,lr} 93 | bx lr 94 | 95 | .section .ewram,"ax" 96 | .align 2 97 | ;@---------------------------------------------------------------------------- 98 | loadCart: ;@ Called from C: 99 | .type loadCart STT_FUNC 100 | ;@---------------------------------------------------------------------------- 101 | stmfd sp!,{r4-r11,lr} 102 | 103 | bl wsCartReset 104 | 105 | ldr v30ptr,=V30OpTable 106 | 107 | ldrb r5,gMachine 108 | cmp r5,#HW_WONDERSWAN 109 | cmpne r5,#HW_POCKETCHALLENGEV2 110 | moveq r0,#1 ;@ Set boot rom overlay (size small) 111 | ldreq r1,g_BIOSBASE_BNW 112 | ldreq r2,=WS_BIOS_INTERNAL 113 | moveq r4,#SOC_ASWAN 114 | movne r0,#2 ;@ Set boot rom overlay (size big) 115 | ldrne r1,g_BIOSBASE_COLOR 116 | ldrne r2,=WSC_BIOS_INTERNAL 117 | movne r4,#SOC_SPHINX 118 | cmp r5,#HW_SWANCRYSTAL 119 | ldreq r1,g_BIOSBASE_CRYSTAL 120 | ldreq r2,=SC_BIOS_INTERNAL 121 | moveq r4,#SOC_SPHINX2 122 | strb r4,gSOC 123 | cmp r1,#0 124 | moveq r1,r2 ;@ Use internal bios 125 | str r1,biosBase 126 | bl setBootRomOverlay 127 | 128 | ldr r0,=wsRAM 129 | str r0,[v30ptr,#v30MemTblInv-1*4] ;@ 0 RAM 130 | mov r1,#0x10000/4 131 | bl memclr_ ;@ Clear RAM 132 | bl clearDirtyTiles 133 | cmp r4,#SOC_ASWAN 134 | ldreq r0,=wsRAM+0x4000 ;@ Clear mem outside of RAM 135 | moveq r1,#0x90 136 | moveq r2,#0xC000 137 | bleq memset 138 | 139 | // bl hacksInit 140 | bl gfxReset 141 | bl resetCartridgeBanks 142 | bl ioReset 143 | bl soundReset 144 | mov r0,r4 ;@ SOC 145 | bl cpuReset 146 | ldmfd sp!,{r4-r11,lr} 147 | bx lr 148 | 149 | 150 | ;@---------------------------------------------------------------------------- 151 | clearDirtyTiles: 152 | ;@---------------------------------------------------------------------------- 153 | ldr r0,=DIRTYTILES ;@ Clear RAM 154 | mov r1,#0x800/4 155 | b memclr_ 156 | 157 | romInfo: ;@ 158 | emuFlags: 159 | .byte 0 ;@ emuflags (label this so GUI.c can take a peek) see EmuSettings.h for bitfields 160 | .byte 0 ;@ (display type) 161 | .byte 0,0 ;@ (sprite follow val) 162 | gMachineSet: 163 | .byte HW_AUTO 164 | gMachine: 165 | .byte HW_WONDERSWANCOLOR 166 | gSOC: 167 | .byte SOC_SPHINX 168 | gLang: 169 | .byte 1 ;@ language 170 | gPaletteBank: 171 | .byte 0 ;@ palettebank 172 | .space 3 ;@ alignment. 173 | 174 | allocatedRomMem: 175 | .long 0 176 | allocatedRomMemSize: 177 | .long 0 178 | maxRomSize: 179 | .long 0 180 | g_BIOSBASE_BNW: 181 | .long 0 182 | g_BIOSBASE_COLOR: 183 | .long 0 184 | g_BIOSBASE_CRYSTAL: 185 | .long 0 186 | biosBase: 187 | .long 0 188 | 189 | ;@---------------------------------------------------------------------------- 190 | #ifdef GBA 191 | .section .sbss ;@ For the GBA 192 | #else 193 | .section .bss 194 | #endif 195 | .align 4 196 | wsRAM: 197 | .space 0x10000 198 | DIRTYTILES: 199 | .space 0x800 200 | biosSpace: 201 | .space 0x1000 202 | biosSpaceColor: 203 | .space 0x2000 204 | biosSpaceCrystal: 205 | .space 0x2000 206 | ;@---------------------------------------------------------------------------- 207 | .end 208 | #endif // #ifdef __arm__ 209 | -------------------------------------------------------------------------------- /source/Emubase.h: -------------------------------------------------------------------------------- 1 | #ifndef EMUBASE 2 | #define EMUBASE 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | typedef struct { //(config struct) 9 | char magic[4]; //="CFG",0 10 | int emuSettings; 11 | int sleepTime; // autoSleepTime in 60Hz frames 12 | u8 gammaValue; // from gfx.s 13 | u8 config; // Bit 0=border on/off. 14 | u8 controller; // from io.s 15 | u8 name[16]; // Name on start screen 16 | u8 birthYear[2]; // BCD encoded BIG endian 17 | u8 birthMonth; // BCD encoded 18 | u8 birthDay; // BCD encoded 19 | u8 sex; // 0 = ?, 1 = male, 2 = female 20 | u8 bloodType; // 0 = ?, 1 = A, 2 = B, 3 = O, 4 = AB 21 | u8 machine; 22 | u8 palette; 23 | char currentPath[256]; 24 | char monoBiosPath[256]; 25 | char colorBiosPath[256]; 26 | char crystalBiosPath[256]; 27 | char wonderWitchPath[256]; 28 | } ConfigData; 29 | 30 | #ifdef __cplusplus 31 | } // extern "C" 32 | #endif 33 | 34 | #endif // EMUBASE 35 | -------------------------------------------------------------------------------- /source/FileHandling.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "FileHandling.h" 6 | #include "Shared/EmuMenu.h" 7 | #include "Shared/EmuSettings.h" 8 | #include "Shared/FileHelper.h" 9 | #include "Shared/IPSPatch.h" 10 | #include "Shared/AsmExtra.h" 11 | #include "Shared/CartridgeRAM.h" 12 | #include "Main.h" 13 | #include "Gui.h" 14 | #include "Cart.h" 15 | #include "cpu.h" 16 | #include "Gfx.h" 17 | #include "io.h" 18 | #include "Memory.h" 19 | #include "InternalEEPROM.h" 20 | #include "WonderWitch.h" 21 | #include "WSCart/WSCart.h" 22 | 23 | extern u8 flashMemChanged; // From FlashMemory.s 24 | 25 | static const char *const folderName = "nitroswan"; 26 | static const char *const settingName = "settings.cfg"; 27 | static const char *const wsEepromName = "wsinternal.eeprom"; 28 | static const char *const wscEepromName = "wscinternal.eeprom"; 29 | static const char *const scEepromName = "scinternal.eeprom"; 30 | static const char *const nitroSwanName = "@ NitroSwan @"; 31 | 32 | char translateDSChar(u16 char16); 33 | 34 | ConfigData cfg; 35 | 36 | //--------------------------------------------------------------------------------- 37 | int initSettings() { 38 | cfg.config = 0; 39 | cfg.palette = 0; 40 | cfg.gammaValue = 0x30; 41 | cfg.emuSettings = AUTOPAUSE_EMULATION | AUTOLOAD_NVRAM | AUTOSLEEP_OFF | ENABLE_HEADPHONES; 42 | cfg.sleepTime = 60*60*5; 43 | cfg.controller = 0; // Don't swap A/B 44 | cfg.birthYear[0] = 0x19; 45 | cfg.birthYear[1] = 0x99; 46 | cfg.birthMonth = bin2BCD(PersonalData->birthMonth); 47 | cfg.birthDay = bin2BCD(PersonalData->birthDay); 48 | cfg.sex = 0; 49 | cfg.bloodType = 0; 50 | cfg.machine = ((PersonalData->language == 0) ? 0 : 1) | (HW_AUTO<<1); 51 | 52 | int i; 53 | for (i = 0; i < 13; i++) { 54 | s16 char16 = nitroSwanName[i]; 55 | cfg.name[i] = translateDSChar(char16); 56 | } 57 | // for (i = 0; i < PersonalData->nameLen; i++) { 58 | // s16 char16 = PersonalData->name[i]; 59 | // debugIO(char16, 0, "C"); 60 | // cfg.name[i] = translateDSChar(char16); 61 | // } 62 | cfg.name[i] = 0; 63 | return 0; 64 | } 65 | 66 | char translateDSChar(u16 char16) { 67 | // Translate numbers. 68 | if (char16 > 0x2F && char16 < 0x3A) { 69 | return char16 - 0x2F; 70 | } 71 | // Translate normal chars. 72 | if ((char16 > 0x40 && char16 < 0x5B) || (char16 > 0x60 && char16 < 0x7B)) { 73 | return (char16 & 0x1F) + 10; 74 | } 75 | // Check for heart (♥︎). 76 | if (char16 == 0xE017 || char16 == 0x0040) { 77 | return 0x25; 78 | } 79 | // Check for note (♪). 80 | if (char16 == 0x266A) { 81 | return 0x26; 82 | } 83 | // Check for plus (+). 84 | if (char16 == 0x002B) { 85 | return 0x27; 86 | } 87 | // Check for minus/dash (-). 88 | if (char16 == 0x002D || char16 == 0x30FC) { 89 | return 0x28; 90 | } 91 | // Check for different question marks (?). 92 | if (char16 == 0x003F || char16 == 0xFF1F || char16 == 0xE011) { 93 | return 0x29; 94 | } 95 | // Check for different dots/full stop (.). 96 | if (char16 == 0x002E || char16 == 0x3002) { 97 | return 0x2A; 98 | } 99 | return 0; // Space 100 | } 101 | 102 | bool updateSettingsFromWS() { 103 | bool changed = false; 104 | IntEEPROM *intProm = (IntEEPROM *)intEeprom.memory; 105 | 106 | WSUserData *userData = &intProm->userData; 107 | if (cfg.birthYear[0] != userData->birthYear[0] 108 | || cfg.birthYear[1] != userData->birthYear[1]) { 109 | cfg.birthYear[0] = userData->birthYear[0]; 110 | cfg.birthYear[1] = userData->birthYear[1]; 111 | changed = true; 112 | } 113 | if (cfg.birthMonth != userData->birthMonth) { 114 | cfg.birthMonth = userData->birthMonth; 115 | changed = true; 116 | } 117 | if (cfg.birthDay != userData->birthDay) { 118 | cfg.birthDay = userData->birthDay; 119 | changed = true; 120 | } 121 | if (cfg.sex != userData->sex) { 122 | cfg.sex = userData->sex; 123 | changed = true; 124 | } 125 | if (cfg.bloodType != userData->bloodType) { 126 | cfg.bloodType = userData->bloodType; 127 | changed = true; 128 | } 129 | if (memcmp(cfg.name, userData->name, 16) != 0) { 130 | memcpy(cfg.name, userData->name, 16); 131 | } 132 | settingsChanged |= changed; 133 | return changed; 134 | } 135 | 136 | int loadSettings() { 137 | FILE *file; 138 | int result = 0; 139 | 140 | if (findFolder(folderName)) { 141 | result = 1; 142 | } 143 | else if ( (file = fopen(settingName, "r")) ) { 144 | fread(&cfg, 1, sizeof(ConfigData), file); 145 | fclose(file); 146 | if (!strstr(cfg.magic,"cfg")) { 147 | infoOutput("Error in settings file."); 148 | result = 1; 149 | } 150 | } 151 | else { 152 | infoOutput("Couldn't open file:"); 153 | infoOutput(settingName); 154 | result = 1; 155 | } 156 | 157 | gBorderEnable = (cfg.config & 1) ^ 1; 158 | gPaletteBank = cfg.palette; 159 | gGammaValue = cfg.gammaValue & 0xF; 160 | gContrastValue = (cfg.gammaValue>>4) & 0xF; 161 | emuSettings = cfg.emuSettings & ~EMUSPEED_MASK; // Clear speed setting. 162 | sleepTime = cfg.sleepTime; 163 | joyCfg = (joyCfg & ~0x400)|((cfg.controller & 1)<<10); 164 | joyMapping = (joyMapping & ~1)|((cfg.controller & 2)>>1); 165 | gMachineSet = (cfg.machine>>1) & 0x7; 166 | strlcpy(currentDir, cfg.currentPath, sizeof(currentDir)); 167 | if (strlen(cfg.wonderWitchPath) == 0) { 168 | strlcpy(cfg.wonderWitchPath, cfg.currentPath, sizeof(cfg.wonderWitchPath)); 169 | } 170 | strlcpy(wwDir, cfg.wonderWitchPath, sizeof(currentDir)); 171 | if (gMachineSet != HW_AUTO) { 172 | gMachine = gMachineSet; 173 | } 174 | pauseEmulation = emuSettings & AUTOPAUSE_EMULATION; 175 | 176 | infoOutput("Settings loaded."); 177 | return result; 178 | } 179 | 180 | void saveSettings() { 181 | FILE *file; 182 | 183 | strcpy(cfg.magic,"cfg"); 184 | cfg.config = (gBorderEnable & 1) ^ 1; 185 | cfg.palette = gPaletteBank; 186 | cfg.gammaValue = (gGammaValue & 0xF) | (gContrastValue<<4); 187 | cfg.emuSettings = emuSettings & ~EMUSPEED_MASK; // Clear speed setting. 188 | cfg.sleepTime = sleepTime; 189 | cfg.controller = ((joyCfg>>10)&1) | (joyMapping&1)<<1; 190 | cfg.machine = (gMachineSet&7)<<1; 191 | strlcpy(cfg.currentPath, currentDir, sizeof(cfg.currentPath)); 192 | strlcpy(cfg.wonderWitchPath, wwDir, sizeof(cfg.currentPath)); 193 | 194 | if (findFolder(folderName)) { 195 | return; 196 | } 197 | if ( (file = fopen(settingName, "w")) ) { 198 | fwrite(&cfg, 1, sizeof(ConfigData), file); 199 | fclose(file); 200 | infoOutput("Settings saved."); 201 | } 202 | else { 203 | infoOutput("Couldn't open file:"); 204 | infoOutput(settingName); 205 | } 206 | saveIntEeproms(); 207 | } 208 | 209 | static void loadFlashMem() { 210 | FILE *flashFile; 211 | char flashName[FILENAME_MAX_LENGTH]; 212 | int saveSize = gRomSize; 213 | void *nvMem = romSpacePtr; 214 | 215 | setFileExtension(flashName, currentFilename, ".flash", sizeof(flashName)); 216 | 217 | if (findFolder(folderName)) { 218 | return; 219 | } 220 | if ( (flashFile = fopen(flashName, "r")) ) { 221 | if (fread(nvMem, 1, saveSize, flashFile) != saveSize) { 222 | infoOutput("Bad Flash file:"); 223 | infoOutput(flashName); 224 | } 225 | fclose(flashFile); 226 | infoOutput("Loaded Flash."); 227 | } 228 | else { 229 | infoOutput("Couldn't open Flash file:"); 230 | infoOutput(flashName); 231 | } 232 | } 233 | 234 | void loadNVRAM() { 235 | loadFlashMem(); 236 | FILE *wssFile; 237 | char nvRamName[FILENAME_MAX_LENGTH]; 238 | int saveSize = 0; 239 | void *nvMem = NULL; 240 | 241 | if (sramSize > 0) { 242 | saveSize = sramSize; 243 | nvMem = cartSRAM; 244 | setFileExtension(nvRamName, currentFilename, ".ram", sizeof(nvRamName)); 245 | } 246 | else if (eepromSize > 0) { 247 | saveSize = eepromSize; 248 | nvMem = cartEepromMem; 249 | setFileExtension(nvRamName, currentFilename, ".eeprom", sizeof(nvRamName)); 250 | } 251 | else { 252 | return; 253 | } 254 | if (findFolder(folderName)) { 255 | return; 256 | } 257 | if ( (wssFile = fopen(nvRamName, "r")) ) { 258 | if (fread(nvMem, 1, saveSize, wssFile) != saveSize) { 259 | infoOutput("Bad NVRAM file:"); 260 | infoOutput(nvRamName); 261 | } 262 | fclose(wssFile); 263 | infoOutput("Loaded NVRAM."); 264 | } 265 | else { 266 | // memset(nvMem, 0, saveSize); 267 | infoOutput("Couldn't open NVRAM file:"); 268 | infoOutput(nvRamName); 269 | } 270 | } 271 | 272 | static void saveFlashMem() { 273 | if (!flashMemChanged) { 274 | return; 275 | } 276 | FILE *flashFile; 277 | char flashName[FILENAME_MAX_LENGTH]; 278 | int saveSize = gRomSize; 279 | void *nvMem = romSpacePtr; 280 | 281 | setFileExtension(flashName, currentFilename, ".flash", sizeof(flashName)); 282 | 283 | if (findFolder(folderName)) { 284 | return; 285 | } 286 | if ( (flashFile = fopen(flashName, "w")) ) { 287 | if (fwrite(nvMem, 1, saveSize, flashFile) != saveSize) { 288 | infoOutput("Couldn't write Flash file:"); 289 | infoOutput(flashName); 290 | } 291 | else { 292 | flashMemChanged = 0; 293 | infoOutput("Saved Flash."); 294 | } 295 | fclose(flashFile); 296 | } 297 | else { 298 | infoOutput("Couldn't open Flash file:"); 299 | infoOutput(flashName); 300 | } 301 | } 302 | 303 | void saveNVRAM() { 304 | saveFlashMem(); 305 | FILE *wssFile; 306 | char nvRamName[FILENAME_MAX_LENGTH]; 307 | int saveSize = 0; 308 | void *nvMem = NULL; 309 | 310 | if (sramSize > 0) { 311 | saveSize = sramSize; 312 | nvMem = cartSRAM; 313 | setFileExtension(nvRamName, currentFilename, ".ram", sizeof(nvRamName)); 314 | } 315 | else if (eepromSize > 0) { 316 | saveSize = eepromSize; 317 | nvMem = cartEepromMem; 318 | setFileExtension(nvRamName, currentFilename, ".eeprom", sizeof(nvRamName)); 319 | } 320 | else { 321 | return; 322 | } 323 | if (findFolder(folderName)) { 324 | return; 325 | } 326 | if ( (wssFile = fopen(nvRamName, "w")) ) { 327 | if (fwrite(nvMem, 1, saveSize, wssFile) != saveSize) { 328 | infoOutput("Couldn't write correct number of bytes."); 329 | } 330 | fclose(wssFile); 331 | infoOutput("Saved NVRAM."); 332 | } 333 | else { 334 | infoOutput("Couldn't open NVRAM file:"); 335 | infoOutput(nvRamName); 336 | } 337 | } 338 | 339 | void loadState() { 340 | loadDeviceState(folderName); 341 | } 342 | 343 | void saveState() { 344 | saveDeviceState(folderName); 345 | } 346 | 347 | //--------------------------------------------------------------------------------- 348 | int loadIntEeprom(const char *name, u8 *dest, int size) { 349 | FILE *file; 350 | if ( (file = fopen(name, "r")) ) { 351 | fread(dest, 1, size, file); 352 | fclose(file); 353 | infoOutput("Internal EEPROM loaded."); 354 | return 0; 355 | } 356 | 357 | infoOutput("Couldn't open file:"); 358 | infoOutput(name); 359 | return 1; 360 | } 361 | 362 | int saveIntEeprom(const char *name, u8 *source, int size) { 363 | FILE *file; 364 | if ( (file = fopen(name, "w")) ) { 365 | fwrite(source, 1, size, file); 366 | fclose(file); 367 | infoOutput("Internal EEPROM saved."); 368 | return 0; 369 | } 370 | 371 | infoOutput("Couldn't open file:"); 372 | infoOutput(name); 373 | return 1; 374 | } 375 | 376 | static void initIntEepromWS(IntEEPROM *intProm) { 377 | WSUserData *userData = &intProm->userData; 378 | memcpy(userData->name, cfg.name, 16); 379 | userData->birthYear[0] = cfg.birthYear[0]; 380 | userData->birthYear[1] = cfg.birthYear[1]; 381 | userData->birthMonth = cfg.birthMonth; 382 | userData->birthDay = cfg.birthDay; 383 | userData->sex = cfg.sex; 384 | userData->bloodType = cfg.bloodType; 385 | } 386 | static void initIntEepromWSC(IntEEPROM *intProm) { 387 | initIntEepromWS(intProm); 388 | intProm->splashData.consoleFlags = 3; 389 | } 390 | static void initIntEepromSC(IntEEPROM *intProm) { 391 | initIntEepromWSC(intProm); 392 | WSBootSplash *splashData = &intProm->splashData; 393 | splashData->crystalLCD70 = 0xd0; 394 | splashData->crystalLCD71 = 0x77; 395 | splashData->crystalLCD72 = 0xf7; 396 | splashData->crystalLCD73 = 0x06; 397 | splashData->crystalLCD74 = 0xe2; 398 | splashData->crystalLCD75 = 0x0a; 399 | splashData->crystalLCD76 = 0xea; 400 | splashData->crystalLCD77 = 0xee; 401 | } 402 | 403 | 404 | static void clearIntEepromWS() { 405 | memset(wsEepromMem, 0, sizeof(wsEepromMem)); 406 | initIntEepromWS((IntEEPROM *)wsEepromMem); 407 | } 408 | static void clearIntEepromWSC() { 409 | memset(wscEepromMem, 0, sizeof(wscEepromMem)); 410 | initIntEepromWSC((IntEEPROM *)wscEepromMem); 411 | } 412 | static void clearIntEepromSC() { 413 | memset(scEepromMem, 0, sizeof(scEepromMem)); 414 | initIntEepromSC((IntEEPROM *)scEepromMem); 415 | } 416 | 417 | int loadIntEeproms() { 418 | int status = 0; 419 | clearIntEepromWS(); 420 | clearIntEepromWSC(); 421 | clearIntEepromSC(); 422 | if (!findFolder(folderName)) { 423 | status = loadIntEeprom(wsEepromName, wsEepromMem, sizeof(wsEepromMem)); 424 | status |= loadIntEeprom(wscEepromName, wscEepromMem, sizeof(wscEepromMem)); 425 | status |= loadIntEeprom(scEepromName, scEepromMem, sizeof(scEepromMem)); 426 | } 427 | return status; 428 | } 429 | 430 | int saveIntEeproms() { 431 | int status = 1; 432 | if (!findFolder(folderName)) { 433 | switch (gSOC) { 434 | case SOC_ASWAN: 435 | status = saveIntEeprom(wsEepromName, wsEepromMem, sizeof(wsEepromMem)); 436 | break; 437 | case SOC_SPHINX: 438 | status = saveIntEeprom(wscEepromName, wscEepromMem, sizeof(wscEepromMem)); 439 | break; 440 | case SOC_SPHINX2: 441 | status = saveIntEeprom(scEepromName, scEepromMem, sizeof(scEepromMem)); 442 | break; 443 | } 444 | } 445 | return status; 446 | } 447 | 448 | void selectEEPROM() { 449 | pauseEmulation = true; 450 | ui10(); 451 | const char *eepromName = browseForFileType(".eeprom"); 452 | if (eepromName) { 453 | switch (gSOC) { 454 | case SOC_SPHINX: 455 | loadIntEeprom(eepromName, wscEepromMem, sizeof(wscEepromMem)); 456 | break; 457 | case SOC_SPHINX2: 458 | loadIntEeprom(eepromName, scEepromMem, sizeof(scEepromMem)); 459 | break; 460 | } 461 | } 462 | backOutOfMenu(); 463 | } 464 | 465 | void clearIntEeproms() { 466 | switch (gSOC) { 467 | case SOC_ASWAN: 468 | clearIntEepromWS(); 469 | break; 470 | case SOC_SPHINX: 471 | clearIntEepromWSC(); 472 | break; 473 | case SOC_SPHINX2: 474 | clearIntEepromSC(); 475 | break; 476 | } 477 | } 478 | 479 | //--------------------------------------------------------------------------------- 480 | bool loadGame(const char *gameName) { 481 | if (gameName) { 482 | cls(0); 483 | drawText(" Please wait, loading.", 11, 0); 484 | u32 maxSize = allocatedRomMemSize; 485 | u8 *romPtr = allocatedRomMem; 486 | gRomSize = loadROM(romPtr, gameName, maxSize); 487 | if (!gRomSize) { 488 | // Enable Expansion RAM in GBA port 489 | drawText(" Trying Exp-RAM.", 10, 0); 490 | if (cartRamInit(DETECT_RAM) != DETECT_RAM) { 491 | drawText(" Using Exp-RAM.", 10, 0); 492 | infoOutput("Using Exp-RAM."); 493 | romPtr = (u8 *)cartRamUnlock(); 494 | maxSize = cartRamSize(); 495 | gRomSize = loadROM(romPtr, gameName, maxSize); 496 | enableSlot2Cache(); 497 | } 498 | } 499 | else { 500 | cartRamLock(); 501 | } 502 | 503 | if (gRomSize) { 504 | maxRomSize = maxSize; 505 | romSpacePtr = romPtr; 506 | 507 | setEmuSpeed(0); 508 | checkMachine(); 509 | loadCart(); 510 | setupEmuBackground(); 511 | gameInserted = true; 512 | if (emuSettings & AUTOLOAD_NVRAM) { 513 | loadNVRAM(); 514 | } 515 | if (emuSettings & AUTOLOAD_STATE) { 516 | loadState(); 517 | } 518 | powerIsOn = true; 519 | closeMenu(); 520 | return false; 521 | } 522 | } 523 | return true; 524 | } 525 | 526 | void selectGame() { 527 | pauseEmulation = true; 528 | ui10(); 529 | const char *gameName = browseForFileType(FILEEXTENSIONS".zip"); 530 | loadGame(gameName); 531 | backOutOfMenu(); 532 | } 533 | 534 | void checkMachine() { 535 | char fileExt[8]; 536 | if (gMachineSet == HW_AUTO) { 537 | getFileExtension(fileExt, currentFilename); 538 | if (romSpacePtr[gRomSize - 9] != 0 || strstr(fileExt, ".wsc")) { 539 | gMachine = HW_WONDERSWANCOLOR; 540 | } 541 | else if (strstr(fileExt, ".pc2")) { 542 | gMachine = HW_POCKETCHALLENGEV2; 543 | } 544 | else { 545 | gMachine = HW_WONDERSWAN; 546 | } 547 | } 548 | else { 549 | gMachine = gMachineSet; 550 | } 551 | } 552 | 553 | //--------------------------------------------------------------------------------- 554 | void ejectCart() { 555 | gRomSize = 0x200000; 556 | memset(romSpacePtr, -1, gRomSize); 557 | gameInserted = false; 558 | } 559 | 560 | //--------------------------------------------------------------------------------- 561 | static int loadBIOS(void *dest, const char *fPath, const int maxSize) { 562 | char tempString[FILEPATH_MAX_LENGTH]; 563 | char *sPtr; 564 | 565 | cls(0); 566 | strlcpy(tempString, fPath, sizeof(tempString)); 567 | if ( (sPtr = strrchr(tempString, '/')) ) { 568 | sPtr[0] = 0; 569 | sPtr += 1; 570 | chdir("/"); 571 | chdir(tempString); 572 | return loadROM(dest, sPtr, maxSize); 573 | } 574 | return 0; 575 | } 576 | 577 | int loadBnWBIOS(void) { 578 | if (loadBIOS(biosSpace, cfg.monoBiosPath, sizeof(biosSpace))) { 579 | g_BIOSBASE_BNW = biosSpace; 580 | return 1; 581 | } 582 | g_BIOSBASE_BNW = NULL; 583 | return 0; 584 | } 585 | 586 | int loadColorBIOS(void) { 587 | if (loadBIOS(biosSpaceColor, cfg.colorBiosPath, sizeof(biosSpaceColor))) { 588 | g_BIOSBASE_COLOR = biosSpaceColor; 589 | return 1; 590 | } 591 | g_BIOSBASE_COLOR = NULL; 592 | return 0; 593 | } 594 | 595 | int loadCrystalBIOS(void) { 596 | if (loadBIOS(biosSpaceCrystal, cfg.crystalBiosPath, sizeof(biosSpaceCrystal))) { 597 | g_BIOSBASE_CRYSTAL = biosSpaceCrystal; 598 | return 1; 599 | } 600 | g_BIOSBASE_CRYSTAL = NULL; 601 | return 0; 602 | } 603 | 604 | static bool selectBios(char *dest, const char *fileTypes) { 605 | ui10(); 606 | const char *biosName = browseForFileType(fileTypes); 607 | backOutOfMenu(); 608 | 609 | if (biosName) { 610 | strlcpy(dest, currentDir, FILEPATH_MAX_LENGTH); 611 | strlcat(dest, "/", FILEPATH_MAX_LENGTH); 612 | strlcat(dest, biosName, FILEPATH_MAX_LENGTH); 613 | return true; 614 | } 615 | return false; 616 | } 617 | 618 | void selectBnWBios() { 619 | if (selectBios(cfg.monoBiosPath, ".ws.rom.zip")) { 620 | loadBnWBIOS(); 621 | } 622 | } 623 | 624 | void selectColorBios() { 625 | if (selectBios(cfg.colorBiosPath, ".ws.wsc.rom.zip")) { 626 | loadColorBIOS(); 627 | } 628 | } 629 | 630 | void selectCrystalBios() { 631 | if (selectBios(cfg.crystalBiosPath, ".ws.wsc.rom.zip")) { 632 | loadCrystalBIOS(); 633 | } 634 | } 635 | 636 | void selectIPS() { 637 | pauseEmulation = true; 638 | ui10(); 639 | const char *ipsName = browseForFileType(".ips"); 640 | if (ipsName && patchRom(romSpacePtr, ipsName, gRomSize)) { 641 | checkMachine(); 642 | loadCart(); 643 | setupEmuBackground(); 644 | } 645 | backOutOfMenu(); 646 | } 647 | -------------------------------------------------------------------------------- /source/FileHandling.h: -------------------------------------------------------------------------------- 1 | #ifndef FILEHANDLING_HEADER 2 | #define FILEHANDLING_HEADER 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "Emubase.h" 9 | #include "WonderSwan.h" 10 | 11 | #define FILEEXTENSIONS ".ws.wsc.pc2.pcv2" 12 | 13 | extern ConfigData cfg; 14 | 15 | int initSettings(void); 16 | bool updateSettingsFromWS(void); 17 | /// Load user settings and internal eeprom. 18 | int loadSettings(void); 19 | /// Save user settings and internal eeprom. 20 | void saveSettings(void); 21 | bool loadGame(const char *gameName); 22 | void checkMachine(void); 23 | /// Load SRAM, EEPROM and/or Flash if they exist. 24 | void loadNVRAM(void); 25 | /// Save SRAM, EEPROM and/or Flash if they exist. 26 | void saveNVRAM(void); 27 | void loadState(void); 28 | void saveState(void); 29 | void ejectCart(void); 30 | void selectGame(void); 31 | void selectBnWBios(void); 32 | void selectColorBios(void); 33 | void selectCrystalBios(void); 34 | int loadBnWBIOS(void); 35 | int loadColorBIOS(void); 36 | int loadCrystalBIOS(void); 37 | int loadIntEeproms(void); 38 | int saveIntEeproms(void); 39 | void selectEEPROM(void); 40 | void clearIntEeproms(void); 41 | void selectIPS(void); 42 | 43 | #ifdef __cplusplus 44 | } // extern "C" 45 | #endif 46 | 47 | #endif // FILEHANDLING_HEADER 48 | -------------------------------------------------------------------------------- /source/Gfx.h: -------------------------------------------------------------------------------- 1 | #ifndef GFX_HEADER 2 | #define GFX_HEADER 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "Sphinx/Sphinx.h" 9 | 10 | extern u8 gFlicker; 11 | extern u8 gTwitch; 12 | extern u8 gGfxMask; 13 | 14 | extern Sphinx sphinx0; 15 | extern u16 EMUPALBUFF[0x200]; 16 | extern u16 MAPPED_BNW[0x10]; 17 | extern u32 GFX_DISPCNT; 18 | extern u16 GFX_BG0CNT; 19 | extern u16 GFX_BG1CNT; 20 | 21 | void gfxInit(void); 22 | void vblIrqHandler(void); 23 | 24 | /** 25 | * Calculate new (color) palette look up table. 26 | * @param gammaVal: 0-4. 27 | * @param contrast: 0-4. 28 | * @param bright: -255 -> 255. 29 | */ 30 | void paletteInit(u8 gammaVal, u8 contrast, int bright); 31 | 32 | /** 33 | * Calculate new (mono) palette look up table. 34 | * @param gammaVal: 0-4. 35 | * @param contrast: 0-4. 36 | * @param bright: -255 -> 255. 37 | */ 38 | void monoPalInit(u8 gammaVal, u8 contrast, int bright); 39 | 40 | void paletteTxAll(void); 41 | void shutDownLCD(void); 42 | void updateLCDRefresh(void); 43 | void gfxRefresh(void); 44 | u8 v30ReadPort(u16 port); 45 | u16 v30ReadPort16(u16 port); 46 | void v30WritePort(u8 value, u16 port); 47 | void v30WritePort16(u16 value, u16 port); 48 | 49 | void pushVolumeButton(void); 50 | int getHeadphones(void); 51 | void setHeadphones(bool enable); 52 | void setSerialByteIn(u8 value); 53 | void setPowerOff(void); 54 | 55 | #ifdef __cplusplus 56 | } // extern "C" 57 | #endif 58 | 59 | #endif // GFX_HEADER 60 | -------------------------------------------------------------------------------- /source/Gfx.s: -------------------------------------------------------------------------------- 1 | #ifdef __arm__ 2 | 3 | #include "Shared/nds_asm.h" 4 | #include "Sphinx/Sphinx.i" 5 | 6 | #define ALLOW_REFRESH_CHG (1<<19) 7 | 8 | .global gFlicker 9 | .global gTwitch 10 | .global gGfxMask 11 | .global GFX_DISPCNT 12 | .global GFX_BG0CNT 13 | .global GFX_BG1CNT 14 | .global EMUPALBUFF 15 | .global MAPPED_BNW 16 | .global frameTotal 17 | .global sphinx0 18 | 19 | .global gfxInit 20 | .global gfxReset 21 | .global monoPalInit 22 | .global paletteInit 23 | .global paletteTxAll 24 | .global gfxRefresh 25 | .global gfxEndFrame 26 | .global vblIrqHandler 27 | .global v30ReadPort 28 | .global v30ReadPort16 29 | .global v30WritePort 30 | .global v30WritePort16 31 | .global shutDownLCD 32 | .global updateLCDRefresh 33 | .global setScreenRefresh 34 | .global pushVolumeButton 35 | .global getHeadphones 36 | .global setHeadphones 37 | .global setLowBattery 38 | .global setSerialByteIn 39 | .global setInterruptExternal 40 | .global getInterruptVector 41 | .global setBusStatus 42 | .global setPowerOff 43 | 44 | 45 | .syntax unified 46 | .arm 47 | 48 | #ifdef GBA 49 | .section .ewram, "ax", %progbits ;@ For the GBA 50 | #else 51 | .section .text ;@ For anything else 52 | #endif 53 | .align 2 54 | ;@---------------------------------------------------------------------------- 55 | gfxInit: ;@ Called from machineInit 56 | ;@---------------------------------------------------------------------------- 57 | stmfd sp!,{lr} 58 | 59 | ldr r0,=OAM_BUFFER1 ;@ No stray sprites please 60 | mov r1,#0x200+SCREEN_HEIGHT 61 | mov r2,#0x100*3 ;@ 3 buffers 62 | bl memset_ 63 | 64 | bl wsVideoInit 65 | bl gfxWinInit 66 | 67 | ldr r0,=DISP_CTRL_LUT ;@ Destination 68 | mov r1,#0 69 | dispLutLoop: 70 | and r2,r1,#0x03 ;@ BG & FG 71 | orr r2,r2,#0x04 ;@ Allways enable Border. 72 | tst r1,#0x04 ;@ WS Sprites on? 73 | orrne r2,r2,#0x10 ;@ Sprites 74 | orr r2,r2,r2,lsl#8 ;@ Set both Win0 & Win1 75 | and r3,r1,#0x30 ;@ FG Win Ctrl 76 | cmp r3,#0x20 ;@ FG only inside Win0 77 | biceq r2,r2,#0x0200 78 | cmp r3,#0x30 ;@ FG only outside Win0 79 | biceq r2,r2,#0x0002 80 | orr r2,r2,#0x002C0000 ;@ WinOUT, Only BG2, BG3 & BLEND enabled outside Windows. 81 | str r2,[r0],#4 82 | add r1,r1,#1 83 | cmp r1,#64 84 | bne dispLutLoop 85 | 86 | ldmfd sp!,{pc} 87 | 88 | ;@---------------------------------------------------------------------------- 89 | gfxReset: ;@ Called with CPU reset 90 | ;@---------------------------------------------------------------------------- 91 | stmfd sp!,{r4,r5,lr} 92 | 93 | ldr r0,=gfxState 94 | mov r1,#5 ;@ 5*4 95 | bl memclr_ ;@ Clear GFX regs 96 | 97 | bl gfxWinInit 98 | 99 | ldr r0,=wsRAM 100 | ldr r1,=gMachine 101 | ldrb r1,[r1] 102 | ldr r2,=V30SetIRQPin 103 | bl wsVideoReset0 104 | ldr r3,=handleSerialInEmpty 105 | str r3,[spxptr,#rxFunction] 106 | ldr r3,=handleSerialReceive 107 | str r3,[spxptr,#txFunction] 108 | 109 | ldr r0,=cartOrientation 110 | ldrb r0,[r0] 111 | strb r0,[spxptr,#wsvOrientation] 112 | 113 | ldr r0,=gRomSize 114 | ldr r0,[r0] 115 | cmp r0,#0 116 | movne r0,#1 117 | bl wsvSetCartOk 118 | 119 | ldr r4,=gGammaValue 120 | ldr r5,=gContrastValue 121 | ldrb r4,[r4] 122 | ldrb r5,[r5] 123 | mov r0,r4 124 | mov r1,r5 125 | mov r2,#0 126 | bl paletteInit ;@ Do palette mapping 127 | mov r0,r4 128 | mov r1,r5 129 | mov r2,#0 130 | bl monoPalInit ;@ Do mono palette mapping 131 | bl paletteTxAll ;@ Transfer it 132 | 133 | ldr r0,=emuSettings 134 | ldr r0,[r0] 135 | and r0,r0,#1<<18 136 | ldr r1,=gMachine 137 | ldrb r1,[r1] 138 | cmp r1,#HW_POCKETCHALLENGEV2 139 | moveq r0,#1 ;@ PCV2 has headphones always. 140 | bl setHeadphones 141 | 142 | ldmfd sp!,{r4,r5,pc} 143 | 144 | ;@---------------------------------------------------------------------------- 145 | gfxWinInit: 146 | ;@---------------------------------------------------------------------------- 147 | stmfd sp!,{lr} 148 | mov r0,#REG_BASE 149 | ;@ Horizontal start-end 150 | ldr r1,=(((SCREEN_WIDTH-GAME_WIDTH)/2)<<8)+(SCREEN_WIDTH+GAME_WIDTH)/2 151 | orr r1,r1,r1,lsl#16 ;@ Also WIN1H 152 | str r1,[r0,#REG_WIN0H] 153 | ;@ Vertical start-end 154 | ldr r2,=(((SCREEN_HEIGHT-GAME_HEIGHT)/2)<<8)+(SCREEN_HEIGHT+GAME_HEIGHT)/2 155 | orr r2,r2,r2,lsl#16 ;@ Also WIN1V 156 | str r2,[r0,#REG_WIN0V] 157 | 158 | ldr r3,=0x002C0004 ;@ WinIN0/1, BG0, BG1, SPR & BLEND inside Win0 159 | str r3,[r0,#REG_WININ] ;@ WinOUT, Only BG2, BG3 & BLEND enabled outside Windows. 160 | 161 | ldr lr,=WININOUTBUFF1 162 | mov r0,#SCREEN_HEIGHT 163 | gfxWinLoop: ;@ 3 buffers 164 | stmia lr!,{r1-r3} 165 | stmia lr!,{r1-r3} 166 | stmia lr!,{r1-r3} 167 | subs r0,r0,#1 168 | bne gfxWinLoop 169 | ldmfd sp!,{pc} 170 | ;@---------------------------------------------------------------------------- 171 | monoPalInit: 172 | .type monoPalInit STT_FUNC 173 | ;@ Called by ui.c: void monoPalInit(gammaVal, contrast, brightness); 174 | ;@---------------------------------------------------------------------------- 175 | stmfd sp!,{r4-r9,lr} 176 | mov r8,#30 177 | rsb r1,r1,#4 178 | mul r8,r1,r8 179 | mov r1,r0 ;@ Gamma value = 0 -> 4 180 | mov r9,r2 181 | ldr spxptr,=sphinx0 182 | ldr r0,=gSOC 183 | ldrb r0,[r0] 184 | cmp r0,#SOC_ASWAN 185 | movne r0,#1 186 | ldreq r0,=gPaletteBank 187 | ldrbeq r0,[r0] 188 | adr r5,monoPalettes ;@ 3*16 for each palette 189 | add r5,r5,r0,lsl#4 ;@ +16 190 | add r5,r5,r0,lsl#5 ;@ +32 191 | ldr r6,=MAPPED_BNW 192 | 193 | mov r4,#16 194 | monoPalLoop: 195 | ldrb r0,[r5],#1 ;@ Red 196 | bl cPrefix 197 | mov r7,r0 198 | ldrb r0,[r5],#1 ;@ Green 199 | bl cPrefix 200 | orr r7,r7,r0,lsl#5 201 | ldrb r0,[r5],#1 ;@ Blue 202 | bl cPrefix 203 | orr r7,r7,r0,lsl#10 204 | strh r7,[r6],#2 205 | 206 | subs r4,r4,#1 207 | bne monoPalLoop 208 | 209 | ldmfd sp!,{r4-r9,lr} 210 | bx lr 211 | ;@---------------------------------------------------------------------------- 212 | monoPalettes: 213 | 214 | ;@ Classic 215 | .byte 0xF6,0xFE,0xAE, 0xE7,0xEE,0xA2, 0xD8,0xDE,0x96, 0xCA,0xCE,0x8B 216 | .byte 0xBB,0xBE,0x7F, 0xAC,0xAE,0x74, 0x9E,0x9E,0x68, 0x8F,0x8E,0x5C 217 | .byte 0x80,0x7F,0x51, 0x72,0x6F,0x45, 0x63,0x5F,0x3A, 0x54,0x4F,0x2E 218 | .byte 0x46,0x3F,0x22, 0x37,0x2F,0x17, 0x28,0x1F,0x0B, 0x19,0x0F,0x00 219 | ;@ Black & White 220 | .byte 0xFF,0xFF,0xFF, 0xEE,0xEE,0xEE, 0xDD,0xDD,0xDD, 0xCC,0xCC,0xCC 221 | .byte 0xBB,0xBB,0xBB, 0xAA,0xAA,0xAA, 0x99,0x99,0x99, 0x88,0x88,0x88 222 | .byte 0x77,0x77,0x77, 0x66,0x66,0x66, 0x55,0x55,0x55, 0x44,0x44,0x44 223 | .byte 0x33,0x33,0x33, 0x22,0x22,0x22, 0x11,0x11,0x11, 0x00,0x00,0x00 224 | ;@ Red 225 | .byte 0xFF,0xC0,0xC0, 0xEE,0xB3,0xB3, 0xDD,0xA6,0xA6, 0xCC,0x99,0x99 226 | .byte 0xBB,0x8C,0x8C, 0xAA,0x80,0x80, 0x99,0x73,0x73, 0x88,0x66,0x66 227 | .byte 0x77,0x59,0x59, 0x66,0x4C,0x4C, 0x55,0x40,0x40, 0x44,0x33,0x33 228 | .byte 0x33,0x26,0x26, 0x22,0x19,0x19, 0x11,0x0C,0x0C, 0x00,0x00,0x00 229 | ;@ Green 230 | .byte 0xC0,0xFF,0xC0, 0xB3,0xEE,0xB3, 0xA6,0xDD,0xA6, 0x99,0xCC,0x99 231 | .byte 0x8C,0xBB,0x8C, 0x80,0xAA,0x80, 0x73,0x99,0x73, 0x66,0x88,0x66 232 | .byte 0x59,0x77,0x59, 0x4C,0x66,0x4C, 0x40,0x55,0x40, 0x33,0x44,0x33 233 | .byte 0x26,0x33,0x26, 0x19,0x22,0x19, 0x0C,0x11,0x0C, 0x00,0x00,0x00 234 | ;@ Blue 235 | .byte 0xC0,0xC0,0xFF, 0xB3,0xB3,0xEE, 0xA6,0xA6,0xDD, 0x99,0x99,0xCC 236 | .byte 0x8C,0x8C,0xBB, 0x80,0x80,0xAA, 0x73,0x73,0x99, 0x66,0x66,0x88 237 | .byte 0x59,0x59,0x77, 0x4C,0x4C,0x66, 0x40,0x40,0x55, 0x33,0x33,0x44 238 | .byte 0x26,0x26,0x33, 0x19,0x19,0x22, 0x0C,0x0C,0x11, 0x00,0x00,0x00 239 | ;@ Green-Blue 240 | .byte 0xFF,0xFF,0xFF, 0xDD,0xFF,0xDD, 0xBB,0xFF,0xBB, 0xBB,0xBB,0xFF 241 | .byte 0x77,0xFF,0x77, 0x55,0xEE,0x55, 0x88,0x88,0xFF, 0x11,0xC6,0x11 242 | .byte 0x77,0x77,0xF8, 0x00,0xBB,0x00, 0x00,0xA1,0x00, 0x22,0x22,0xBB 243 | .byte 0x00,0x6E,0x00, 0x00,0x55,0x00, 0x00,0x00,0x3B, 0x00,0x00,0x00 244 | ;@ Blue-Green 245 | .byte 0xFF,0xFF,0xFF, 0xE5,0xE5,0xFF, 0xCC,0xCC,0xFF, 0x99,0xFF,0x99 246 | .byte 0xAA,0xAA,0xFF, 0x99,0x99,0xFF, 0x33,0xDD,0x33, 0x66,0x66,0xF3 247 | .byte 0x22,0xD1,0x22, 0x55,0x55,0xEE, 0x3B,0x3B,0xD4, 0x00,0x88,0x00 248 | .byte 0x11,0x11,0x99, 0x00,0x00,0x77, 0x00,0x2A,0x00, 0x00,0x00,0x00 249 | ;@ Puyo Puyo Tsu 250 | .byte 0xFF,0xFF,0xFF, 0xF6,0xEE,0xD4, 0xEE,0xDD,0xAA, 0xD8,0xCC,0xC0 251 | .byte 0xDD,0xBB,0x88, 0xFF,0x99,0x35, 0xFD,0xAF,0x07, 0xDA,0x6C,0x00 252 | .byte 0xAF,0x8D,0x49, 0x16,0xB8,0x1E, 0xAA,0x24,0x00, 0x3C,0x4E,0xAA 253 | .byte 0x5D,0x44,0x08, 0x44,0x33,0x00, 0x22,0x19,0x00, 0x00,0x00,0x00 254 | ;@---------------------------------------------------------------------------- 255 | paletteInit: ;@ r0-r3 modified. 256 | .type paletteInit STT_FUNC 257 | ;@ Called by ui.c: void paletteInit(gammaVal, contrast, brightness); 258 | ;@---------------------------------------------------------------------------- 259 | stmfd sp!,{r4-r9,lr} 260 | mov r8,#30 261 | rsb r1,r1,#4 262 | mul r8,r1,r8 263 | mov r9,r2 264 | mov r1,r0 ;@ Gamma value = 0 -> 4 265 | mov r7,#0xF ;@ mask 266 | ldr r6,=MAPPED_RGB 267 | mov r4,#4096*2 268 | sub r4,r4,#2 269 | noMap: ;@ Map 0000rrrrggggbbbb -> 0bbbbbgggggrrrrr 270 | and r0,r7,r4,lsr#1 ;@ Blue ready 271 | bl gPrefix 272 | mov r5,r0,lsl#10 273 | 274 | and r0,r7,r4,lsr#5 ;@ Green ready 275 | bl gPrefix 276 | orr r5,r5,r0,lsl#5 277 | 278 | and r0,r7,r4,lsr#9 ;@ Red ready 279 | bl gPrefix 280 | orr r5,r5,r0 281 | 282 | strh r5,[r6,r4] 283 | subs r4,r4,#2 284 | bpl noMap 285 | 286 | ldmfd sp!,{r4-r9,lr} 287 | bx lr 288 | 289 | ;@---------------------------------------------------------------------------- 290 | gPrefix: 291 | orr r0,r0,r0,lsl#4 292 | cPrefix: 293 | mov r2,r8 294 | ;@---------------------------------------------------------------------------- 295 | brightConvert: ;@ Takes value in r0(0-0xFF), brightness in r9(-255 -> 255),returns new value in r0=0xFF 296 | ;@---------------------------------------------------------------------------- 297 | adds r0,r0,r9 298 | movmi r0,#0 299 | cmp r0,#0xFF 300 | movpl r0,#0xFF 301 | ;@---------------------------------------------------------------------------- 302 | contrastConvert: ;@ Takes value in r0(0-0xFF), gamma in r1(0-4), contrast in r2(0-255) returns new value in r0=0xFF 303 | ;@---------------------------------------------------------------------------- 304 | rsb r3,r2,#256 305 | mul r0,r3,r0 306 | add r0,r0,r2,lsl#7 307 | mov r0,r0,lsr#8 308 | ;@---------------------------------------------------------------------------- 309 | gammaConvert: ;@ Takes value in r0(0-0xFF), gamma in r1(0-4),returns new value in r0=0x1F 310 | ;@---------------------------------------------------------------------------- 311 | rsb r2,r0,#0x100 312 | mul r3,r2,r2 313 | rsbs r2,r3,#0x10000 314 | rsb r3,r1,#4 315 | orr r0,r0,r0,lsl#8 316 | mul r2,r1,r2 317 | mla r0,r3,r0,r2 318 | movs r0,r0,lsr#13 319 | 320 | bx lr 321 | ;@---------------------------------------------------------------------------- 322 | paletteTxAll: ;@ Called from ui.c 323 | .type paletteTxAll STT_FUNC 324 | ;@---------------------------------------------------------------------------- 325 | ldr r0,=EMUPALBUFF 326 | ldr spxptr,=sphinx0 327 | ;@---------------------------------------------------------------------------- 328 | paletteTx: ;@ r0=destination, spxptr=Sphinx 329 | ;@---------------------------------------------------------------------------- 330 | ldr r1,=MAPPED_RGB 331 | ldr r2,=0x1FFE 332 | stmfd sp!,{r4-r8,lr} 333 | ldr r8,[spxptr,#wsvDefaultBgCol] ;@ Used when LCD off 334 | mov r5,#0 335 | ldrb r3,[spxptr,#wsvBgColor];@ Background color 336 | ldrb r7,[spxptr,#wsvVideoMode] 337 | tst r7,#0x80 ;@ Color mode? 338 | beq bnwTx 339 | 340 | ldr r4,[spxptr,#paletteRAM] 341 | mov r3,r3,lsl#1 342 | ldrh r3,[r4,r3] 343 | movs r8,r8,lsl#1 ;@ LCD on? 344 | andcs r8,r2,r3,lsl#1 345 | ldrh r3,[r1,r8] 346 | strh r3,[r0] ;@ Background color 347 | tst r7,#0x40 ;@ 4bitplane mode? 348 | beq col4Tx 349 | add r6,r0,#0x100 ;@ Sprite pal ofs - r5 350 | txLoop: 351 | ldrh r3,[r4],#2 352 | and r3,r2,r3,lsl#1 353 | ldrh r3,[r1,r3] 354 | cmp r5,#0x00 355 | strhne r3,[r0,r5] ;@ Background palette 356 | cmp r5,#0x100 357 | strhpl r3,[r6,r5] ;@ Sprite palette 358 | 359 | add r5,r5,#2 360 | cmp r5,#0x200 361 | bmi txLoop 362 | 363 | ldmfd sp!,{r4-r8,lr} 364 | bx lr 365 | 366 | col4Tx: 367 | col4TxLoop: 368 | ldrh r3,[r4,r5] 369 | and r3,r2,r3,lsl#1 370 | ldrh r3,[r1,r3] 371 | tst r5,#0x1E 372 | strhne r3,[r0] ;@ Background palette 373 | strh r3,[r0,#0x8] ;@ Opaque tiles palette 374 | cmp r5,#0x100 375 | addpl r6,r0,#0x100 376 | strhpl r3,[r6] ;@ Sprite palette 377 | strhpl r3,[r6,#0x8] ;@ Sprite palette opaque 378 | 379 | add r0,r0,#2 380 | add r5,r5,#2 381 | tst r5,#6 382 | bne col4TxLoop 383 | add r0,r0,#0x18 384 | add r5,r5,#0x18 385 | cmp r5,#0x200 386 | bmi col4TxLoop 387 | 388 | ldmfd sp!,{r4-r8,lr} 389 | bx lr 390 | 391 | bnwTx: 392 | add r1,r1,#MAPPED_BNW-MAPPED_RGB 393 | mov r2,#0x1E 394 | add r4,spxptr,#wsvPalette0 395 | and r3,r3,#0x7 396 | tst r3,#1 397 | add r7,spxptr,#wsvColor01 398 | ldrb r3,[r7,r3,lsr#1] 399 | andeq r3,r2,r3,lsl#1 400 | andne r3,r2,r3,lsr#3 401 | mvns r8,r8,lsl#1 ;@ LCD on? 402 | andcc r3,r2,r8 403 | ldrh r3,[r1,r3] 404 | strh r3,[r0] ;@ Background color 405 | bnwTxLoop2: 406 | ldrh r6,[r4],#2 407 | bnwTxLoop: 408 | and r3,r6,#0x7 409 | mov r6,r6,lsr#4 410 | tst r3,#1 411 | ldrb r3,[r7,r3,lsr#1] 412 | andeq r3,r2,r3,lsl#1 413 | andne r3,r2,r3,lsr#3 414 | ldrh r3,[r1,r3] 415 | 416 | tst r5,#0x1E 417 | strhne r3,[r0] ;@ Background palette 418 | strh r3,[r0,#0x8] ;@ Opaque tiles palette 419 | cmp r5,#0x100 420 | addpl r8,r0,#0x100 421 | strhpl r3,[r8] ;@ Sprite palette 422 | strhpl r3,[r8,#0x8] ;@ Sprite palette opaque 423 | 424 | add r0,r0,#2 425 | add r5,r5,#2 426 | tst r5,#6 427 | bne bnwTxLoop 428 | add r0,r0,#0x18 429 | add r5,r5,#0x18 430 | cmp r5,#0x200 431 | bmi bnwTxLoop2 432 | 433 | ldmfd sp!,{r4-r8,lr} 434 | bx lr 435 | 436 | ;@---------------------------------------------------------------------------- 437 | shutDownLCD: 438 | .type shutDownLCD STT_FUNC 439 | ;@---------------------------------------------------------------------------- 440 | ldr r0,=gMachine 441 | ldrb r0,[r0] 442 | cmp r0,#HW_WONDERSWANCOLOR 443 | beq doColorShutDown 444 | cmp r0,#HW_SWANCRYSTAL 445 | bxne lr 446 | stmfd sp!,{lr} 447 | ldr r0,=gGammaValue 448 | ldrb r0,[r0] 449 | ldr r1,=gContrastValue 450 | ldrb r1,[r1] 451 | ldr r2,whiteOut 452 | add r3,r2,#1 453 | cmp r3,#0xFF 454 | movcs r3,#0xFF 455 | str r3,whiteOut 456 | stmfd sp!,{r0-r2} 457 | bl paletteInit 458 | ldmfd sp!,{r0-r2} 459 | bl monoPalInit 460 | bl paletteTxAll ;@ Make new palette visible 461 | ldmfd sp!,{lr} 462 | bx lr 463 | 464 | doColorShutDown: 465 | stmfd sp!,{lr} 466 | ldr r0,=gGammaValue 467 | ldrb r0,[r0] 468 | ldr r1,=gContrastValue 469 | ldrb r1,[r1] 470 | ldr r2,whiteOut 471 | sub r3,r2,#2 472 | cmp r3,#-0xFF 473 | movmi r3,#-0xFF 474 | str r3,whiteOut 475 | cmp r2,#-0xD0 476 | movpl r2,#0 477 | bl monoPalInit 478 | bl setupEmuBorderPalette ;@ Make new palette visible 479 | ldmfd sp!,{lr} 480 | bx lr 481 | 482 | ;@---------------------------------------------------------------------------- 483 | updateLCDRefresh: 484 | .type updateLCDRefresh STT_FUNC 485 | ;@---------------------------------------------------------------------------- 486 | adr spxptr,sphinx0 487 | ldrb r0,[spxptr,#wsvTotalLines] 488 | b wsvRefW 489 | ;@---------------------------------------------------------------------------- 490 | setScreenRefresh: ;@ r0 in = WS scan line count. 491 | .type setScreenRefresh STT_FUNC 492 | ;@---------------------------------------------------------------------------- 493 | stmfd sp!,{r4-r6,spxptr,lr} 494 | mov r4,r0 495 | ldr r6,=12000 ;@ WS scanline frequency = 12kHz 496 | mov r0,r6,lsl#1 497 | mov r1,r4 498 | swi 0x090000 ;@ Division r0/r1, r0=result, r1=remainder. 499 | movs r0,r0,lsr#1 500 | adc r0,r0,#0 501 | mov r5,r0 502 | bl setTargetFPS 503 | ldr r0,=emuSettings 504 | ldr r0,[r0] 505 | tst r0,#ALLOW_REFRESH_CHG 506 | moveq r0,#59 507 | subne r0,r5,#1 508 | ldr r1,=fpsNominal 509 | strb r0,[r1] 510 | 511 | ldr r0,=15734 ;@ DS scanline frequency = 15734.3Hz 512 | mul r0,r4,r0 ;@ DS scanline freq * WS scanlines 513 | mov r1,r6 ;@ / WS scanline freq = DS scanlines. 514 | swi 0x090000 ;@ Division r0/r1, r0=result, r1=remainder. 515 | ldr r1,=263 516 | sub r0,r1,r0 517 | cmp r0,#3 518 | movmi r0,#0 519 | str r0,lcdSkip 520 | 521 | ldmfd sp!,{r4-r6,spxptr,lr} 522 | bx lr 523 | 524 | ;@---------------------------------------------------------------------------- 525 | #ifdef GBA 526 | .section .iwram, "ax", %progbits ;@ For the GBA 527 | #endif 528 | ;@---------------------------------------------------------------------------- 529 | vblIrqHandler: 530 | .type vblIrqHandler STT_FUNC 531 | ;@---------------------------------------------------------------------------- 532 | stmfd sp!,{r4-r8,lr} 533 | bl calculateFPS 534 | 535 | mov r6,#REG_BASE 536 | strh r6,[r6,#REG_DMA0CNT_H] ;@ DMA0 stop 537 | strh r6,[r6,#REG_DMA3CNT_H] ;@ DMA3 stop 538 | 539 | add r0,r6,#REG_DMA0SAD 540 | ldr r1,dmaScroll ;@ Setup DMA buffer for scrolling: 541 | ldmia r1!,{r3-r4} ;@ Read 542 | add r2,r6,#REG_BG0HOFS ;@ DMA0 always goes here 543 | stmia r2,{r3-r4} ;@ Set 1st values manually, HBL is AFTER 1st line 544 | ldr r3,=0x96600002 ;@ hblank 32bit repeat incsrc inc_reloaddst, 2 words 545 | stmia r0,{r1-r3} ;@ DMA0 go 546 | 547 | add r0,r6,#REG_DMA3SAD 548 | ldr r1,dmaOamBuffer ;@ DMA3 src, OAM transfer: 549 | mov r2,#OAM ;@ DMA3 dst 550 | mov r3,#0x84000000 ;@ 32bit incsrc incdst 551 | orr r3,r3,#128*2 ;@ 128 sprites * 2 longwords 552 | stmia r0,{r1-r3} ;@ DMA3 go 553 | 554 | ldr r1,=EMUPALBUFF ;@ DMA3 src, Palette transfer: 555 | mov r2,#BG_PALETTE ;@ DMA3 dst 556 | mov r3,#0x84000000 ;@ 32bit incsrc incdst 557 | orr r3,r3,#0x100 ;@ 256 words (1024 bytes) 558 | stmia r0,{r1-r3} ;@ DMA3 go 559 | 560 | ldr r1,dmaWinInOut ;@ Setup DMA buffer for window stuff: 561 | ldmia r1!,{r3-r5} ;@ Read 562 | add r2,r6,#REG_WIN0H ;@ DMA3 dst 563 | stmia r2,{r3-r5} ;@ Set 1st values manually, HBL is AFTER 1st line 564 | ldr r3,=0x96600003 ;@ hblank 32bit repeat incsrc inc_reloaddst, 3 words 565 | stmia r0,{r1-r3} ;@ DMA3 go 566 | 567 | adr spxptr,sphinx0 568 | ldr r0,GFX_DISPCNT 569 | ldrb r2,gGfxMask 570 | bic r0,r0,r2,lsl#8 571 | strh r0,[r6,#REG_DISPCNT] 572 | 573 | ldr r0,=emuSettings 574 | ldr r0,[r0] 575 | tst r0,#ALLOW_REFRESH_CHG 576 | beq exit75Hz 577 | ldr r0,=pauseEmulation 578 | ldrb r0,[r0] 579 | cmp r0,#0 580 | bne exit75Hz 581 | ldr r0,lcdSkip 582 | cmp r0,#0 583 | beq exit75Hz 584 | hz75Start: 585 | hz75Loop: 586 | ldrh r1,[r6,#REG_VCOUNT] 587 | cmp r1,#202 588 | bmi hz75Loop 589 | add r1,r1,r0 ;@ Skip 55(?) scan lines for 75Hz. 590 | cmp r1,#260 591 | movpl r1,#260 592 | strh r1,[r6,#REG_VCOUNT] 593 | exit75Hz: 594 | 595 | ldrb r0,frameDone 596 | cmp r0,#0 597 | beq nothingNew 598 | // bl wsvConvertTiles 599 | mov r0,#BG_GFX 600 | bl wsvConvertTileMaps 601 | mov r0,#0 602 | strb r0,frameDone 603 | nothingNew: 604 | 605 | blx scanKeys 606 | ldmfd sp!,{r4-r8,pc} 607 | 608 | ;@---------------------------------------------------------------------------- 609 | copyWindowValues: ;@ r0 = destination 610 | ;@---------------------------------------------------------------------------- 611 | stmfd sp!,{r4-r9,lr} 612 | add r0,r0,#((SCREEN_HEIGHT-GAME_HEIGHT)/2)*12 ;@ 12 bytes per row 613 | ldr r9,=(((SCREEN_WIDTH-GAME_WIDTH)/2)<<24)+(((SCREEN_WIDTH+GAME_WIDTH)/2)<<16)+(((SCREEN_WIDTH-GAME_WIDTH)/2)<<8)+((SCREEN_WIDTH-GAME_WIDTH)/2 + 1) 614 | ldr lr,=(((SCREEN_HEIGHT-GAME_HEIGHT)/2)<<24)+(((SCREEN_HEIGHT+GAME_HEIGHT)/2)<<16)+(((SCREEN_HEIGHT-GAME_HEIGHT)/2)<<8)+((SCREEN_HEIGHT-GAME_HEIGHT)/2 + 1) 615 | ldr r1,[spxptr,#dispBuff] 616 | ldr r4,[spxptr,#windowBuff] 617 | ldr r2,=DISP_CTRL_LUT 618 | 619 | mov r3,#GAME_HEIGHT<<24 620 | setWindowLoop: 621 | ldr r8,[r4],#4 ;@ FG Win pos/size 622 | and r6,r8,#0x000000FF ;@ H start 623 | and r7,r8,#0x00FF0000 ;@ H end 624 | cmp r6,#GAME_WIDTH 625 | movcs r6,#GAME_WIDTH 626 | cmp r7,#(GAME_WIDTH-1)<<16 627 | movcs r7,#(GAME_WIDTH-1)<<16 628 | cmp r7,r6,lsl#16 629 | orr r6,r6,r7,lsl#8 630 | movcc r6,#-1 631 | add r6,r9,r6,ror#24 632 | 633 | rsb r5,r3,#GAME_HEIGHT<<24 634 | and r7,r8,#0x0000FF00 ;@ V start, V end top byte 635 | cmp r7,r5,lsr#16 636 | movcc r7,r5,lsr#16 637 | cmp r7,#GAME_HEIGHT<<8 638 | movcs r7,#GAME_HEIGHT<<8 639 | cmp r8,#(GAME_HEIGHT-1)<<24 640 | movcs r8,#(GAME_HEIGHT-1)<<24 641 | cmp r8,r5 642 | subcc r8,r5,#1<<24 643 | orr r7,r7,r8,lsr#24 644 | add r7,r7,lr 645 | 646 | ldrb r8,[r1],#1 647 | ldr r8,[r2,r8,lsl#2] 648 | stmia r0!,{r6-r8} 649 | subs r3,r3,#1<<24 650 | bne setWindowLoop 651 | 652 | ldmfd sp!,{r4-r9,pc} 653 | 654 | ;@---------------------------------------------------------------------------- 655 | gfxRefresh: ;@ Called from C when changing scaling. 656 | .type gfxRefresh STT_FUNC 657 | ;@---------------------------------------------------------------------------- 658 | adr spxptr,sphinx0 659 | ;@---------------------------------------------------------------------------- 660 | gfxEndFrame: ;@ Called just after screen end (line 144) (r0-r3 safe to use) 661 | ;@---------------------------------------------------------------------------- 662 | stmfd sp!,{r4-r8,lr} 663 | 664 | ldr r0,tmpScroll ;@ Destination 665 | bl wsvCopyScrollValues 666 | ldr r0,tmpWinInOut ;@ Destination 667 | bl copyWindowValues 668 | ldr r0,tmpOamBuffer ;@ Destination 669 | bl wsvConvertSprites 670 | bl paletteTxAll 671 | ;@-------------------------- 672 | 673 | adr r0,tmpOamBuffer 674 | ldmia r0,{r1-r8,lr} 675 | stmia r0!,{r7,r8,lr} 676 | stmia r0,{r1-r6} 677 | 678 | mov r0,#1 679 | strb r0,frameDone 680 | bl updateSlowIO ;@ Battery level/RTC/Alarm 681 | 682 | ldr r1,=fpsValue 683 | ldr r0,[r1] 684 | add r0,r0,#1 685 | str r0,[r1] 686 | 687 | ldr r1,frameTotal 688 | add r1,r1,#1 689 | str r1,frameTotal 690 | 691 | ldmfd sp!,{r4-r8,lr} 692 | bx lr 693 | 694 | ;@---------------------------------------------------------------------------- 695 | frameTotal: .long 0 ;@ Let Gui.c see frame count for savestates 696 | 697 | tmpOamBuffer: .long OAM_BUFFER1 698 | tmpScroll: .long SCROLLBUFF1 699 | tmpWinInOut: .long WININOUTBUFF1 700 | dmaOamBuffer: .long OAM_BUFFER2 701 | dmaScroll: .long SCROLLBUFF2 702 | dmaWinInOut: .long WININOUTBUFF2 703 | xtrOamBuffer: .long OAM_BUFFER3 704 | xtrScroll: .long SCROLLBUFF3 705 | xtrWinInOut: .long WININOUTBUFF3 706 | 707 | 708 | gFlicker: .byte 1 709 | .space 2 710 | gTwitch: .byte 0 711 | 712 | gGfxMask: .byte 0 713 | frameDone: .byte 0 714 | .byte 0,0 715 | ;@---------------------------------------------------------------------------- 716 | wsVideoReset0: ;@ r0=ram+LUTs, r1=machine, r2=IrqFunc 717 | ;@---------------------------------------------------------------------------- 718 | adr spxptr,sphinx0 719 | b wsVideoReset 720 | ;@---------------------------------------------------------------------------- 721 | v30ReadPort: 722 | .type v30ReadPort STT_FUNC 723 | ;@---------------------------------------------------------------------------- 724 | adr spxptr,sphinx0 725 | b wsvRead 726 | ;@---------------------------------------------------------------------------- 727 | v30ReadPort16: 728 | .type v30ReadPort16 STT_FUNC 729 | ;@---------------------------------------------------------------------------- 730 | adr spxptr,sphinx0 731 | b wsvRead16 732 | ;@---------------------------------------------------------------------------- 733 | v30WritePort: 734 | .type v30WritePort STT_FUNC 735 | ;@---------------------------------------------------------------------------- 736 | adr spxptr,sphinx0 737 | b wsvWrite 738 | ;@---------------------------------------------------------------------------- 739 | v30WritePort16: 740 | .type v30WritePort16 STT_FUNC 741 | ;@---------------------------------------------------------------------------- 742 | adr spxptr,sphinx0 743 | b wsvWrite16 744 | ;@---------------------------------------------------------------------------- 745 | pushVolumeButton: 746 | .type pushVolumeButton STT_FUNC 747 | ;@---------------------------------------------------------------------------- 748 | adr spxptr,sphinx0 749 | b wsvPushVolumeButton 750 | ;@---------------------------------------------------------------------------- 751 | getHeadphones: ;@ out r0 = on/off 752 | .type getHeadphones STT_FUNC 753 | ;@---------------------------------------------------------------------------- 754 | adr spxptr,sphinx0 755 | b wsvGetHeadphones 756 | ;@---------------------------------------------------------------------------- 757 | setHeadphones: ;@ r0 = on/off 758 | .type setHeadphones STT_FUNC 759 | ;@---------------------------------------------------------------------------- 760 | adr spxptr,sphinx0 761 | b wsvSetHeadphones 762 | ;@---------------------------------------------------------------------------- 763 | setLowBattery: ;@ r0 = on/off 764 | .type setLowBattery STT_FUNC 765 | ;@---------------------------------------------------------------------------- 766 | adr spxptr,sphinx0 767 | b wsvSetLowBattery 768 | ;@---------------------------------------------------------------------------- 769 | setSerialByteIn: 770 | .type setSerialByteIn STT_FUNC 771 | ;@---------------------------------------------------------------------------- 772 | adr spxptr,sphinx0 773 | b wsvSetSerialByteIn 774 | ;@---------------------------------------------------------------------------- 775 | setInterruptExternal: ;@ r0=irq state 776 | ;@---------------------------------------------------------------------------- 777 | adr spxptr,sphinx0 778 | b wsvSetInterruptExternal 779 | ;@---------------------------------------------------------------------------- 780 | getInterruptVector: 781 | ;@---------------------------------------------------------------------------- 782 | adr spxptr,sphinx0 783 | b wsvGetInterruptVector 784 | ;@---------------------------------------------------------------------------- 785 | setBusStatus: 786 | ;@---------------------------------------------------------------------------- 787 | adr spxptr,sphinx0 788 | b wsvHandleHalt 789 | ;@---------------------------------------------------------------------------- 790 | setPowerOff: 791 | .type setPowerOff STT_FUNC 792 | ;@---------------------------------------------------------------------------- 793 | adr spxptr,sphinx0 794 | b wsvSetPowerOff 795 | sphinx0: 796 | .space sphinxSize 797 | ;@---------------------------------------------------------------------------- 798 | 799 | gfxState: 800 | .long 0 801 | whiteOut: 802 | .long 0 803 | .long 0,0 804 | lcdSkip: 805 | .long 0 806 | 807 | GFX_DISPCNT: 808 | .long 0 809 | GFX_BG0CNT: 810 | .short 0 811 | GFX_BG1CNT: 812 | .short 0 813 | 814 | #ifdef GBA 815 | .section .sbss ;@ For the GBA 816 | #else 817 | .section .bss 818 | #endif 819 | .align 2 820 | OAM_BUFFER1: 821 | .space 0x400 822 | OAM_BUFFER2: 823 | .space 0x400 824 | OAM_BUFFER3: 825 | .space 0x400 826 | SCROLLBUFF1: 827 | .space SCREEN_HEIGHT*8 ;@ Scrollbuffer. 828 | SCROLLBUFF2: 829 | .space SCREEN_HEIGHT*8 ;@ Scrollbuffer. 830 | SCROLLBUFF3: 831 | .space SCREEN_HEIGHT*8 ;@ Scrollbuffer. 832 | WININOUTBUFF1: 833 | .space SCREEN_HEIGHT*12 ;@ Scrollbuffer. 834 | WININOUTBUFF2: 835 | .space SCREEN_HEIGHT*12 ;@ Scrollbuffer. 836 | WININOUTBUFF3: 837 | .space SCREEN_HEIGHT*12 ;@ Scrollbuffer. 838 | DISP_CTRL_LUT: 839 | .space 64*4 ;@ Convert from WS DispCtrl to NDS/GBA WinCtrl 840 | MAPPED_RGB: 841 | .space 0x2000 ;@ 4096*2 842 | MAPPED_BNW: 843 | .space 0x20 844 | EMUPALBUFF: 845 | .space 0x400 846 | 847 | ;@---------------------------------------------------------------------------- 848 | .end 849 | #endif // #ifdef __arm__ 850 | -------------------------------------------------------------------------------- /source/Gui.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Gui.h" 4 | #include "Shared/EmuMenu.h" 5 | #include "Shared/EmuSettings.h" 6 | #include "Main.h" 7 | #include "FileHandling.h" 8 | #include "WonderSwan.h" 9 | #include "WonderWitch.h" 10 | #include "Cart.h" 11 | #include "Gfx.h" 12 | #include "io.h" 13 | #include "cpu.h" 14 | #include "WSCart/WSCart.h" 15 | #include "ARMV30MZ/Version.h" 16 | #include "Sphinx/Version.h" 17 | #include "WSBottom.h" 18 | #include "WSCBottom.h" 19 | #include "SCBottom.h" 20 | 21 | #define EMUVERSION "V0.7.2 2025-05-25" 22 | 23 | void hacksInit(void); 24 | 25 | static void nullUIWS(int key); 26 | static void nullUIWSC(int key); 27 | 28 | static void gammaChange(void); 29 | static void paletteChange(void); 30 | static const char *getPaletteText(void); 31 | static void machineSet(void); 32 | static const char *getMachineText(void); 33 | static void headphonesSet(void); 34 | static const char *getHeadphonesText(void); 35 | static void speedHackSet(void); 36 | static const char *getSpeedHackText(void); 37 | static void refreshChgSet(void); 38 | static const char *getRefreshChgText(void); 39 | static void borderSet(void); 40 | static const char *getBorderText(void); 41 | static void joyMappingSet(void); 42 | static const char *getJoyMappingText(void); 43 | static void swapABSet(void); 44 | static const char *getSwapABText(void); 45 | static void contrastSet(void); 46 | static const char *getContrastText(void); 47 | static void fgrLayerSet(void); 48 | static const char *getFgrLayerText(void); 49 | static void bgrLayerSet(void); 50 | static const char *getBgrLayerText(void); 51 | static void sprLayerSet(void); 52 | static const char *getSprLayerText(void); 53 | static void winLayerSet(void); 54 | static const char *getWinLayerText(void); 55 | static void languageSet(void); 56 | 57 | static void ui11(void); 58 | static void ui12(void); 59 | static void ui13(void); 60 | static void updateGameId(char *buffer); 61 | static void updateCartInfo(char *buffer); 62 | static void updateMapperInfo(char *buffer); 63 | 64 | const MItem dummyItems[] = { 65 | {"", uiDummy} 66 | }; 67 | const MItem fileItems[] = { 68 | {"Load Game", selectGame}, 69 | {"Load State", loadState}, 70 | {"Save State", saveState}, 71 | {"Load NVRAM", loadNVRAM}, 72 | {"Save NVRAM", saveNVRAM}, 73 | {"Load Patch", selectIPS}, 74 | {"Save Settings", saveSettings}, 75 | {"Eject Game", ejectGame}, 76 | {"Reset Console", resetGame}, 77 | {"Quit Emulator", ui9}, 78 | }; 79 | const MItem optionItems[] = { 80 | {"Controller", ui4}, 81 | {"Display", ui5}, 82 | {"Machine", ui6}, 83 | {"Settings", ui7}, 84 | {"WonderWitch", ui11}, 85 | {"BootFriend", ui12}, 86 | {"Debug", ui8}, 87 | }; 88 | const MItem ctrlItems[] = { 89 | {"B Autofire:", autoBSet, getAutoBText}, 90 | {"A Autofire:", autoASet, getAutoAText}, 91 | {"Swap A-B: ", swapABSet, getSwapABText}, 92 | {"Alternate map:", joyMappingSet, getJoyMappingText}, 93 | }; 94 | const MItem displayItems[] = { 95 | {"Gamma:", gammaChange, getGammaText}, 96 | {"Contrast:", contrastSet, getContrastText}, 97 | {"B&W Palette:", paletteChange, getPaletteText}, 98 | {"Border:", borderSet, getBorderText}, 99 | }; 100 | const MItem machineItems[] = { 101 | {"Machine:", machineSet, getMachineText}, 102 | {"Select WS Bios", selectBnWBios}, 103 | {"Select WS Color Bios", selectColorBios}, 104 | {"Select WS Crystal Bios", selectCrystalBios}, 105 | {"Import Internal EEPROM", selectEEPROM}, 106 | {"Clear Internal EEPROM", clearIntEeproms}, 107 | {"Headphones:", headphonesSet, getHeadphonesText}, 108 | {"Cpu Speed Hacks:", speedHackSet, getSpeedHackText}, 109 | //{"Language:", languageSet}, 110 | }; 111 | const MItem setItems[] = { 112 | {"Speed:", speedSet, getSpeedText}, 113 | {"Allow Refresh Change:", refreshChgSet, getRefreshChgText}, 114 | {"Autoload State:", autoStateSet, getAutoStateText}, 115 | {"Autoload NVRAM:", autoNVRAMSet, getAutoNVRAMText}, 116 | {"Autosave Settings:", autoSettingsSet, getAutoSettingsText}, 117 | {"Autopause Game:", autoPauseGameSet, getAutoPauseGameText}, 118 | {"Powersave 2nd Screen:", powerSaveSet, getPowerSaveText}, 119 | {"Emulator on Bottom:", screenSwapSet, getScreenSwapText}, 120 | {"Autosleep:", sleepSet, getSleepText}, 121 | }; 122 | const MItem debugItems[] = { 123 | {"Debug Output:", debugTextSet, getDebugText}, 124 | {"Disable Foreground:", fgrLayerSet, getFgrLayerText}, 125 | {"Disable Background:", bgrLayerSet, getBgrLayerText}, 126 | {"Disable Sprites:", sprLayerSet, getSprLayerText}, 127 | {"Disable Windows:", winLayerSet, getWinLayerText}, 128 | {"Step Frame", stepFrame}, 129 | }; 130 | const MItem quitItems[] = { 131 | {"Yes ", exitEmulator}, 132 | {"No ", backOutOfMenu}, 133 | }; 134 | const MItem wonderWitchItems[] = { 135 | {"Storage:", wwChangeStorage, wwGetStorageText}, 136 | {"Upload File", wwStartPut}, 137 | {"Dir", wwStartDir}, 138 | {"Execute", wwStartExec}, 139 | {"Delete", wwStartDelete}, 140 | {"Defrag", wwStartDefrag}, 141 | {"Download File", wwStartGet}, 142 | {"NewFS (Formatt)", ui13}, 143 | {"XMODEM Transmit", startWWXModemTransmit}, 144 | {"XMODEM Receive", startXModemReceive}, 145 | {"Reboot WW", wwStartReboot}, 146 | {"CD", wwStartCD}, 147 | {"Interact", wwStartInteract}, 148 | {"Stty", wwStartStty}, 149 | {"Hello", wwStartHello}, 150 | {"Speed", wwStartSpeed}, 151 | }; 152 | const MItem bootFriendItems[] = { 153 | {"XMODEM Transmit", startXModemTransmit}, 154 | {"XMODEM Receive", startXModemReceive}, 155 | }; 156 | const MItem formattItems[] = { 157 | {"Yes ", wwStartNewFS}, 158 | {"No ", backOutOfMenu}, 159 | }; 160 | 161 | const Menu menu0 = MENU_M("", uiNullNormal, dummyItems); 162 | Menu menu1 = MENU_M("", uiAuto, fileItems); 163 | const Menu menu2 = MENU_M("", uiAuto, optionItems); 164 | const Menu menu3 = MENU_M("", uiAbout, dummyItems); 165 | const Menu menu4 = MENU_M("Controller Settings", uiAuto, ctrlItems); 166 | const Menu menu5 = MENU_M("Display Settings", uiAuto, displayItems); 167 | const Menu menu6 = MENU_M("Machine Settings", uiAuto, machineItems); 168 | const Menu menu7 = MENU_M("Settings", uiAuto, setItems); 169 | const Menu menu8 = MENU_M("Debug", uiAuto, debugItems); 170 | const Menu menu9 = MENU_M("Quit Emulator?", uiAuto, quitItems); 171 | const Menu menu10 = MENU_M("", uiDummy, dummyItems); 172 | const Menu menu11 = MENU_M("WonderWitch", uiAuto, wonderWitchItems); 173 | const Menu menu12 = MENU_M("BootFriend", uiAuto, bootFriendItems); 174 | const Menu menu13 = MENU_M("Formatt Storage?", uiAuto, formattItems); 175 | 176 | const Menu *const menus[] = {&menu0, &menu1, &menu2, &menu3, &menu4, &menu5, &menu6, &menu7, &menu8, &menu9, &menu10, &menu11, &menu12, &menu13}; 177 | 178 | u8 gContrastValue = 3; 179 | u8 gBorderEnable = 1; 180 | u8 serialPos = 0; 181 | char serialOut[32]; 182 | 183 | static const char *const machTxt[] = {"Auto", "WonderSwan", "WonderSwan Color", "SwanCrystal", "Pocket Challenge V2"}; 184 | static const char *const palTxt[] = {"Classic", "Black & White", "Red", "Green", "Blue", "Green-Blue", "Blue-Green", "Puyo Puyo Tsu"}; 185 | static const char *const bordTxt[] = {"Black", "Frame", "BG Color", "None"}; 186 | //static const char *const langTxt[] = {"Japanese", "English"}; 187 | 188 | 189 | void setupGUI() { 190 | keysSetRepeat(25, 4); // Delay, repeat. 191 | menu1.itemCount = ARRSIZE(fileItems) - (enableExit?0:1); 192 | openMenu(); 193 | } 194 | 195 | /// This is called when going from emu to ui. 196 | void enterGUI() { 197 | if ((emuSettings & AUTOSAVE_SETTINGS) && updateSettingsFromWS()) { 198 | saveSettings(); 199 | settingsChanged = false; 200 | } 201 | } 202 | 203 | /// This is called going from ui to emu. 204 | void exitGUI() { 205 | } 206 | 207 | void quickSelectGame(void) { 208 | openMenu(); 209 | selectGame(); 210 | closeMenu(); 211 | } 212 | 213 | void uiNullNormal() { 214 | if (gMachine == HW_WONDERSWAN) { 215 | setupCompressedBackground(WSBottomTiles, WSBottomMap, 0); 216 | memcpy(BG_PALETTE_SUB+0x80, WSBottomPal, WSBottomPalLen); 217 | } 218 | else if (gMachine == HW_WONDERSWANCOLOR) { 219 | setupCompressedBackground(WSCBottomTiles, WSCBottomMap, 0); 220 | memcpy(BG_PALETTE_SUB+0x80, WSCBottomPal, WSCBottomPalLen); 221 | } 222 | else if (gMachine == HW_SWANCRYSTAL) { 223 | setupCompressedBackground(SCBottomTiles, SCBottomMap, 0); 224 | memcpy(BG_PALETTE_SUB+0x80, SCBottomPal, SCBottomPalLen); 225 | } 226 | uiNullDefault(); 227 | } 228 | 229 | void uiAbout() { 230 | char gameInfoString[32]; 231 | cls(1); 232 | drawTabs(); 233 | drawMenuText("B: WS B button", 4, 0); 234 | drawMenuText("A: WS A button", 5, 0); 235 | drawMenuText("Start: WS Start button", 6, 0); 236 | drawMenuText("Select: WS Sound button", 7, 0); 237 | drawMenuText("DPad: WS X1-X4", 8, 0); 238 | 239 | updateGameId(gameInfoString); 240 | drawMenuText(gameInfoString, 10, 0); 241 | 242 | updateCartInfo(gameInfoString); 243 | drawMenuText(gameInfoString, 11, 0); 244 | 245 | updateMapperInfo(gameInfoString); 246 | drawMenuText(gameInfoString, 12, 0); 247 | 248 | drawMenuText("NitroSwan " EMUVERSION, 21, 0); 249 | drawMenuText("Sphinx " SPHINXVERSION, 22, 0); 250 | drawMenuText("ARMV30MZ " ARMV30MZVERSION, 23, 0); 251 | } 252 | 253 | void ui11() { 254 | enterMenu(11); 255 | } 256 | void ui12() { 257 | enterMenu(12); 258 | } 259 | void ui13() { 260 | enterMenu(13); 261 | } 262 | 263 | void nullUINormal(int key) { 264 | switch (gMachine) { 265 | case HW_WONDERSWAN: 266 | nullUIWS(key); 267 | break; 268 | case HW_WONDERSWANCOLOR: 269 | nullUIWSC(key); 270 | break; 271 | case HW_SWANCRYSTAL: 272 | nullUIWSC(key); 273 | break; 274 | default: 275 | if (key & KEY_TOUCH) { 276 | openMenu(); 277 | } 278 | break; 279 | } 280 | } 281 | 282 | void nullUIDebug(int key) { 283 | if (key & KEY_TOUCH) { 284 | openMenu(); 285 | } 286 | } 287 | 288 | void ejectGame() { 289 | ejectCart(); 290 | } 291 | 292 | void resetGame() { 293 | checkMachine(); 294 | loadCart(); 295 | setupEmuBackground(); 296 | powerIsOn = true; 297 | } 298 | 299 | void updateGameId(char *buffer) { 300 | char catalog[8]; 301 | char2HexStr(catalog, gGameHeader->gameId); 302 | strlMerge(buffer, "Game Id, Revision #: 0x", catalog, 32); 303 | strlMerge(buffer, buffer, " 0x", 32); 304 | char2HexStr(catalog, gGameHeader->gameRev); 305 | strlMerge(buffer, buffer, catalog, 32); 306 | } 307 | 308 | void updateCartInfo(char *buffer) { 309 | char catalog[8]; 310 | char2HexStr(catalog, gGameHeader->romSize); 311 | strlMerge(buffer, "ROM Size, Save #: 0x", catalog, 32); 312 | strlMerge(buffer, buffer, " 0x", 32); 313 | char2HexStr(catalog, gGameHeader->nvramSize); 314 | strlMerge(buffer, buffer, catalog, 32); 315 | } 316 | 317 | void updateMapperInfo(char *buffer) { 318 | char catalog[8]; 319 | char2HexStr(catalog, gGameHeader->flags); 320 | strlMerge(buffer, "Flags, Mapper #: 0x", catalog, 32); 321 | strlMerge(buffer, buffer, " 0x", 32); 322 | char2HexStr(catalog, gGameHeader->mapper); 323 | strlMerge(buffer, buffer, catalog, 32); 324 | } 325 | 326 | //--------------------------------------------------------------------------------- 327 | void debugIO(u16 port, u8 val, const char *message) { 328 | char debugString[32]; 329 | 330 | strlcpy(debugString, message, sizeof(debugString)); 331 | short2HexStr(&debugString[strlen(debugString)], port); 332 | strlcat(debugString, " val:", sizeof(debugString)); 333 | char2HexStr(&debugString[strlen(debugString)], val); 334 | debugOutput(debugString); 335 | } 336 | //--------------------------------------------------------------------------------- 337 | void debugIOUnimplR(u16 port, u8 val) { 338 | debugIO(port, val, "Unimpl R port:"); 339 | } 340 | void debugIOUnimplW(u8 val, u16 port) { 341 | debugIO(port, val, "Unimpl W port:"); 342 | } 343 | void debugIOUnmappedR(u16 port, u8 val) { 344 | debugIO(port, val, "Unmapped R port:"); 345 | } 346 | void debugIOUnmappedW(u8 val, u16 port) { 347 | debugIO(port, val, "Unmapped W port:"); 348 | } 349 | void debugROMW(u8 val, u16 adr) { 350 | debugIO(adr, val, "Rom W:"); 351 | } 352 | void debugSerialOutW(u8 val) { 353 | if (val < 0x80) { 354 | serialOut[serialPos++] = val; 355 | if (serialPos >= 31 || val == 0 || val == 0xA || val == 0xD) { 356 | serialOut[serialPos] = 0; 357 | serialPos = 0; 358 | debugOutput(serialOut); 359 | } 360 | } 361 | } 362 | void debugDivideError() { 363 | debugOutput("Divide Error."); 364 | } 365 | void debugUndefinedInstruction() { 366 | debugOutput("Undefined Instruction."); 367 | } 368 | void debugCrashInstruction() { 369 | debugOutput("CPU Crash! (0xF1)"); 370 | } 371 | 372 | //--------------------------------------------------------------------------------- 373 | void nullUIWS(int keyHit) { 374 | if (EMUinput & KEY_TOUCH) { 375 | touchPosition myTouch; 376 | touchRead(&myTouch); 377 | int xpos = (myTouch.px>>2); 378 | int ypos = (myTouch.py>>2); 379 | if ( ypos > 8 ) { 380 | openMenu(); 381 | } 382 | else if (xpos > 20 && xpos < 29) { // Start button 383 | EMUinput |= KEY_START; 384 | } 385 | else if (keyHit & KEY_TOUCH) { 386 | if (xpos > 9 && xpos < 19) { // Sound button 387 | pushVolumeButton(); 388 | } 389 | } 390 | } 391 | } 392 | 393 | //--------------------------------------------------------------------------------- 394 | void nullUIWSC(int keyHit) { 395 | if (EMUinput & KEY_TOUCH) { 396 | touchPosition myTouch; 397 | touchRead(&myTouch); 398 | int xpos = (myTouch.px>>2); 399 | int ypos = (myTouch.py>>2); 400 | if ( ypos > 8 ) { 401 | openMenu(); 402 | } 403 | else if (xpos > 16 && xpos < 24) { // Start button 404 | EMUinput |= KEY_START; 405 | } 406 | else if (keyHit & KEY_TOUCH) { 407 | if (xpos > 6 && xpos < 14) { // Sound button 408 | pushVolumeButton(); 409 | } 410 | else if (xpos > 27 && xpos < 35) { // Power button 411 | if (powerIsOn) { 412 | setPowerOff(); 413 | gfxRefresh(); 414 | } 415 | else { 416 | resetGame(); 417 | } 418 | } 419 | } 420 | } 421 | } 422 | 423 | //--------------------------------------------------------------------------------- 424 | void nullUISC(int keyHit) { 425 | if (EMUinput & KEY_TOUCH) { 426 | touchPosition myTouch; 427 | touchRead(&myTouch); 428 | int xpos = (myTouch.px>>2); 429 | int ypos = (myTouch.py>>2); 430 | if ( ypos > 8 ) { 431 | openMenu(); 432 | } 433 | else if (xpos > 16 && xpos < 25) { // Start button 434 | EMUinput |= KEY_START; 435 | } 436 | else if (keyHit & KEY_TOUCH) { 437 | if (xpos > 6 && xpos < 14) { // Sound button 438 | pushVolumeButton(); 439 | } 440 | else if (xpos > 30 && xpos < 36) { // Power button 441 | if (powerIsOn) { 442 | setPowerOff(); 443 | gfxRefresh(); 444 | } 445 | else { 446 | resetGame(); 447 | } 448 | } 449 | } 450 | } 451 | } 452 | 453 | //--------------------------------------------------------------------------------- 454 | /// Swap A & B buttons 455 | void swapABSet() { 456 | joyCfg ^= 0x400; 457 | } 458 | const char *getSwapABText() { 459 | return autoTxt[(joyCfg>>10)&1]; 460 | } 461 | 462 | void gammaChange() { 463 | gammaSet(); 464 | paletteInit(gGammaValue, gContrastValue, 0); 465 | monoPalInit(gGammaValue, gContrastValue, 0); 466 | setupEmuBorderPalette(); 467 | setupMenuPalette(); 468 | } 469 | 470 | /// Change contrast 471 | void contrastSet() { 472 | gContrastValue++; 473 | if (gContrastValue > 4) gContrastValue = 0; 474 | paletteInit(gGammaValue, gContrastValue, 0); 475 | monoPalInit(gGammaValue, gContrastValue, 0); 476 | setupEmuBorderPalette(); 477 | settingsChanged = true; 478 | } 479 | const char *getContrastText() { 480 | return brighTxt[gContrastValue]; 481 | } 482 | 483 | /// Turn on/off rendering of foreground 484 | void fgrLayerSet() { 485 | gGfxMask ^= 0x02; 486 | } 487 | const char *getFgrLayerText() { 488 | return autoTxt[(gGfxMask>>1)&1]; 489 | } 490 | /// Turn on/off rendering of background 491 | void bgrLayerSet() { 492 | gGfxMask ^= 0x01; 493 | } 494 | const char *getBgrLayerText() { 495 | return autoTxt[gGfxMask&1]; 496 | } 497 | /// Turn on/off rendering of sprites 498 | void sprLayerSet() { 499 | gGfxMask ^= 0x10; 500 | } 501 | const char *getSprLayerText() { 502 | return autoTxt[(gGfxMask>>4)&1]; 503 | } 504 | /// Turn on/off windows 505 | void winLayerSet() { 506 | gGfxMask ^= 0x20; 507 | } 508 | const char *getWinLayerText() { 509 | return autoTxt[(gGfxMask>>5)&1]; 510 | } 511 | 512 | void paletteChange() { 513 | gPaletteBank++; 514 | if (gPaletteBank > 7) { 515 | gPaletteBank = 0; 516 | } 517 | monoPalInit(gGammaValue, gContrastValue, 0); 518 | setupEmuBorderPalette(); 519 | settingsChanged = true; 520 | } 521 | const char *getPaletteText() { 522 | return palTxt[gPaletteBank]; 523 | } 524 | 525 | void borderSet() { 526 | gBorderEnable ^= 0x01; 527 | setupEmuBorderPalette(); 528 | } 529 | const char *getBorderText() { 530 | return bordTxt[gBorderEnable]; 531 | } 532 | 533 | void languageSet() { 534 | gLang ^= 0x01; 535 | } 536 | 537 | void machineSet() { 538 | gMachineSet++; 539 | if (gMachineSet >= HW_SELECT_END) { 540 | gMachineSet = 0; 541 | } 542 | setupEmuBorderPalette(); 543 | } 544 | const char *getMachineText() { 545 | return machTxt[gMachineSet]; 546 | } 547 | 548 | void speedHackSet() { 549 | emuSettings ^= ALLOW_SPEED_HACKS; 550 | hacksInit(); 551 | } 552 | const char *getSpeedHackText() { 553 | return autoTxt[(emuSettings & ALLOW_SPEED_HACKS)>>17]; 554 | } 555 | 556 | void joyMappingSet() { 557 | joyMapping ^= 0x01; 558 | setJoyMapping(joyMapping); 559 | } 560 | const char *getJoyMappingText() { 561 | return autoTxt[joyMapping&1]; 562 | } 563 | 564 | void headphonesSet() { 565 | if (gMachine != HW_POCKETCHALLENGEV2) { 566 | emuSettings ^= ENABLE_HEADPHONES; 567 | setHeadphones(emuSettings & ENABLE_HEADPHONES); 568 | } 569 | } 570 | const char *getHeadphonesText() { 571 | return autoTxt[getHeadphones()]; 572 | } 573 | 574 | void refreshChgSet() { 575 | emuSettings ^= ALLOW_REFRESH_CHG; 576 | updateLCDRefresh(); 577 | } 578 | const char *getRefreshChgText() { 579 | return autoTxt[(emuSettings & ALLOW_REFRESH_CHG)>>19]; 580 | } 581 | -------------------------------------------------------------------------------- /source/Gui.h: -------------------------------------------------------------------------------- 1 | #ifndef GUI_HEADER 2 | #define GUI_HEADER 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #define ALLOW_SPEED_HACKS (1<<17) 9 | #define ENABLE_HEADPHONES (1<<18) 10 | #define ALLOW_REFRESH_CHG (1<<19) 11 | 12 | extern u8 gContrastValue; 13 | extern u8 gBorderEnable; 14 | 15 | void setupGUI(void); 16 | void enterGUI(void); 17 | void exitGUI(void); 18 | void quickSelectGame(void); 19 | void nullUINormal(int key); 20 | void nullUIDebug(int key); 21 | void resetGame(void); 22 | void ejectGame(void); 23 | 24 | void uiNullNormal(void); 25 | void uiAbout(void); 26 | 27 | void debugIOUnmappedR(u16 port, u8 val); 28 | void debugIOUnmappedW(u8 val, u16 port); 29 | void debugIOUnimplR(u16 port, u8 val); 30 | void debugIOUnimplW(u8 val, u16 port); 31 | void debugSerialOutW(u8 val); 32 | void debugDivideError(void); 33 | void debugUndefinedInstruction(void); 34 | void debugCrashInstruction(void); 35 | 36 | #ifdef __cplusplus 37 | } // extern "C" 38 | #endif 39 | 40 | #endif // GUI_HEADER 41 | -------------------------------------------------------------------------------- /source/InternalEEPROM.h: -------------------------------------------------------------------------------- 1 | // Bandai WonderSwan Internal EEPROM 2 | 3 | #ifndef INT_EEPROM_HEADER 4 | #define INT_EEPROM_HEADER 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | // Font order = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZhn+-?." h=heart, n=note. 11 | // Values are 0x00-0x2A 12 | // Starts at 0x60, size 0x20. 13 | typedef struct { 14 | u8 name[16]; 15 | u8 birthYear[2]; // BCD encoded BIG endian 16 | u8 birthMonth; // BCD encoded 17 | u8 birthDay; // BCD encoded 18 | u8 sex; // 0 = ?, 1 = male, 2 = female 19 | u8 bloodType; // 0 = ?, 1 = A, 2 = B, 3 = O, 4 = AB 20 | u8 publisher; // Copy of ROM header field Publisher ID from the previous boot 21 | u8 color; // Copy of ROM header field Color from the previous boot 22 | u8 gameId; // Copy of ROM header field Game ID from the previous boot 23 | u8 unknown[3]; // (Unknown) 24 | u8 gameChgCount; // Number of times the cartridge has been changed 25 | u8 nameChgCount; // Number of times the owner name has been changed 26 | u16 bootCount; // Number of times the system has booted 27 | } WSUserData; 28 | 29 | // Starts at 0x80 30 | typedef struct { 31 | u8 padding[3]; 32 | u8 consoleFlags; // Bit 0 & 1 = Volume, bit 6 = High Contrast (WSC), bit 7 = Custom Boot. 33 | u8 consoleNameColor; 34 | u8 padding2; // Must be 0 35 | u8 size; 36 | u8 startFrame; 37 | u8 endFrame; 38 | u8 spriteCount; 39 | u8 paletteFlags; 40 | u8 tilesCount; 41 | u16 paletteOffset; 42 | u16 tilesetOffset; 43 | // 0x90 44 | u16 tilemapOffset; 45 | u16 horizontalTilemapDestOffset; 46 | u16 verticalTilemapDestOffset; 47 | u8 tilemapWidth; 48 | u8 tilemapHeight; 49 | u32 splashCodePointer; 50 | u8 consoleNameHorizontalPosX; 51 | u8 consoleNameHorizontalPosY; 52 | u8 consoleNameVerticalPosX; 53 | u8 consoleNameVerticalPosY; 54 | // 0xA0 55 | u8 padding3[2]; 56 | u16 soundSampleOffset; 57 | u16 soundChannelDataOffset[5]; 58 | u8 crystalLCD70; // 0xD0 59 | u8 crystalLCD71; // 0x77 60 | // 0xB0 61 | u8 crystalLCD72; // 0xF7 62 | u8 crystalLCD73; // 0x06 63 | u8 crystalLCD74; // 0xE2 64 | u8 crystalLCD75; // 0x0A 65 | u8 crystalLCD76; // 0xEA 66 | u8 crystalLCD77; // 0xEE 67 | } WSBootSplash; 68 | 69 | typedef struct { 70 | u8 programData[0x60]; 71 | WSUserData userData; 72 | WSBootSplash splashData; 73 | u8 memBank1[]; 74 | } IntEEPROM; 75 | 76 | #ifdef __cplusplus 77 | } // extern "C" 78 | #endif 79 | 80 | #endif // INT_EEPROM_HEADER 81 | -------------------------------------------------------------------------------- /source/Main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "Main.h" 5 | #include "Shared/EmuMenu.h" 6 | #include "Shared/FileHelper.h" 7 | #include "Shared/AsmExtra.h" 8 | #include "Gui.h" 9 | #include "FileHandling.h" 10 | #include "EmuFont.h" 11 | #include "WonderSwan.h" 12 | #include "Cart.h" 13 | #include "cpu.h" 14 | #include "Gfx.h" 15 | #include "io.h" 16 | #include "Sound.h" 17 | #include "WSCart/WSCart.h" 18 | 19 | static void checkTimeOut(void); 20 | static void setupGraphics(void); 21 | static void setupStream(void); 22 | 23 | bool powerIsOn = false; 24 | bool gameInserted = false; 25 | static int sleepTimer = 60*60*5; // 5 min 26 | static bool vBlankOverflow = false; 27 | 28 | static mm_ds_system sys; 29 | static mm_stream myStream; 30 | 31 | uint16 *map0sub; 32 | uint16 *map1sub; 33 | 34 | static const u8 guiPalette[] = { 35 | 0x00,0x00,0xC0, 0x00,0x00,0x00, 0x81,0x81,0x81, 0x93,0x93,0x93, 0xA5,0xA5,0xA5, 0xB7,0xB7,0xB7, 0xC9,0xC9,0xC9, 0xDB,0xDB,0xDB, 36 | 0xED,0xED,0xED, 0xFF,0xFF,0xFF, 0x00,0x00,0xC0, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 37 | 0x00,0x00,0x00, 0x00,0x00,0x00, 0x50,0x78,0x78, 0x60,0x90,0x90, 0x78,0xB0,0xB0, 0x88,0xC8,0xC8, 0x90,0xE0,0xE0, 0xA0,0xF0,0xF0, 38 | 0xB8,0xF8,0xF8, 0xEF,0xFF,0xFF, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 39 | 0x00,0x00,0x00, 0x00,0x00,0x00, 0x21,0x21,0x21, 0x33,0x33,0x33, 0x45,0x45,0x45, 0x47,0x47,0x47, 0x59,0x59,0x59, 0x6B,0x6B,0x6B, 40 | 0x7D,0x7D,0x7D, 0x8F,0x8F,0x8F, 0x20,0x20,0xE0, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 41 | 0x00,0x00,0x00, 0x00,0x00,0x00, 0x81,0x81,0x81, 0x93,0x93,0x93, 0xA5,0xA5,0xA5, 0xB7,0xB7,0xB7, 0xC9,0xC9,0xC9, 0xDB,0xDB,0xDB, 42 | 0xED,0xED,0xED, 0xFF,0xFF,0xFF, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 43 | 0x00,0x00,0x00, 0x00,0x00,0x00, 0x70,0x70,0x20, 0x88,0x88,0x40, 0xA0,0xA0,0x60, 0xB8,0xB8,0x80, 0xD0,0xD0,0x90, 0xE8,0xE8,0xA0, 44 | 0xF7,0xF7,0xC0, 0xFF,0xFF,0xE0, 0x00,0x00,0x60, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00 45 | }; 46 | 47 | //--------------------------------------------------------------------------------- 48 | void myVblank(void) { 49 | //--------------------------------------------------------------------------------- 50 | vBlankOverflow = true; 51 | // DC_FlushRange(EMUPALBUFF, 0x400); 52 | vblIrqHandler(); 53 | } 54 | 55 | //--------------------------------------------------------------------------------- 56 | int main(int argc, char **argv) { 57 | //--------------------------------------------------------------------------------- 58 | if (argc > 1) { 59 | enableExit = true; 60 | } 61 | // Try to allocate 8MB on DSi 62 | allocatedRomMemSize = 0x800000 + 0x1000; 63 | allocatedRomMem = malloc(allocatedRomMemSize); 64 | if (allocatedRomMem == NULL) { 65 | // Try to allocate 2MB on DS 66 | allocatedRomMemSize = 0x200000 + 0x1000; 67 | allocatedRomMem = malloc(allocatedRomMemSize); 68 | } 69 | maxRomSize = allocatedRomMemSize; 70 | romSpacePtr = allocatedRomMem; 71 | setupGraphics(); 72 | 73 | setupStream(); 74 | irqSet(IRQ_VBLANK, myVblank); 75 | setupGUI(); 76 | getInput(); 77 | initSettings(); 78 | bool fsOk = initFileHelper(); 79 | loadSettings(); 80 | machineInit(); 81 | loadCart(); 82 | setupEmuBackground(); 83 | loadIntEeproms(); 84 | if (fsOk) { 85 | loadBnWBIOS(); 86 | loadColorBIOS(); 87 | loadCrystalBIOS(); 88 | if (argc > 1) { 89 | loadGame(argv[1]); 90 | setMuteSoundGUI(); 91 | } 92 | redrawUI(); 93 | } 94 | else { 95 | infoOutput("fatInitDefault() failure."); 96 | } 97 | 98 | while (1) { 99 | waitVBlank(); 100 | checkTimeOut(); 101 | guiRunLoop(); 102 | if (!pauseEmulation) { 103 | if (powerIsOn) { 104 | run(); 105 | } 106 | else { 107 | shutDownLCD(); 108 | } 109 | } 110 | } 111 | return 0; 112 | } 113 | 114 | //--------------------------------------------------------------------------------- 115 | void pausVBlank(int count) { 116 | //--------------------------------------------------------------------------------- 117 | while (--count) { 118 | waitVBlank(); 119 | } 120 | } 121 | 122 | //--------------------------------------------------------------------------------- 123 | void waitVBlank() { 124 | //--------------------------------------------------------------------------------- 125 | // Workaround for bug in Bios. 126 | if (!vBlankOverflow) { 127 | swiIntrWait(1, IRQ_VBLANK); 128 | } 129 | vBlankOverflow = false; 130 | } 131 | 132 | //--------------------------------------------------------------------------------- 133 | static void checkTimeOut() { 134 | //--------------------------------------------------------------------------------- 135 | if (EMUinput) { 136 | sleepTimer = sleepTime; 137 | } 138 | else { 139 | sleepTimer--; 140 | if (sleepTimer < 0) { 141 | sleepTimer = sleepTime; 142 | // systemSleep doesn't work as expected. 143 | //systemSleep(); 144 | } 145 | } 146 | } 147 | 148 | //--------------------------------------------------------------------------------- 149 | void setEmuSpeed(int speed) { 150 | //--------------------------------------------------------------------------------- 151 | if (speed == 0) { // Normal Speed 152 | waitMaskIn = 0x00; 153 | waitMaskOut = 0x00; 154 | } 155 | else if (speed == 1) { // Double speed 156 | waitMaskIn = 0x00; 157 | waitMaskOut = 0x01; 158 | } 159 | else if (speed == 2) { // Max speed (4x) 160 | waitMaskIn = 0x00; 161 | waitMaskOut = 0x03; 162 | } 163 | else if (speed == 3) { // 50% speed 164 | waitMaskIn = 0x01; 165 | waitMaskOut = 0x00; 166 | } 167 | } 168 | 169 | //--------------------------------------------------------------------------------- 170 | static void setupGraphics() { 171 | //--------------------------------------------------------------------------------- 172 | 173 | vramSetBankA(VRAM_A_MAIN_BG); 174 | vramSetBankB(VRAM_B_MAIN_BG_0x06020000); 175 | vramSetBankC(VRAM_C_MAIN_BG_0x06040000); 176 | vramSetBankD(VRAM_D_MAIN_BG_0x06060000); 177 | vramSetBankE(VRAM_E_MAIN_SPRITE); 178 | vramSetBankF(VRAM_F_LCD); 179 | vramSetBankG(VRAM_G_LCD); 180 | vramSetBankH(VRAM_H_SUB_BG); 181 | vramSetBankI(VRAM_I_SUB_SPRITE); 182 | 183 | // Set up the main display 184 | GFX_DISPCNT = MODE_0_2D 185 | | DISPLAY_BG0_ACTIVE 186 | | DISPLAY_BG1_ACTIVE 187 | | DISPLAY_BG2_ACTIVE 188 | | DISPLAY_SPR_ACTIVE 189 | | DISPLAY_WIN0_ON 190 | | DISPLAY_WIN1_ON 191 | | DISPLAY_BG_EXT_PALETTE 192 | ; 193 | videoSetMode(GFX_DISPCNT); 194 | GFX_BG0CNT = BG_32x64 | BG_MAP_BASE(0) | BG_COLOR_16 | BG_TILE_BASE(2) | BG_PRIORITY(2); 195 | GFX_BG1CNT = BG_32x64 | BG_MAP_BASE(2) | BG_COLOR_16 | BG_TILE_BASE(2) | BG_PRIORITY(1); 196 | REG_BG0CNT = GFX_BG0CNT; 197 | REG_BG1CNT = GFX_BG1CNT; 198 | // Background 2 for border 199 | REG_BG2CNT = BG_32x32 | BG_MAP_BASE(15) | BG_COLOR_256 | BG_TILE_BASE(1) | BG_PRIORITY(0); 200 | 201 | // Set up the sub display 202 | videoSetModeSub(MODE_0_2D 203 | | DISPLAY_BG0_ACTIVE 204 | | DISPLAY_BG1_ACTIVE 205 | ); 206 | // Set up two backgrounds for menu 207 | REG_BG0CNT_SUB = BG_32x32 | BG_MAP_BASE(0) | BG_COLOR_16 | BG_TILE_BASE(0) | BG_PRIORITY(0); 208 | REG_BG1CNT_SUB = BG_32x32 | BG_MAP_BASE(1) | BG_COLOR_16 | BG_TILE_BASE(0) | BG_PRIORITY(0); 209 | REG_BG1HOFS_SUB = 0; 210 | REG_BG1VOFS_SUB = 0; 211 | map0sub = BG_MAP_RAM_SUB(0); 212 | map1sub = BG_MAP_RAM_SUB(1); 213 | 214 | decompress(EmuFontTiles, BG_GFX_SUB+0x1200, LZ77Vram); 215 | setupMenuPalette(); 216 | } 217 | 218 | void setupMenuPalette() { 219 | convertPalette(BG_PALETTE_SUB, guiPalette, sizeof(guiPalette)/3, gGammaValue); 220 | } 221 | 222 | //--------------------------------------------------------------------------------- 223 | static void setupStream(void) { 224 | //--------------------------------------------------------------------------------- 225 | 226 | //---------------------------------------------------------------- 227 | // initialize maxmod without any soundbank (unusual setup) 228 | //---------------------------------------------------------------- 229 | sys.mod_count = 0; 230 | sys.samp_count = 0; 231 | sys.mem_bank = 0; 232 | sys.fifo_channel = FIFO_MAXMOD; 233 | mmInit( &sys ); 234 | 235 | //---------------------------------------------------------------- 236 | // open stream 237 | //---------------------------------------------------------------- 238 | myStream.sampling_rate = sample_rate; // sampling rate = 239 | myStream.buffer_length = buffer_size; // buffer length = 240 | myStream.callback = VblSound2; // set callback function 241 | myStream.format = MM_STREAM_16BIT_STEREO; // format = stereo 16-bit 242 | myStream.timer = MM_TIMER0; // use hardware timer 0 243 | myStream.manual = false; // use manual filling 244 | mmStreamOpen( &myStream ); 245 | 246 | //---------------------------------------------------------------- 247 | // when using 'automatic' filling, your callback will be triggered 248 | // every time half of the wave buffer is processed. 249 | // 250 | // so: 251 | // 25000 (rate) 252 | // ----- = ~21 Hz for a full pass, and ~42hz for half pass 253 | // 1200 (length) 254 | //---------------------------------------------------------------- 255 | // with 'manual' filling, you must call mmStreamUpdate 256 | // periodically (and often enough to avoid buffer underruns) 257 | //---------------------------------------------------------------- 258 | } 259 | -------------------------------------------------------------------------------- /source/Main.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_HEADER 2 | #define MAIN_HEADER 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | extern bool powerIsOn; 9 | extern bool gameInserted; 10 | extern uint16 *map0sub; 11 | 12 | void waitVBlank(void); 13 | 14 | /** 15 | * Waits the specified number of frames before returning. 16 | * @param count: Number of frames to wait. 17 | * @deprecated Don't use, solve it some other way. 18 | */ 19 | void pausVBlank(int count); 20 | 21 | void setEmuSpeed(int speed); 22 | void setupMenuPalette(void); 23 | 24 | #ifdef __cplusplus 25 | } // extern "C" 26 | #endif 27 | 28 | #endif // MAIN_HEADER 29 | -------------------------------------------------------------------------------- /source/Memory.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMORY_HEADER 2 | #define MEMORY_HEADER 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | u8 cpuReadMem20(u32 addr); 11 | u16 cpuReadMem20W(u32 addr); 12 | u16 dmaReadMem20W(u32 addr); 13 | void cpuWriteMem20(u32 addr, u8 value); 14 | void cpuWriteMem20W(u32 addr, u16 value); 15 | void dmaWriteMem20W(u32 addr, u16 value); 16 | 17 | #ifdef __cplusplus 18 | } // extern "C" 19 | #endif 20 | 21 | #endif // MEMORY_HEADER 22 | -------------------------------------------------------------------------------- /source/Memory.s: -------------------------------------------------------------------------------- 1 | #ifdef __arm__ 2 | 3 | #include "ARMV30MZ/ARMV30MZmac.h" 4 | #include "Sphinx/Sphinx.i" 5 | 6 | .global memoryInit 7 | .global empty_IO_R 8 | .global empty_IO_W 9 | .global rom_W 10 | 11 | .global cpuReadMem20 12 | .global cpuReadMem20W 13 | .global dmaReadMem20W 14 | .global v30ReadEA1 15 | .global v30ReadEA 16 | .global v30ReadStack 17 | .global v30ReadDsIx 18 | .global v30ReadSegOfs 19 | .global v30ReadEAWF7 20 | .global v30ReadEAW1 21 | .global v30ReadEAW 22 | .global v30ReadEAW_noAdd 23 | .global v30ReadSegOfsW 24 | 25 | .global cpuWriteMem20 26 | .global cpuWriteMem20W 27 | .global dmaWriteMem20W 28 | .global v30WriteEA 29 | .global v30WriteEsIy 30 | .global v30WriteSegOfs 31 | .global v30WriteEAW2 32 | .global v30WriteEAW 33 | .global v30PushW 34 | .global v30PushLastW 35 | .global v30WriteSegOfsW 36 | .global setBootRomOverlay 37 | .global setSRamArea 38 | .global setFlashRead 39 | .global bootRomSwitchB 40 | .global bootRomSwitchW 41 | 42 | .syntax unified 43 | .arm 44 | 45 | .section .text 46 | .align 2 47 | ;@---------------------------------------------------------------------------- 48 | memoryInit: 49 | ;@---------------------------------------------------------------------------- 50 | ldr r0,=flashReadMem20 51 | ldr r1,=cpuReadMem20+8 52 | sub r0,r0,r1 53 | mov r0,r0,lsl#6 54 | mov r0,r0,lsr#8 55 | orr r0,r0,#0xEA000000 ;@ Branch always 56 | str r0,flashCmdList+4 57 | bx lr 58 | ;@---------------------------------------------------------------------------- 59 | empty_IO_R: ;@ Read bad IO address (error) 60 | ;@---------------------------------------------------------------------------- 61 | mov r11,r11 ;@ No$GBA breakpoint 62 | mov r0,#0x10 63 | bx lr 64 | ;@---------------------------------------------------------------------------- 65 | empty_IO_W: ;@ Write bad IO address (error) 66 | ;@---------------------------------------------------------------------------- 67 | mov r11,r11 ;@ No$GBA breakpoint 68 | mov r0,#0x18 69 | bx lr 70 | ;@---------------------------------------------------------------------------- 71 | rom_W: ;@ Write ROM address (error) 72 | ;@---------------------------------------------------------------------------- 73 | mov r11,r11 ;@ No$GBA breakpoint 74 | stmfd sp!,{r12,lr} 75 | bl debugROMW 76 | ldmfd sp!,{r12,pc} 77 | mov r0,#0xB0 78 | bx lr 79 | ;@---------------------------------------------------------------------------- 80 | setBootRomOverlay: ;@ r0=arg0, 0=remove overlay, 1=WS, 2=WSC/SC 81 | ;@---------------------------------------------------------------------------- 82 | cmp r0,#3 83 | bxcs lr 84 | stmfd sp!,{r4} 85 | ldr r1,=bootRomSwitchB 86 | ldr r2,=bootRomSwitchW 87 | adr r3,commandList 88 | ldr r4,[r3,r0,lsl#2] 89 | str r4,[r1] 90 | str r4,[r2] 91 | cmp r0,#0 92 | addeq r3,r3,#4 93 | ldr r4,[r3,#3*4] 94 | str r4,[r1,#-2*4] 95 | ldr r4,[r3,#4*4] 96 | str r4,[r1,#-1*4] 97 | ldmfd sp!,{r4} 98 | commandList: 99 | bx lr 100 | subs r2,r2,#0xFF000 101 | subs r2,r2,#0xFE000 102 | 103 | mov r2,r0,lsr#12 104 | ldrb r0,[r1,r0,lsr#12]! 105 | bx lr 106 | ;@---------------------------------------------------------------------------- 107 | setSRamArea: ;@ r0=arg0, 0=SRAM, 1=ROM/Flash 108 | ;@---------------------------------------------------------------------------- 109 | cmp r0,#2 110 | ldrcc r1,=sram_WB 111 | ldrcc r2,=sram_WW 112 | adr r3,sramCmdList 113 | ldrcc r0,[r3,r0,lsl#2] 114 | strcc r0,[r1] 115 | strcc r0,[r2] 116 | bx lr 117 | sramCmdList: 118 | ldreq r2,[v30ptr,#v30MemTblInv-2*4] 119 | cmp r2,#0 120 | ;@---------------------------------------------------------------------------- 121 | setFlashRead: ;@ r0=arg0, 0=Normal, 1=Flash info 122 | ;@---------------------------------------------------------------------------- 123 | cmp r0,#2 124 | ldrcc r1,=cpuReadMem20 125 | // ldrcc r2,=cpuReadMem20W 126 | adr r3,flashCmdList 127 | ldrcc r0,[r3,r0,lsl#2] 128 | strcc r0,[r1] 129 | // strcc r0,[r2] 130 | bx lr 131 | flashCmdList: 132 | mvn r2,r0,lsr#28 133 | //This is "b always flashReadMem20" 134 | .long 0xEA000000 // + ((flashReadMem20 - cpuReadMem20)>>2) & 0xFFFFFF 135 | // .long 0xEA000000 + ((flashCmdList - setFlashRead)>>2) & 0xFFFFFF 136 | 137 | ;@---------------------------------------------------------------------------- 138 | 139 | #ifdef NDS 140 | .section .itcm ;@ For the NDS ARM9 141 | #elif GBA 142 | .section .iwram, "ax", %progbits ;@ For the GBA 143 | #endif 144 | .align 2 145 | 146 | ;@---------------------------------------------------------------------------- 147 | cpuReadWordUnaligned: ;@ Make sure cpuReadMem20 does not use r3 or r12! 148 | ;@---------------------------------------------------------------------------- 149 | eatCycles 1 150 | stmfd sp!,{lr} 151 | mov r3,r0 152 | bl cpuReadMem20 153 | mov r12,r0 154 | add r0,r3,#0x1000 155 | bl cpuReadMem20 156 | orr r0,r12,r0,lsl#8 157 | ldmfd sp!,{pc} 158 | 159 | ;@---------------------------------------------------------------------------- 160 | v30ReadEA1: ;@ In v30ofs=v30ptr+second byte of opcode. 161 | ;@---------------------------------------------------------------------------- 162 | eatCycles 1 163 | ;@---------------------------------------------------------------------------- 164 | v30ReadEA: ;@ In v30ofs=v30ptr+second byte of opcode. 165 | ;@---------------------------------------------------------------------------- 166 | adr r12,v30ReadSegOfs ;@ Return reg for EA 167 | ldr pc,[v30ofs,#v30EATable] 168 | ;@---------------------------------------------------------------------------- 169 | v30ReadDsIx: ;@ 170 | ;@---------------------------------------------------------------------------- 171 | ldrsb r4,[v30ptr,#v30DF] 172 | ldr v30ofs,[v30ptr,#v30RegIX] 173 | TestSegmentPrefix 174 | ldreq v30csr,[v30ptr,#v30SRegDS0] 175 | add r0,v30ofs,r4,lsl#16 176 | str r0,[v30ptr,#v30RegIX] 177 | ;@---------------------------------------------------------------------------- 178 | v30ReadSegOfs: ;@ In r7=segment in top 16 bits, r6=offset in top 16 bits. 179 | ;@---------------------------------------------------------------------------- 180 | add r0,v30csr,v30ofs,lsr#4 181 | ;@---------------------------------------------------------------------------- 182 | cpuReadMem20: ;@ In r0=address set in top 20 bits. Out r0=val, r1=phyAdr 183 | ;@ If this is updated, remember to also update V30EncodePC 184 | ;@---------------------------------------------------------------------------- 185 | mvn r2,r0,lsr#28 186 | ldr r1,[v30ptr,r2,lsl#2] 187 | mov r2,r0,lsr#12 188 | ldrb r0,[r1,r0,lsr#12]! 189 | bootRomSwitchB: 190 | subs r2,r2,#0xFE000 191 | bxcc lr 192 | ldr r1,=biosBase 193 | ldr r1,[r1] 194 | ldrb r0,[r1,r2]! 195 | bx lr 196 | 197 | ;@---------------------------------------------------------------------------- 198 | v30ReadEAWF7: ;@ In r0=second byte of opcode. 199 | ;@---------------------------------------------------------------------------- 200 | add v30ofs,v30ptr,r0,lsl#2 201 | ;@---------------------------------------------------------------------------- 202 | v30ReadEAW1: ;@ In v30ofs=v30ptr+second byte of opcode. 203 | ;@---------------------------------------------------------------------------- 204 | eatCycles 1 205 | adr r12,v30ReadSegOfsW ;@ Return reg for EA 206 | ldr pc,[v30ofs,#v30EATable] 207 | ;@---------------------------------------------------------------------------- 208 | v30ReadEAW: ;@ In r0=second byte of opcode. 209 | ;@---------------------------------------------------------------------------- 210 | add v30ofs,v30ptr,r0,lsl#2 211 | v30ReadEAW_noAdd: 212 | adr r12,v30ReadSegOfsW ;@ Return reg for EA 213 | ldr pc,[v30ofs,#v30EATable] 214 | ;@---------------------------------------------------------------------------- 215 | v30ReadStack: ;@ Read a word from the stack. 216 | ;@---------------------------------------------------------------------------- 217 | ldr v30ofs,[v30ptr,#v30RegSP] 218 | ldr v30csr,[v30ptr,#v30SRegSS] 219 | ;@---------------------------------------------------------------------------- 220 | v30ReadSegOfsW: ;@ In r7=segment in top 16 bits, r6=offset in top 16 bits. 221 | ;@---------------------------------------------------------------------------- 222 | add r0,v30csr,v30ofs,lsr#4 223 | ;@---------------------------------------------------------------------------- 224 | cpuReadMem20W: ;@ In r0=address set in top 20 bits. Out r0=val, r1=phyAdr 225 | ;@---------------------------------------------------------------------------- 226 | tst r0,#0x1000 227 | bne cpuReadWordUnaligned 228 | dmaReadMem20W: 229 | mvn r2,r0,lsr#28 230 | ldr r1,[v30ptr,r2,lsl#2] 231 | mov r2,r0,lsr#12 232 | ldrh r0,[r1,r2]! 233 | bootRomSwitchW: 234 | subs r2,r2,#0xFE000 235 | bxcc lr 236 | ldr r1,=biosBase 237 | ldr r1,[r1] 238 | ldrh r0,[r1,r2]! 239 | bx lr 240 | 241 | ;@---------------------------------------------------------------------------- 242 | cpuWriteWordUnaligned: ;@ Make sure cpuWriteMem20 does not change r0 or r1! 243 | ;@---------------------------------------------------------------------------- 244 | eatCycles 1 245 | stmfd sp!,{lr} 246 | bl cpuWriteMem20 247 | ldmfd sp!,{lr} 248 | add r0,r0,#0x1000 249 | mov r1,r1,lsr#8 250 | b cpuWriteMem20 251 | 252 | ;@---------------------------------------------------------------------------- 253 | v30WriteEA: ;@ In v30ofs=v30ptr+second byte of opcode. 254 | ;@---------------------------------------------------------------------------- 255 | adr r12,v30WriteSegOfs ;@ Return reg for EA 256 | ldr pc,[v30ofs,#v30EATable] 257 | ;@---------------------------------------------------------------------------- 258 | ;@v30WriteEsIy: ;@ 259 | ;@---------------------------------------------------------------------------- 260 | ;@ ldrsb r4,[v30ptr,#v30DF] 261 | ;@---------------------------------------------------------------------------- 262 | v30WriteEsIy: ;@ 263 | ;@---------------------------------------------------------------------------- 264 | GetIyOfsESegment 265 | add r2,v30ofs,r4,lsl#16 266 | str r2,[v30ptr,#v30RegIY] 267 | ;@---------------------------------------------------------------------------- 268 | v30WriteSegOfs: ;@ In r7=segment in top 16 bits, r6=offset in top 16 bits. 269 | ;@---------------------------------------------------------------------------- 270 | add r0,v30csr,v30ofs,lsr#4 271 | ;@---------------------------------------------------------------------------- 272 | cpuWriteMem20: ;@ r0=address set in top 20 bits, r1=value 273 | ;@---------------------------------------------------------------------------- 274 | movs r2,r0,lsr#28 275 | bne cart_WB 276 | ;@---------------------------------------------------------------------------- 277 | ram_WB: ;@ Write ram ($00000-$0FFFF) 278 | ;@---------------------------------------------------------------------------- 279 | ldr r2,[v30ptr,#v30MemTblInv-1*4] 280 | strb r1,[r2,r0,lsr#12] 281 | add r2,r2,#0x10000 ;@ Size of wsRAM, ptr to DIRTYTILES. 282 | strb r0,[r2,r0,lsr#17] 283 | bx lr 284 | ;@---------------------------------------------------------------------------- 285 | cart_WB: 286 | ;@---------------------------------------------------------------------------- 287 | cmp r2,#1 288 | ;@---------------------------------------------------------------------------- 289 | sram_WB: ;@ Write sram ($10000-$1FFFF) 290 | ;@---------------------------------------------------------------------------- 291 | ldreq r2,[v30ptr,#v30MemTblInv-2*4] 292 | strbeq r1,[r2,r0,lsr#12] 293 | bxeq lr 294 | b flashWriteMem20 295 | 296 | ;@---------------------------------------------------------------------------- 297 | v30WriteEAW2: ;@ In v30ofs=v30ptr+second byte of opcode. 298 | ;@---------------------------------------------------------------------------- 299 | eatCycles 2 300 | ;@---------------------------------------------------------------------------- 301 | v30WriteEAW: ;@ In v30ofs=v30ptr+second byte of opcode. 302 | ;@---------------------------------------------------------------------------- 303 | adr r12,v30WriteSegOfsW ;@ Return reg for EA 304 | ldr pc,[v30ofs,#v30EATable] 305 | ;@---------------------------------------------------------------------------- 306 | v30PushW: ;@ In r1=value. 307 | ;@---------------------------------------------------------------------------- 308 | ldr v30ofs,[v30ptr,#v30RegSP] 309 | ldr v30csr,[v30ptr,#v30SRegSS] 310 | ;@---------------------------------------------------------------------------- 311 | v30PushLastW: ;@ In r1=value. 312 | ;@---------------------------------------------------------------------------- 313 | sub v30ofs,v30ofs,#0x20000 314 | str v30ofs,[v30ptr,#v30RegSP] 315 | ;@---------------------------------------------------------------------------- 316 | v30WriteSegOfsW: ;@ In r7=segment in top 16 bits, r6=offset in top 16 bits. 317 | ;@---------------------------------------------------------------------------- 318 | add r0,v30csr,v30ofs,lsr#4 319 | ;@---------------------------------------------------------------------------- 320 | cpuWriteMem20W: ;@ r0=address set in top 20 bits, r1=value 321 | ;@---------------------------------------------------------------------------- 322 | tst r0,#0x1000 323 | bne cpuWriteWordUnaligned 324 | movs r2,r0,lsr#28 325 | bne cart_WW 326 | ;@---------------------------------------------------------------------------- 327 | ram_WW: ;@ Write ram ($00000-$0FFFF) 328 | dmaWriteMem20W: 329 | ;@---------------------------------------------------------------------------- 330 | ldr r2,[v30ptr,#v30MemTblInv-1*4] 331 | mov r3,r0,lsr#12 332 | strh r1,[r2,r3] 333 | add r3,r2,#0x10000 ;@ Size of wsRAM, ptr to DIRTYTILES. 334 | strb r0,[r3,r0,lsr#17] 335 | bx lr 336 | ;@---------------------------------------------------------------------------- 337 | cart_WW: 338 | ;@---------------------------------------------------------------------------- 339 | cmp r2,#1 340 | ;@---------------------------------------------------------------------------- 341 | sram_WW: ;@ Write sram ($10000-$1FFFF) 342 | ;@---------------------------------------------------------------------------- 343 | ldreq r2,[v30ptr,#v30MemTblInv-2*4] 344 | moveq r0,r0,lsr#12 345 | strheq r1,[r2,r0] 346 | subeq v30cyc,v30cyc,#1*CYCLE 347 | bxeq lr 348 | b flashWriteMem20W 349 | 350 | ;@---------------------------------------------------------------------------- 351 | .end 352 | #endif // #ifdef __arm__ 353 | -------------------------------------------------------------------------------- /source/Sound.h: -------------------------------------------------------------------------------- 1 | #ifndef SOUND_HEADER 2 | #define SOUND_HEADER 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | #define sample_rate 24000 11 | #define buffer_size (640) 12 | 13 | void soundInit(void); 14 | void setMuteSoundGUI(void); 15 | void setMuteSoundChip(void); 16 | mm_word VblSound2(mm_word length, mm_addr dest, mm_stream_formats format); 17 | 18 | #ifdef __cplusplus 19 | } // extern "C" 20 | #endif 21 | 22 | #endif // SOUND_HEADER 23 | -------------------------------------------------------------------------------- /source/Sound.s: -------------------------------------------------------------------------------- 1 | #ifdef __arm__ 2 | 3 | #include "Sphinx/Sphinx.i" 4 | 5 | .extern pauseEmulation 6 | 7 | .global soundInit 8 | .global soundReset 9 | .global VblSound2 10 | .global setMuteSoundGUI 11 | .global setMuteSoundChip 12 | .global soundUpdate 13 | .global mix8Vol 14 | 15 | #define WRITE_BUFFER_SIZE (0x800) 16 | #define SHIFTVAL (21) 17 | 18 | .syntax unified 19 | .arm 20 | 21 | .section .text 22 | .align 2 23 | ;@---------------------------------------------------------------------------- 24 | soundInit: 25 | .type soundInit STT_FUNC 26 | ;@---------------------------------------------------------------------------- 27 | // stmfd sp!,{lr} 28 | 29 | // ldmfd sp!,{lr} 30 | // bx lr 31 | 32 | ;@---------------------------------------------------------------------------- 33 | soundReset: 34 | ;@---------------------------------------------------------------------------- 35 | stmfd sp!,{lr} 36 | mov r0,#WRITE_BUFFER_SIZE/2 37 | str r0,pcmWritePtr 38 | mov r0,#0 39 | str r0,pcmReadPtr 40 | strb r0,muteSoundChip 41 | ldr spxptr,=sphinx0 42 | bl wsAudioReset ;@ sound 43 | mov r0,#WRITE_BUFFER_SIZE 44 | ldr r1,=WAVBUFFER 45 | bl silenceMix 46 | ldmfd sp!,{lr} 47 | bx lr 48 | 49 | ;@---------------------------------------------------------------------------- 50 | setMuteSoundGUI: 51 | .type setMuteSoundGUI STT_FUNC 52 | ;@---------------------------------------------------------------------------- 53 | ldr r1,=pauseEmulation ;@ Output silence when emulation paused. 54 | ldrb r0,[r1] 55 | strb r0,muteSoundGUI 56 | bx lr 57 | ;@---------------------------------------------------------------------------- 58 | setMuteSoundChip: 59 | .type setMuteSoundChip STT_FUNC 60 | ;@---------------------------------------------------------------------------- 61 | mov r0,#1 62 | strb r0,muteSoundChip 63 | bx lr 64 | ;@---------------------------------------------------------------------------- 65 | VblSound2: ;@ r0=length, r1=pointer 66 | ;@---------------------------------------------------------------------------- 67 | ldr r2,muteSound 68 | cmp r2,#0 69 | bne silenceMix 70 | 71 | stmfd sp!,{r0,r4,r5,lr} 72 | ldr spxptr,=sphinx0 73 | ldr r4,pcmReadPtr 74 | add r5,r4,r0 75 | str r5,pcmReadPtr 76 | 77 | bl soundCopyBuff 78 | 79 | ldr r0,pcmWritePtr 80 | sub r0,r5,r0 81 | add r0,r0,#WRITE_BUFFER_SIZE/2 82 | ldr r2,neededExtra 83 | rsb r2,r2,r2,lsl#3 ;@ mul 7 84 | add r0,r2,r0 85 | mov r0,r0,asr#3 86 | str r0,neededExtra 87 | bic r0,r0,#1 // 7 88 | // mov r0,r0,asr#1 89 | // bics r0,r0,#0xFF 90 | str r0,[spxptr,#missingSamplesCnt] 91 | // blne debugIOUnmappedR 92 | 93 | ldmfd sp!,{r0,r4,r5,lr} 94 | bx lr 95 | ;@---------------------------------------------------------------------------- 96 | soundCopyBuff: 97 | ;@---------------------------------------------------------------------------- 98 | ldr r3,=WAVBUFFER 99 | mov r4,r4,lsl#SHIFTVAL 100 | ldrb r2,[spxptr,#wsvSoundOutput] 101 | tst r2,#0x80 ;@ Headphones? 102 | beq soundCopyBuffInt 103 | sndCopyLoop: 104 | subs r0,r0,#1 105 | ldrpl r2,[r3,r4,lsr#SHIFTVAL-2] 106 | strpl r2,[r1],#4 107 | add r4,r4,#1< 2 | 3 | #include "WonderSwan.h" 4 | #include "Shared/EmuMenu.h" 5 | #include "WSBorder.h" 6 | #include "WSCBorder.h" 7 | #include "SCBorder.h" 8 | #include "PCV2Border.h" 9 | #include "Gui.h" 10 | #include "Cart.h" 11 | #include "Gfx.h" 12 | #include "ARMV30MZ/ARMV30MZ.h" 13 | #include "WSCart/WSCart.h" 14 | 15 | 16 | int packState(void *statePtr) { 17 | int size = 0; 18 | memcpy(statePtr+size, wsRAM, sizeof(wsRAM)); 19 | size += sizeof(wsRAM); 20 | size += sphinxSaveState(statePtr+size, &sphinx0); 21 | size += V30SaveState(statePtr+size, &V30OpTable); 22 | memcpy(statePtr+size, cartSRAM, sizeof(cartSRAM)); 23 | size += sizeof(cartSRAM); 24 | size += wsEepromSaveState(statePtr+size, &cartEeprom); 25 | return size; 26 | } 27 | 28 | void unpackState(const void *statePtr) { 29 | int size = 0; 30 | memcpy(wsRAM, statePtr+size, sizeof(wsRAM)); 31 | size += sizeof(wsRAM); 32 | size += sphinxLoadState(&sphinx0, statePtr+size); 33 | size += V30LoadState(&V30OpTable, statePtr+size); 34 | memcpy(cartSRAM, statePtr+size, sizeof(cartSRAM)); 35 | size += sizeof(cartSRAM); 36 | size += wsEepromLoadState(&cartEeprom, statePtr+size); 37 | } 38 | 39 | int getStateSize() { 40 | int size = 0; 41 | size += sizeof(wsRAM); 42 | size += sphinxGetStateSize(); 43 | size += V30GetStateSize(); 44 | size += sizeof(cartSRAM); 45 | size += wsEepromGetStateSize(); 46 | return size; 47 | } 48 | 49 | static void setupBorderPalette(const unsigned short *palette, int len) { 50 | vramSetBankF(VRAM_F_LCD); 51 | if (gBorderEnable == 0) { 52 | memset(VRAM_F, 0, len); 53 | } 54 | else { 55 | memcpy(VRAM_F, palette, len); 56 | } 57 | // Copy Icon colors. 58 | memcpy(VRAM_F + 0xF0, MAPPED_BNW, sizeof(MAPPED_BNW)); 59 | vramSetBankF(VRAM_F_BG_EXT_PALETTE_SLOT23); 60 | paletteTxAll(); // Make new palette visible 61 | } 62 | 63 | static void setupBorderTiles(const void *tiles) { 64 | decompress(tiles, BG_TILE_RAM(1), LZ77Vram); 65 | } 66 | 67 | static void setupBorderMap(const void *map) { 68 | decompress(map, BG_MAP_RAM(15), LZ77Vram); 69 | } 70 | 71 | void setupWSBorderPalette() { 72 | setupBorderPalette(WSBorderPal, WSBorderPalLen); 73 | } 74 | 75 | void setupWSCBorderPalette() { 76 | setupBorderPalette(WSCBorderPal, WSCBorderPalLen); 77 | } 78 | 79 | void setupSCBorderPalette() { 80 | setupBorderPalette(SCBorderPal, SCBorderPalLen); 81 | } 82 | 83 | void setupPCV2BorderPalette() { 84 | setupBorderPalette(PCV2BorderPal, PCV2BorderPalLen); 85 | } 86 | 87 | static void fillScreenMap(u16 val) { 88 | u16 *dest = BG_MAP_RAM(15) + (3 * 32) + 2; 89 | u16 fill = BG_MAP_RAM(15)[val]; 90 | for (int i=0;i<18;i++) { 91 | for (int j=0;j<28;j++) { 92 | dest[i*32+j] = fill; 93 | } 94 | } 95 | } 96 | 97 | static void fillScreenRow(u16 val, int row) { 98 | u16 *dest = BG_MAP_RAM(15) + (3 * 32) + 2; 99 | u16 fill = BG_MAP_RAM(15)[val]; 100 | for (int j=0;j<28;j++) { 101 | dest[row*32+j] = fill; 102 | } 103 | } 104 | 105 | void setupEmuBackground() { 106 | if (gMachine == HW_WONDERSWANCOLOR) { 107 | setupBorderTiles(WSCBorderTiles); 108 | setupBorderMap(WSCBorderMap); 109 | setupWSCBorderPalette(); 110 | } 111 | else if (gMachine == HW_SWANCRYSTAL) { 112 | setupBorderTiles(SCBorderTiles); 113 | setupBorderMap(SCBorderMap); 114 | setupSCBorderPalette(); 115 | } 116 | else if (gMachine == HW_WONDERSWAN) { 117 | setupBorderTiles(WSBorderTiles); 118 | setupBorderMap(WSBorderMap); 119 | setupWSBorderPalette(); 120 | } 121 | else { 122 | setupBorderTiles(PCV2BorderTiles); 123 | setupBorderMap(PCV2BorderMap); 124 | setupPCV2BorderPalette(); 125 | } 126 | } 127 | 128 | void setupEmuBgrShutDown() { 129 | if (gMachine == HW_WONDERSWANCOLOR) { 130 | setupBorderMap(WSCBorderMap); 131 | fillScreenMap(0x31E); 132 | fillScreenRow(0x31F, 5); 133 | } 134 | else if (gMachine == HW_SWANCRYSTAL) { 135 | setupBorderMap(SCBorderMap); 136 | fillScreenMap(0x31F); 137 | } 138 | else if (gMachine == HW_WONDERSWAN) { 139 | setupBorderMap(WSBorderMap); 140 | fillScreenMap(0x31F); 141 | } 142 | else { 143 | setupBorderMap(PCV2BorderMap); 144 | fillScreenMap(0x31F); 145 | } 146 | } 147 | 148 | void setupEmuBorderPalette() { 149 | if (gMachine == HW_WONDERSWANCOLOR) { 150 | setupWSCBorderPalette(); 151 | } 152 | else if (gMachine == HW_SWANCRYSTAL) { 153 | setupSCBorderPalette(); 154 | } 155 | else if (gMachine == HW_WONDERSWAN) { 156 | setupWSBorderPalette(); 157 | } 158 | else { 159 | setupPCV2BorderPalette(); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /source/WonderSwan.h: -------------------------------------------------------------------------------- 1 | #ifndef WONDERSWAN_HEADER 2 | #define WONDERSWAN_HEADER 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | /// This runs all save state functions for each chip. 9 | int packState(void *statePtr); 10 | 11 | /// This runs all load state functions for each chip. 12 | void unpackState(const void *statePtr); 13 | 14 | /// Gets the total state size in bytes. 15 | int getStateSize(void); 16 | 17 | /// Setup WonderSwan background for emulator screen. 18 | void setupEmuBackground(void); 19 | 20 | /// Setup WonderSwan background for shut down. 21 | void setupEmuBgrShutdown(void); 22 | 23 | void setupEmuBorderPalette(void); 24 | 25 | #ifdef __cplusplus 26 | } // extern "C" 27 | #endif 28 | 29 | #endif // WONDERSWAN_HEADER 30 | -------------------------------------------------------------------------------- /source/WonderWitch.c: -------------------------------------------------------------------------------- 1 | // 2 | // WonderWitch.c 3 | // WonderWitch utility functions. 4 | // 5 | // Created by Fredrik Ahlström on 2024-09-01. 6 | // This is public domain, no copyright no warranty. 7 | // Write it, cut it, paste it, save it 8 | // Load it, check it, quick – rewrite it 9 | // Name it, read it, tune it, print it 10 | // Scan it, send it, fax – rename it 11 | // 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "Shared/EmuMenu.h" 19 | #include "WonderWitch.h" 20 | #include "Gui.h" 21 | #include "Gfx.h" 22 | 23 | FILE *file = NULL; 24 | 25 | int counter = 0; 26 | int prevPage = 0; 27 | int fileSize = 0; 28 | int pageCount = 0; 29 | Mode mode = standby; 30 | WWCmd wwCmd = wwCmdNone; 31 | Storage storage = rom0; 32 | bool serialInEmpty = false; 33 | bool isWWTransfer = true; 34 | u8 checksum = 0; 35 | u8 outIdx = 0; 36 | u8 inIdx = 0; 37 | //static const char lineFeed[3] = {CR, NL, 0x0}; 38 | static const char lineFeed[2] = {CR, 0x0}; 39 | static const char *const storText[] = {"/rom0", "/ram0", "/kern"}; 40 | const char *currentFileName; 41 | const char *selectedFile; 42 | char outBuf[PAGE_SIZE]; 43 | u8 buffer[PAGE_SIZE]; 44 | char txtBuf[0x400]; 45 | char wwDir[FILEPATH_MAX_LENGTH]; 46 | 47 | void wwChangeStorage() { 48 | storage += 1; 49 | if (storage > kern) { 50 | storage = rom0; 51 | } 52 | } 53 | 54 | const char *wwGetStorageText() { 55 | return storText[storage]; 56 | } 57 | 58 | static bool sendByte(u8 value) { 59 | if (serialInEmpty) { 60 | serialInEmpty = false; 61 | setSerialByteIn(value); 62 | return true; 63 | } 64 | return false; 65 | } 66 | 67 | /// Sends a NACK to the WS serial port. 68 | static void sendNack(void) { 69 | sendByte(NAK); 70 | } 71 | 72 | /// Sends an ACK to the WS serial port. 73 | static void sendAck(void) { 74 | sendByte(ACK); 75 | } 76 | 77 | static void sendCommand() { 78 | u8 val = outBuf[outIdx]; 79 | if (val == 0) { 80 | mode = wwReceiveCommand; 81 | outIdx = 0; 82 | outBuf[outIdx] = 0; 83 | return; 84 | } 85 | if (sendByte(val)) { 86 | outIdx += 1; 87 | } 88 | } 89 | 90 | static void doXModemTransmit(bool wwTransfer) { 91 | isWWTransfer = wwTransfer; 92 | mode = xmodemTransmitHold; 93 | counter = 0; 94 | prevPage = 0; 95 | } 96 | 97 | static void receiveBuffer(u8 val) { 98 | buffer[inIdx] = val; 99 | inIdx += 1; 100 | if (val == NL) { 101 | buffer[inIdx] = 0; 102 | if (strstr((char *)buffer, "125 STARTING")) { 103 | if (wwCmd == wwCmdGet) { 104 | startXModemReceive(); 105 | } 106 | else { 107 | // Receive data 108 | mode = wwReceiveText; 109 | inIdx = 0; 110 | } 111 | } 112 | else if (strstr((char *)buffer, "350 FURTHER INFO")) { 113 | if (wwCmd == wwCmdPut) { 114 | doXModemTransmit(true); 115 | } 116 | } 117 | else if (strstr((char *)buffer, "200 OK")) { 118 | if (wwCmd == wwCmdDir) { 119 | selectedFile = browseDirectory(); 120 | cls(0); 121 | } 122 | inIdx = 0; 123 | buffer[inIdx] = 0; 124 | wwCmd = wwCmdNone; 125 | } 126 | else if (strstr((char *)buffer, "426 TRANSFER ABORTED") 127 | || strstr((char *)buffer, "450 NOT AVAILABLE") 128 | || strstr((char *)buffer, "451 LOCAL ERR") 129 | || strstr((char *)buffer, "452 FS FULL") 130 | || strstr((char *)buffer, "501 SYNTAX ERR") 131 | || strstr((char *)buffer, "502 NOT IMPLEMENTED") 132 | || strstr((char *)buffer, "504 NOT IMPLEMENTED") 133 | || strstr((char *)buffer, "550 NOT AVAILABLE") 134 | || strstr((char *)buffer, "553 NOT AVAILABLE")) { 135 | inIdx = 0; 136 | buffer[inIdx] = 0; 137 | wwCmd = wwCmdNone; 138 | } 139 | } 140 | } 141 | 142 | static void receiveText(u8 val) { 143 | txtBuf[inIdx] = val; 144 | inIdx += 1; 145 | if (val == NL) { 146 | txtBuf[inIdx-1] = 0; 147 | if (txtBuf[inIdx-2] == CR) { 148 | txtBuf[inIdx-2] = 0; 149 | } 150 | if (!strcmp(txtBuf, ".")) { 151 | mode = wwReceiveCommand; 152 | } 153 | else { 154 | if (wwCmd == wwCmdDir) { 155 | if (!strnstr(txtBuf, "total ", 6)){ 156 | inIdx = 16; 157 | while (txtBuf[inIdx] == 0x20) { 158 | inIdx -= 1; 159 | } 160 | txtBuf[inIdx+1] = 0; 161 | browseAddFilename(txtBuf); 162 | } 163 | } 164 | } 165 | inIdx = 0; 166 | } 167 | } 168 | 169 | static void endRxTx(void) { 170 | mode = standby; 171 | fclose(file); 172 | file = NULL; 173 | } 174 | 175 | static bool selectFileToTransmit(void) { 176 | bool result = false; 177 | char oldDir[FILEPATH_MAX_LENGTH]; 178 | strlcpy(oldDir, currentDir, sizeof(oldDir)); 179 | strlcpy(currentDir, wwDir, sizeof(currentDir)); 180 | // Was ".fx.fr.il.bin" 181 | const char *fileName = browseForFileType("*"); 182 | if (fileName != NULL) { 183 | strlcpy(wwDir, currentDir, sizeof(wwDir)); 184 | if (file != NULL) { 185 | fclose(file); 186 | } 187 | if ( (file = fopen(fileName, "r")) ) { 188 | currentFileName = fileName; 189 | fseek(file, 0, SEEK_END); 190 | fileSize = ftell(file); 191 | if (fileSize <= 0x60000) { 192 | pageCount = (fileSize + (PAGE_SIZE-1)) / PAGE_SIZE; 193 | fseek(file, 0, SEEK_SET); 194 | result = true; 195 | } 196 | else { 197 | endRxTx(); 198 | infoOutput("File too large!"); 199 | } 200 | } 201 | else { 202 | infoOutput("Couldn't open file:"); 203 | infoOutput(fileName); 204 | } 205 | } 206 | strlcpy(currentDir, oldDir, sizeof(oldDir)); 207 | cls(0); 208 | return result; 209 | } 210 | 211 | static void startCommand(const char *str, WWCmd cmd) { 212 | mode = wwSendCommand; 213 | wwCmd = cmd; 214 | outIdx = 0; 215 | strlMerge(outBuf, " ", str, PAGE_SIZE); 216 | strlcat(outBuf, lineFeed, PAGE_SIZE); 217 | sendCommand(); 218 | debugOutput(outBuf); 219 | } 220 | 221 | static void startCmdStor(const char *str, WWCmd cmd) { 222 | char comStr[32]; 223 | strlMerge(comStr, str, " ", sizeof(comStr)); 224 | strlcat(comStr, &wwGetStorageText()[1], sizeof(comStr)); 225 | startCommand(comStr, cmd); 226 | } 227 | 228 | static void startCmdPath(const char *str, WWCmd cmd) { 229 | char comStr[32]; 230 | strlMerge(comStr, str, " ", sizeof(comStr)); 231 | strlcat(comStr, wwGetStorageText(), sizeof(comStr)); 232 | startCommand(comStr, cmd); 233 | } 234 | 235 | static void startCmdFile(const char *str, const char *filename, WWCmd cmd) { 236 | char comStr[32]; 237 | strlMerge(comStr, str, " ", sizeof(comStr)); 238 | strlcat(comStr, filename, sizeof(comStr)); 239 | startCommand(comStr, cmd); 240 | } 241 | 242 | static void handleXModemTransmit(bool wwTransfer) { 243 | u8 value = 0; 244 | if (counter == 0) { 245 | checksum = 0; 246 | // End of Transfer? 247 | if (prevPage == pageCount) { 248 | value = EOT; 249 | endRxTx(); 250 | } 251 | else { 252 | // Start of Heading 253 | value = SOH; 254 | fread(outBuf, 1, PAGE_SIZE, file); 255 | if (prevPage == 0) { 256 | FxFile *fxFile = (FxFile *)outBuf; 257 | // WonderWitch file? "#!ws" 258 | if (wwTransfer && fxFile->magic != 0x73772123) { 259 | memset(fxFile, 0x00, sizeof(FxFile)); 260 | fxFile->magic = 0x73772123; 261 | memset(fxFile->ffFill, 0xFF, sizeof(fxFile->ffFill)); 262 | fxFile->binarySize = fileSize; 263 | fxFile->blockCount = pageCount+1; 264 | fxFile->flags = 4; //Read flag. 265 | struct stat attrib; 266 | struct tm ts; 267 | stat(currentFileName, &attrib); 268 | gmtime_r( &attrib.st_mtime, &ts); 269 | fxFile->modificationTime = ((ts.tm_year - 100)<<25) + ((ts.tm_mon + 1)<<21) + (ts.tm_mday<<16) + (ts.tm_hour<<11) + (ts.tm_min<<5) + (ts.tm_sec>>1); 270 | truncateFileName(fxFile->fileName, currentFileName, sizeof(fxFile->fileName)); 271 | strlcpy(fxFile->description, currentFileName, sizeof(fxFile->description)); 272 | fseek(file, 0, SEEK_SET); 273 | } 274 | } 275 | } 276 | } 277 | else if (counter == 1) { 278 | prevPage += 1; 279 | value = prevPage; 280 | } 281 | else if (counter == 2) { 282 | value = ~prevPage; 283 | } 284 | else if (counter < 131) { 285 | value = outBuf[counter-3]; 286 | checksum += value; 287 | } 288 | else if (counter == 131) { 289 | value = checksum; 290 | counter = -1; 291 | } 292 | sendByte( value ); 293 | counter += 1; 294 | } 295 | 296 | static void handleXModemReceive(u8 value) { 297 | bool ok = true; 298 | if (counter == 0) { 299 | checksum = 0; 300 | // End of Transfer? 301 | if (value == EOT) { 302 | sendAck(); 303 | endRxTx(); 304 | } 305 | // Cancel? 306 | else if (value == CAN) { 307 | infoOutput("Receive canceled."); 308 | sendAck(); 309 | endRxTx(); 310 | } 311 | // Not Start of Heading? 312 | else if (value != SOH) { 313 | return; 314 | } 315 | } 316 | else if (counter == 1) { 317 | prevPage += 1; 318 | if (value != (prevPage & 0xFF)) { 319 | ok = false; 320 | } 321 | } 322 | else if (counter == 2) { 323 | if (value != ((~prevPage) & 0xFF)) { 324 | ok = false; 325 | } 326 | } 327 | else if (counter < 131) { 328 | buffer[counter-3] = value; 329 | checksum += value; 330 | } 331 | else if (counter == 131) { 332 | if (value != checksum) { 333 | ok = false; 334 | } 335 | else { 336 | if (prevPage == 1) { 337 | FxFile *fxFile = (FxFile *)buffer; 338 | char fileName[0x20]; 339 | // "#!ws" 340 | if (fxFile->magic == 0x73772123) { 341 | fileSize = fxFile->binarySize + PAGE_SIZE; 342 | strlcpy(fileName, fxFile->fileName, sizeof(fileName)); 343 | // Executeable? 344 | if (fxFile->flags & 0x01) { 345 | strlcat(fileName, ".fx", sizeof(fileName)); 346 | } 347 | // Library? 348 | else if (fxFile->flags & 0x20) { 349 | strlcat(fileName, ".il", sizeof(fileName)); 350 | } 351 | else if (strrchr(fileName, '.') == NULL) { 352 | strlcat(fileName, ".fr", sizeof(fileName)); 353 | } 354 | } 355 | else { 356 | fileSize = 0x60000; 357 | strlcpy(fileName, "ReceiveData.bin", sizeof(fileName)); 358 | } 359 | file = fopen(fileName, "w"); 360 | } 361 | int chunkSize = (fileSize < PAGE_SIZE) ? fileSize : PAGE_SIZE; 362 | fileSize -= chunkSize; 363 | fwrite(buffer, 1, chunkSize, file); 364 | sendAck(); 365 | } 366 | counter = -1; 367 | } 368 | counter += 1; 369 | if (!ok) { 370 | infoOutput("Receive resend."); 371 | counter = 0; 372 | prevPage -= 1; 373 | sendNack(); 374 | } 375 | } 376 | 377 | void handleSerialInEmpty() { 378 | serialInEmpty = true; 379 | if (mode == xmodemTransmit) { 380 | handleXModemTransmit(isWWTransfer); 381 | } 382 | else if (mode == wwSendCommand) { 383 | sendCommand(); 384 | } 385 | } 386 | 387 | void handleSerialReceive(u8 value) { 388 | if (mode == xmodemReceive) { 389 | handleXModemReceive(value); 390 | } 391 | else if (mode == xmodemTransmitHold) { 392 | if (value == NAK) { 393 | mode = xmodemTransmit; 394 | handleXModemTransmit(isWWTransfer); 395 | } 396 | } 397 | else if (mode == xmodemTransmit) { 398 | if (value == NAK) { 399 | infoOutput("Transmit resend."); 400 | counter = 0; 401 | prevPage -= 1; 402 | fseek(file, prevPage*PAGE_SIZE, SEEK_SET); 403 | } 404 | else if (value == CAN) { 405 | infoOutput("Transmit canceled."); 406 | sendAck(); 407 | endRxTx(); 408 | } 409 | } 410 | else if (mode == wwReceiveCommand || mode == wwSendCommand) { 411 | receiveBuffer(value); 412 | } 413 | else if (mode == wwReceiveText) { 414 | receiveText(value); 415 | } 416 | // else if (mode == debugSerial) { 417 | debugSerialOutW(value); 418 | // } 419 | } 420 | 421 | void wwStartStty() { 422 | startCommand("stty", wwCmdStty); 423 | } 424 | 425 | void wwStartInteract() { 426 | startCommand("", wwCmdInt); 427 | } 428 | 429 | void wwStartHello() { 430 | startCommand("HELO", wwCmdHello); 431 | } 432 | 433 | void wwStartPut() { 434 | if (selectFileToTransmit()) { 435 | // startCmdPath("put", wwCmdPut); 436 | startCommand("put", wwCmdPut); 437 | } 438 | } 439 | 440 | void wwStartGet() { 441 | startCmdFile("get", selectedFile, wwCmdGet); 442 | } 443 | 444 | void wwStartDelete() { 445 | startCmdFile("delete", selectedFile, wwCmdDelete); 446 | } 447 | 448 | void wwStartExec() { 449 | startCmdFile("exec", selectedFile, wwCmdExec); 450 | } 451 | 452 | void wwStartReboot() { 453 | startCommand("reboot", wwCmdReboot); 454 | } 455 | 456 | void wwStartLs() { 457 | initBrowse(wwGetStorageText()); 458 | startCmdPath("ls", wwCmdDir); 459 | } 460 | 461 | void wwStartDir() { 462 | initBrowse(wwGetStorageText()); 463 | startCmdPath("dir", wwCmdDir); 464 | } 465 | 466 | void wwStartDF() { 467 | startCmdStor("df", wwCmdDf); 468 | } 469 | 470 | void wwStartNewFS() { 471 | startCmdStor("newfs", wwCmdNewFS); 472 | } 473 | 474 | void wwStartDefrag() { 475 | startCmdStor("defrag", wwCmdDefrag); 476 | } 477 | 478 | void wwStartRename() { 479 | startCommand("rename", wwCmdRename); 480 | } 481 | 482 | void wwStartSpeed() { 483 | startCommand("speed", wwCmdSpeed); 484 | } 485 | 486 | void wwStartDate() { 487 | startCommand("date", wwCmdDate); 488 | } 489 | 490 | void wwStartCopy() { 491 | startCommand("copy", wwCmdCopy); 492 | } 493 | 494 | void wwStartMove() { 495 | startCommand("move", wwCmdMove); 496 | } 497 | 498 | void wwStartSetInfo() { 499 | startCmdFile("setinfo", selectedFile, wwCmdSetInfo); 500 | } 501 | 502 | void wwStartChMod() { 503 | startCmdFile("chmod", selectedFile, wwCmdChMod); 504 | } 505 | 506 | void wwStartCD() { 507 | startCmdPath("cd", wwCmdCD); 508 | } 509 | 510 | void wwStartPwd() { 511 | startCommand("pwd", wwCmdPwd); 512 | } 513 | 514 | void startXModemReceive() { 515 | if (file != NULL) { 516 | fclose(file); 517 | } 518 | mode = xmodemReceive; 519 | counter = 0; 520 | prevPage = 0; 521 | sendNack(); 522 | } 523 | 524 | void startWWXModemTransmit() { 525 | if (selectFileToTransmit()) { 526 | doXModemTransmit(true); 527 | } 528 | } 529 | 530 | void startXModemTransmit() { 531 | if (selectFileToTransmit()) { 532 | doXModemTransmit(false); 533 | } 534 | } 535 | -------------------------------------------------------------------------------- /source/WonderWitch.h: -------------------------------------------------------------------------------- 1 | // 2 | // WonderWitch.h 3 | // WonderWitch utility functions. 4 | // 5 | // Created by Fredrik Ahlström on 2024-09-01. 6 | // This is public domain, no copyright no warranty. 7 | // Write it, cut it, paste it, save it 8 | // Load it, check it, quick – rewrite it 9 | // Name it, read it, tune it, print it 10 | // Scan it, send it, fax – rename it 11 | // 12 | 13 | #include "Shared/FileHelper.h" 14 | 15 | #ifndef WONDERWITCH_HEADER 16 | #define WONDERWITCH_HEADER 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | // SOH, Page,^Page 22 | // XMODEM Start: 0x01, 0x01, 0xFE 23 | 24 | #define PAGE_SIZE (0x80) 25 | 26 | /// Start of Heading 27 | #define SOH 0x01 28 | /// Start of Text, used instead of SOH for 1k blocks. 29 | #define STX 0x02 30 | // End of Transfer 31 | #define EOT 0x04 32 | /// Acknowledged 33 | #define ACK 0x06 34 | /// New Line 35 | #define NL 0x0A 36 | /// Caridge Return 37 | #define CR 0x0D 38 | /// Not Acknowledged 39 | #define NAK 0x15 40 | /// Cancel 41 | #define CAN 0x18 42 | /// Used instead of NAK for CRC-16 instead of byte checksum. 43 | #define C_CHR 0x43 44 | 45 | typedef enum { 46 | rom0 = 0, 47 | ram0, 48 | kern, 49 | } Storage; 50 | 51 | typedef enum { 52 | standby = 0, 53 | wwSendCommand, 54 | wwReceiveCommand, 55 | wwReceiveText, 56 | xmodemTransmitHold, 57 | xmodemTransmit, 58 | xmodemReceive, 59 | debugSerial, 60 | } Mode; 61 | 62 | typedef enum { 63 | wwCmdNone = 0, 64 | wwCmdStty, 65 | wwCmdInt, 66 | wwCmdHello, 67 | wwCmdPut, 68 | wwCmdSend, 69 | wwCmdGet, 70 | wwCmdDelete, 71 | wwCmdExec, 72 | wwCmdReboot, 73 | wwCmdDir, 74 | wwCmdDf, 75 | wwCmdNewFS, 76 | wwCmdDefrag, 77 | wwCmdRename, 78 | wwCmdSpeed, 79 | wwCmdDate, 80 | wwCmdCopy, 81 | wwCmdMove, 82 | wwCmdSetInfo, 83 | wwCmdChMod, 84 | wwCmdCD, 85 | wwCmdPwd, 86 | } WWCmd; 87 | 88 | typedef struct { 89 | u8 startOfHeading; 90 | u8 page; 91 | u8 invPage; 92 | u8 payload[PAGE_SIZE]; 93 | u8 checkSum; 94 | } XModemBlock; 95 | 96 | typedef struct { 97 | /// "#!ws" 98 | //char magic[4]; 99 | u32 magic; 100 | u8 ffFill[0x3C]; 101 | char fileName[0x10]; 102 | char description[0x18]; 103 | u32 binaryOffset; 104 | u32 binarySize; 105 | u16 blockCount; 106 | u16 flags; 107 | /// Seconds since January 1st, 2000. 108 | u32 modificationTime; 109 | u32 handler; 110 | u32 resource; 111 | } FxFile; 112 | 113 | extern char wwDir[FILEPATH_MAX_LENGTH]; 114 | 115 | /// Change between rom0 & ram0 storage on the WW. 116 | void wwChangeStorage(void); 117 | 118 | /// Get the currant storage as a string. 119 | const char *wwGetStorageText(void); 120 | 121 | /// Start Hello command. 122 | void wwStartStty(void); 123 | 124 | /// Start Interactive command. 125 | void wwStartInteract(void); 126 | 127 | /// Start Hello command. 128 | void wwStartHello(void); 129 | 130 | /// Start Put command. 131 | void wwStartPut(void); 132 | 133 | /// Start Get command. 134 | void wwStartGet(void); 135 | 136 | /// Start Delete command. 137 | void wwStartDelete(void); 138 | 139 | /// Start Exec command. 140 | void wwStartExec(void); 141 | 142 | /// Start Reboot command. 143 | void wwStartReboot(void); 144 | 145 | /// Start Ls command. 146 | void wwStartLs(void); 147 | 148 | /// Start Dir command. 149 | void wwStartDir(void); 150 | 151 | /// Start DF command. 152 | void wwStartDF(void); 153 | 154 | /// Start New File System command. 155 | void wwStartNewFS(void); 156 | 157 | /// Start Defrag command. 158 | void wwStartDefrag(void); 159 | 160 | /// Start Rename command. 161 | void wwStartRename(void); 162 | 163 | /// Start Speed command. 164 | void wwStartSpeed(void); 165 | 166 | /// Start Date command. 167 | void wwStartDate(void); 168 | 169 | /// Start Copy command. 170 | void wwStartCopy(void); 171 | 172 | /// Start Move command. 173 | void wwStartMove(void); 174 | 175 | /// Start SetInfo command. 176 | void wwStartSetInfo(void); 177 | 178 | /// Start ChMod command. 179 | void wwStartChMod(void); 180 | 181 | /// Start CD command. 182 | void wwStartCD(void); 183 | 184 | /// Start Pwd command. 185 | void wwStartPwd(void); 186 | 187 | /// Start XMODEM transmit. 188 | void startXModemTransmit(void); 189 | 190 | /// Start WW XMODEM transmit. 191 | void startWWXModemTransmit(void); 192 | 193 | /// Start XMODEM receive. 194 | void startXModemReceive(void); 195 | 196 | /// Handle serial in empty on WonderSwan. 197 | void handleSerialInEmpty(void); 198 | 199 | /// Handle 1 byte from WonderSwan. 200 | void handleSerialReceive(u8 value); 201 | 202 | #ifdef __cplusplus 203 | } // extern "C" 204 | #endif 205 | 206 | #endif // WONDERWITCH_HEADER 207 | -------------------------------------------------------------------------------- /source/cpu.h: -------------------------------------------------------------------------------- 1 | #ifndef CPU_HEADER 2 | #define CPU_HEADER 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | extern u8 waitMaskIn; 9 | extern u8 waitMaskOut; 10 | 11 | void run(void); 12 | void stepFrame(void); 13 | void stepScanLine(void); 14 | void cpuInit(void); 15 | void cpuReset(int type); 16 | 17 | #ifdef __cplusplus 18 | } // extern "C" 19 | #endif 20 | 21 | #endif // CPU_HEADER 22 | -------------------------------------------------------------------------------- /source/cpu.s: -------------------------------------------------------------------------------- 1 | #ifdef __arm__ 2 | 3 | #include "ARMV30MZ/ARMV30MZ.i" 4 | #include "ARMV30MZ/ARMV30MZmac.h" 5 | #include "Sphinx/Sphinx.i" 6 | 7 | #define ALLOW_REFRESH_CHG (1<<19) 8 | 9 | #define CYCLE_PSL (256) 10 | 11 | .global waitMaskIn 12 | .global waitMaskOut 13 | 14 | .global run 15 | .global stepFrame 16 | .global stepScanLine 17 | .global cpuInit 18 | .global cpuReset 19 | 20 | .syntax unified 21 | .arm 22 | 23 | #ifdef GBA 24 | .section .ewram, "ax", %progbits ;@ For the GBA 25 | #else 26 | .section .text ;@ For anything else 27 | #endif 28 | .align 2 29 | ;@---------------------------------------------------------------------------- 30 | run: ;@ Return after X frame(s) 31 | .type run STT_FUNC 32 | ;@---------------------------------------------------------------------------- 33 | ldrh r0,waitCountIn 34 | add r0,r0,#1 35 | ands r0,r0,r0,lsr#8 36 | strb r0,waitCountIn 37 | bxne lr 38 | stmfd sp!,{r4-r11,lr} 39 | 40 | ;@---------------------------------------------------------------------------- 41 | runStart: 42 | ;@---------------------------------------------------------------------------- 43 | ldr v30ptr,=V30OpTable 44 | 45 | bl refreshEMUjoypads 46 | 47 | add r1,v30ptr,#v30Flags 48 | ldmia r1,{v30f-v30cyc} ;@ Restore V30MZ state 49 | 50 | ldr r0,=emuSettings 51 | ldr r0,[r0] 52 | tst r0,#ALLOW_REFRESH_CHG 53 | beq wsFrameLoop3DS 54 | ;@---------------------------------------------------------------------------- 55 | wsFrameLoop: 56 | ;@---------------------------------------------------------------------------- 57 | mov r0,#CYCLE_PSL 58 | bl V30RunXCycles 59 | mov r0,#CYCLE_PSL/8 60 | bl cartUpdate 61 | ldr spxptr,=sphinx0 62 | bl wsvDoScanline 63 | cmp r0,#0 64 | bne wsFrameLoop 65 | ;@---------------------------------------------------------------------------- 66 | wsFrameLoopEnd: 67 | add r0,v30ptr,#v30Flags 68 | stmia r0,{v30f-v30cyc} ;@ Save V30MZ state 69 | 70 | ldrh r0,waitCountOut 71 | add r0,r0,#1 72 | ands r0,r0,r0,lsr#8 73 | strb r0,waitCountOut 74 | ldmfdeq sp!,{r4-r11,lr} ;@ Exit here if doing single frame: 75 | bxeq lr ;@ Return to rommenu() 76 | b runStart 77 | 78 | ;@---------------------------------------------------------------------------- 79 | wsFrameLoop3DS: 80 | ;@---------------------------------------------------------------------------- 81 | mov r0,#CYCLE_PSL 82 | bl V30RunXCycles 83 | mov r0,#CYCLE_PSL/8 84 | bl cartUpdate 85 | ldr spxptr,=sphinx0 86 | bl wsvDoScanline 87 | ldr r1,scanLineCount3DS 88 | cmp r0,#0 89 | cmpeq r1,#2 90 | subsne r1,r1,#1 91 | moveq r1,#200 ;@ (159 * 75.47) / 60 = 199.9955 92 | str r1,scanLineCount3DS 93 | bne wsFrameLoop3DS 94 | b wsFrameLoopEnd 95 | ;@---------------------------------------------------------------------------- 96 | v30MZCyclesPerScanline: .long 0 97 | scanLineCount3DS: .long 200 98 | joyClick: .long 0 99 | waitCountIn: .byte 0 100 | waitMaskIn: .byte 0 101 | waitCountOut: .byte 0 102 | waitMaskOut: .byte 0 103 | 104 | ;@---------------------------------------------------------------------------- 105 | stepScanLine: ;@ Return after 1 scanline 106 | .type stepScanLine STT_FUNC 107 | ;@---------------------------------------------------------------------------- 108 | stmfd sp!,{r4-r11,lr} 109 | ldr v30ptr,=V30OpTable 110 | bl wsScanLine 111 | ldmfd sp!,{r4-r11,lr} 112 | bx lr 113 | ;@---------------------------------------------------------------------------- 114 | wsScanLine: 115 | ;@---------------------------------------------------------------------------- 116 | stmfd sp!,{lr} 117 | mov r0,#CYCLE_PSL 118 | bl V30RestoreAndRunXCycles 119 | add r0,v30ptr,#v30Flags 120 | stmia r0,{v30f-v30cyc} ;@ Save V30MZ state 121 | ldmfd sp!,{lr} 122 | ldr spxptr,=sphinx0 123 | b wsvDoScanline 124 | ;@---------------------------------------------------------------------------- 125 | stepFrame: ;@ Return after 1 frame 126 | .type stepFrame STT_FUNC 127 | ;@---------------------------------------------------------------------------- 128 | stmfd sp!,{r4-r11,lr} 129 | ldr v30ptr,=V30OpTable 130 | ;@---------------------------------------------------------------------------- 131 | wsStepLoop: 132 | ;@---------------------------------------------------------------------------- 133 | bl wsScanLine 134 | cmp r0,#0 135 | bne wsStepLoop 136 | bl wsScanLine 137 | ;@---------------------------------------------------------------------------- 138 | 139 | ldmfd sp!,{r4-r11,lr} 140 | bx lr 141 | ;@---------------------------------------------------------------------------- 142 | cpuInit: ;@ Called by machineInit 143 | ;@---------------------------------------------------------------------------- 144 | stmfd sp!,{v30ptr,lr} 145 | ldr v30ptr,=V30OpTable 146 | 147 | mov r0,#CYCLE_PSL 148 | str r0,v30MZCyclesPerScanline 149 | mov r0,v30ptr 150 | bl V30Init 151 | ldr r0,=getInterruptVector 152 | str r0,[v30ptr,#v30IrqVectorFunc] 153 | ldr r0,=setBusStatus 154 | str r0,[v30ptr,#v30BusStatusFunc] 155 | 156 | ldmfd sp!,{v30ptr,lr} 157 | bx lr 158 | ;@---------------------------------------------------------------------------- 159 | cpuReset: ;@ Called by loadCart/resetGame, r0 = type 160 | ;@---------------------------------------------------------------------------- 161 | stmfd sp!,{v30ptr,lr} 162 | mov r1,r0 163 | ldr v30ptr,=V30OpTable 164 | 165 | mov r0,v30ptr 166 | bl V30Reset 167 | 168 | ldmfd sp!,{v30ptr,lr} 169 | bx lr 170 | ;@---------------------------------------------------------------------------- 171 | .end 172 | #endif // #ifdef __arm__ 173 | -------------------------------------------------------------------------------- /source/io.h: -------------------------------------------------------------------------------- 1 | #ifndef IO_HEADER 2 | #define IO_HEADER 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "WSEEPROM/WSEEPROM.h" 9 | 10 | extern u32 joyCfg; 11 | extern u32 EMUinput; 12 | extern u8 joyMapping; 13 | extern u8 wsEepromMem[0x80]; 14 | extern u8 wscEepromMem[0x800]; 15 | extern u8 scEepromMem[0x800]; 16 | extern WSEEPROM intEeprom; 17 | 18 | /** 19 | * Saves the state of io to the destination. 20 | * @param *destination: Where to save the state. 21 | * @return The size of the state. 22 | */ 23 | int ioSaveState(void *destination); 24 | 25 | /** 26 | * Loads the state of io from the source. 27 | * @param *source: Where to load the state from. 28 | * @return The size of the state. 29 | */ 30 | int ioLoadState(const void *source); 31 | 32 | /** 33 | * Gets the state size of an io state. 34 | * @return The size of the state. 35 | */ 36 | int ioGetStateSize(void); 37 | 38 | /** 39 | * Convert device input keys to target keys. 40 | * @param input NDS/GBA keys 41 | * @return The converted input. 42 | */ 43 | int convertInput(int input); 44 | 45 | /** 46 | * Set joy mapping. 47 | * @param type default or alternate 48 | */ 49 | void setJoyMapping(int type); 50 | 51 | #ifdef __cplusplus 52 | } // extern "C" 53 | #endif 54 | 55 | #endif // IO_HEADER 56 | -------------------------------------------------------------------------------- /source/io.s: -------------------------------------------------------------------------------- 1 | #ifdef __arm__ 2 | 3 | #include "ARMV30MZ/ARMV30MZ.i" 4 | #include "Sphinx/Sphinx.i" 5 | #include "WSEEPROM/WSEEPROM.i" 6 | #include "Shared/EmuMenu.i" 7 | 8 | .global joyCfg 9 | .global EMUinput 10 | .global joyMapping 11 | .global wsEepromMem 12 | .global wscEepromMem 13 | .global scEepromMem 14 | .global intEeprom 15 | 16 | .global ioReset 17 | .global convertInput 18 | .global refreshEMUjoypads 19 | .global ioSaveState 20 | .global ioLoadState 21 | .global ioGetStateSize 22 | .global updateSlowIO 23 | .global setJoyMapping 24 | 25 | .global intEepromSetSize 26 | .global intEepromDataLowR 27 | .global intEepromDataHighR 28 | .global intEepromAdrLowR 29 | .global intEepromAdrHighR 30 | .global intEepromStatusR 31 | .global intEepromDataLowW 32 | .global intEepromDataHighW 33 | .global intEepromAdrLowW 34 | .global intEepromAdrHighW 35 | .global intEepromCommandW 36 | 37 | .syntax unified 38 | .arm 39 | 40 | .section .text 41 | .align 2 42 | ;@---------------------------------------------------------------------------- 43 | ioReset: 44 | ;@---------------------------------------------------------------------------- 45 | stmfd sp!,{lr} 46 | 47 | mov r0,#0xF 48 | strb r0,lastBattery 49 | bl intEepromReset 50 | ldrb r0,joyMapping 51 | bl setJoyMapping 52 | 53 | ldmfd sp!,{pc} 54 | 55 | ;@---------------------------------------------------------------------------- 56 | setJoyMapping: ;@ r0 = type 57 | .type setJoyMapping STT_FUNC 58 | ;@---------------------------------------------------------------------------- 59 | mov r1,#KEY_L|KEY_R 60 | adr r2,joyDefaultMap 61 | cmp r0,#1 62 | adreq r2,joyAlternateMap 63 | moveq r1,#KEY_L|KEY_SELECT 64 | str r1,menuOpener 65 | ldr r0,=gMachine 66 | ldrb r0,[r0] 67 | cmp r0,#HW_POCKETCHALLENGEV2 68 | adreq r2,pcv2Joypad 69 | str r2,joyHandler 70 | bx lr 71 | ;@---------------------------------------------------------------------------- 72 | ioSaveState: ;@ In r0=destination. Out r0=size. 73 | .type ioSaveState STT_FUNC 74 | ;@---------------------------------------------------------------------------- 75 | stmfd sp!,{lr} 76 | 77 | // ldr r1,=rtcRegs 78 | // mov r2,#0x100 79 | // bl memcpy 80 | 81 | ldmfd sp!,{lr} 82 | mov r0,#0x100 83 | bx lr 84 | ;@---------------------------------------------------------------------------- 85 | ioLoadState: ;@ In r0=source. Out r0=size. 86 | .type ioLoadState STT_FUNC 87 | ;@---------------------------------------------------------------------------- 88 | stmfd sp!,{lr} 89 | 90 | // bl initSysMem 91 | 92 | ldmfd sp!,{lr} 93 | ;@---------------------------------------------------------------------------- 94 | ioGetStateSize: ;@ Out r0=state size. 95 | .type ioGetStateSize STT_FUNC 96 | ;@---------------------------------------------------------------------------- 97 | mov r0,#0x100 98 | bx lr 99 | ;@---------------------------------------------------------------------------- 100 | convertInput: ;@ Convert from device keys to target, r0=input/output 101 | .type convertInput STT_FUNC 102 | ;@---------------------------------------------------------------------------- 103 | mvn r1,r0 104 | ldr r2,menuOpener 105 | tst r1,r2 ;@ Keys to open menu 106 | orreq r0,r0,#KEY_OPEN_MENU 107 | bx lr 108 | ;@---------------------------------------------------------------------------- 109 | refreshEMUjoypads: ;@ Call every frame with r10=v30ptr 110 | ;@---------------------------------------------------------------------------- 111 | ldr r4,EMUinput 112 | and r0,r4,#0xf0 113 | ldr pc,joyHandler 114 | ;@---------------------------------------------------------------------------- 115 | joyDefaultMap: 116 | ;@---------------------------------------------------------------------------- 117 | mov r3,r4 ;@ For A/B auto fire. 118 | ldr r1,=frameTotal 119 | ldr r1,[r1] 120 | movs r1,r1,lsr#2 ;@ C=frame&2 (autofire alternates every 4:th frame) 121 | ldr r2,joyCfg 122 | andcs r3,r3,r2 123 | tstcs r3,r3,lsr#10 ;@ NDS L? 124 | andcs r3,r3,r2,lsr#16 125 | adr r1,rlud2urdl 126 | ldrb r0,[r1,r0,lsr#4] 127 | 128 | ldr spxptr,=sphinx0 129 | ldrb r1,[spxptr,#wsvOrientation] 130 | cmp r1,#0 131 | bne verticalJoypad 132 | 133 | tst r4,#KEY_L ;@ NDS L? 134 | tsteq r4,#KEY_R ;@ NDS R? 135 | movne r0,r0,lsl#4 ;@ Map dpad to X or Y keys. 136 | 137 | tst r4,#KEY_START ;@ NDS Start 138 | orrne r0,r0,#WS_KEY_START ;@ WS Start 139 | tst r4,#KEY_SELECT ;@ NDS Select 140 | orrne r0,r0,#WS_KEY_SOUND ;@ WS Sound 141 | 142 | ands r1,r3,#KEY_A|KEY_B ;@ A/B buttons 143 | cmpne r1,#KEY_A|KEY_B 144 | eorne r1,r1,#KEY_A|KEY_B 145 | tst r2,#0x400 ;@ Swap A/B? 146 | andeq r1,r3,#KEY_A|KEY_B 147 | orr r0,r0,r1,lsl#2 148 | 149 | // ldr r1,=0x2222 ;@ Pin strap bit#1 150 | // orr r0,r0,r1 151 | 152 | ldr spxptr,=sphinx0 153 | b wsvSetJoyState 154 | ;@---------------------------------------------------------------------------- 155 | joyAlternateMap: 156 | ;@---------------------------------------------------------------------------- 157 | adr r1,rlud2urdl 158 | ldrb r0,[r1,r0,lsr#4] 159 | 160 | and r3,r4,#0xf00 161 | adr r1,rlxy2y1y4 162 | ldrb r1,[r1,r3,lsr#8] 163 | orr r0,r0,r1,lsl#8 164 | 165 | tst r4,#KEY_START ;@ NDS Start 166 | orrne r0,r0,#WS_KEY_START ;@ WS Start 167 | tst r4,#KEY_SELECT ;@ NDS Select 168 | orrne r0,r0,#WS_KEY_SOUND ;@ WS Sound 169 | 170 | tst r4,#KEY_A ;@ NDS A 171 | orrne r0,r0,#WS_KEY_A ;@ WS A 172 | tst r4,#KEY_B ;@ NDS B 173 | orrne r0,r0,#WS_KEY_B ;@ WS B 174 | 175 | ldr spxptr,=sphinx0 176 | b wsvSetJoyState 177 | ;@---------------------------------------------------------------------------- 178 | verticalJoypad: 179 | ;@---------------------------------------------------------------------------- 180 | mov r0,r0,lsl#4 ;@ Map dpad to Y keys. 181 | 182 | tst r4,#KEY_START ;@ NDS Start 183 | orrne r0,r0,#WS_KEY_START ;@ WS Start 184 | tst r4,#KEY_SELECT ;@ NDS Select 185 | orrne r0,r0,#WS_KEY_SOUND ;@ WS Sound 186 | 187 | and r1,r4,#KEY_A|KEY_B ;@ A/B buttons 188 | and r2,r4,#KEY_X|KEY_Y ;@ X/Y buttons 189 | orr r1,r1,r2,lsr#8 190 | adr r2,abxy2xpad 191 | ldrb r1,[r2,r1] 192 | orr r0,r0,r1,lsl#4 193 | 194 | ldr spxptr,=sphinx0 195 | b wsvSetJoyState 196 | ;@---------------------------------------------------------------------------- 197 | pcv2Joypad: 198 | ;@---------------------------------------------------------------------------- 199 | adr r1,rlud2ldur 200 | ldrb r0,[r1,r0,lsr#4] 201 | and r3,r4,#0xf00 202 | adr r1,rlxy2pcv2 203 | ldrb r1,[r1,r3,lsr#8] 204 | orr r0,r0,r1,lsl#4 205 | 206 | tst r4,#KEY_A ;@ NDS A 207 | orrne r0,r0,#PCV2_KEY_CLEAR ;@ PCV2 Clear 208 | tst r4,#KEY_B ;@ NDS B 209 | orrne r0,r0,#PCV2_KEY_CIRCLE ;@ PCV2 Circle 210 | ldr r1,=0x2222 211 | orr r0,r0,r1 212 | 213 | ldr spxptr,=sphinx0 214 | b wsvSetJoyState 215 | ;@---------------------------------------------------------------------------- 216 | joyHandler: .long joyDefaultMap 217 | menuOpener: .long KEY_L|KEY_R 218 | joyCfg: .long 0x00ff01ff ;@ byte0=auto mask, byte1=(saves R), byte2=R auto mask 219 | 220 | ;@ Bit: 11 10 9 8 7 6 5 4 3 2 1 0 221 | ;@ WS: Y4 Y3 Y2 Y1 X4 X3 X2 X1 B A Start 0 222 | ;@ PCV2: Pass Circle 1 Clear Right Escape 1 View Up Down 1 Left 223 | joy0State: .long 0 224 | rlud2urdl: .byte 0x00,0x20,0x80,0xA0, 0x10,0x30,0x90,0xB0, 0x40,0x60,0xC0,0xE0, 0x50,0x70,0xD0,0xF0 225 | rlud2ldur: .byte 0x00,0x80,0x01,0x81, 0x08,0x88,0x09,0x89, 0x04,0x84,0x05,0x85, 0x0C,0x8C,0x0D,0x8D 226 | abxy2xpad: .byte 0x00,0x02,0x04,0x06, 0x01,0x03,0x05,0x07, 0x08,0x0A,0x0C,0x0E, 0x09,0x0B,0x0D,0x0F 227 | rlxy2pcv2: .byte 0x00,0x01,0x04,0x05, 0x01,0x01,0x05,0x05, 0x80,0x81,0x84,0x85, 0x81,0x81,0x85,0x85 228 | rlxy2y1y4: .byte 0x00,0x01,0x08,0x09, 0x02,0x03,0x0A,0x0B, 0x04,0x05,0x0C,0x0D, 0x06,0x07,0x0E,0x0F 229 | 230 | EMUinput: .long 0 ;@ This label here for Main.c to use 231 | 232 | joyMapping: .byte 0 233 | .byte 0,0,0 234 | 235 | ;@---------------------------------------------------------------------------- 236 | updateSlowIO: ;@ Call once every frame, updates battery levels and rtc. 237 | ;@---------------------------------------------------------------------------- 238 | ldrb r0,slowTimer 239 | subs r0,r0,#1 240 | strbhi r0,slowTimer 241 | bxhi lr 242 | ldr r0,=fpsTarget 243 | ldrb r0,[r0] 244 | strb r0,slowTimer 245 | 246 | stmfd sp!,{r12,lr} 247 | blx getBatteryLevel 248 | ldrb r1,lastBattery 249 | strb r0,lastBattery 250 | eor r1,r1,r0 251 | ands r0,r0,#0xC 252 | mov r0,#0 253 | moveq r0,#1 254 | tst r1,#0xF 255 | blne setLowBattery 256 | ldmfd sp!,{r12,lr} 257 | 258 | bx lr 259 | // ldr r0,=(3072000/8) ;@ 1 Second in cart clocks (384000). 260 | // b cartUpdate 261 | 262 | ;@---------------------------------------------------------------------------- 263 | slowTimer: 264 | .byte 0 265 | lastBattery: 266 | .byte 0 267 | .byte 0 268 | .byte 0 269 | ;@---------------------------------------------------------------------------- 270 | 271 | ;@---------------------------------------------------------------------------- 272 | intEepromDataLowR: ;@ 0xBA 273 | ;@---------------------------------------------------------------------------- 274 | adr eeptr,intEeprom 275 | b wsEepromDataLowR 276 | ;@---------------------------------------------------------------------------- 277 | intEepromDataHighR: ;@ 0xBB 278 | ;@---------------------------------------------------------------------------- 279 | adr eeptr,intEeprom 280 | b wsEepromDataHighR 281 | ;@---------------------------------------------------------------------------- 282 | intEepromAdrLowR: ;@ 0xBC 283 | ;@---------------------------------------------------------------------------- 284 | adr eeptr,intEeprom 285 | b wsEepromAddressLowR 286 | ;@---------------------------------------------------------------------------- 287 | intEepromAdrHighR: ;@ 0xBD 288 | ;@---------------------------------------------------------------------------- 289 | adr eeptr,intEeprom 290 | b wsEepromAddressHighR 291 | ;@---------------------------------------------------------------------------- 292 | intEepromStatusR: ;@ 0xBE 293 | ;@---------------------------------------------------------------------------- 294 | adr eeptr,intEeprom 295 | b wsEepromStatusR 296 | ;@---------------------------------------------------------------------------- 297 | intEepromDataLowW: ;@ 0xBA, r0 = value 298 | ;@---------------------------------------------------------------------------- 299 | mov r1,r0 300 | adr eeptr,intEeprom 301 | b wsEepromDataLowW 302 | ;@---------------------------------------------------------------------------- 303 | intEepromDataHighW: ;@ 0xBB, r0 = value 304 | ;@---------------------------------------------------------------------------- 305 | mov r1,r0 306 | adr eeptr,intEeprom 307 | b wsEepromDataHighW 308 | ;@---------------------------------------------------------------------------- 309 | intEepromAdrLowW: ;@ 0xBC, r0 = value 310 | ;@---------------------------------------------------------------------------- 311 | mov r1,r0 312 | adr eeptr,intEeprom 313 | b wsEepromAddressLowW 314 | ;@---------------------------------------------------------------------------- 315 | intEepromAdrHighW: ;@ 0xBD, r0 = value 316 | ;@---------------------------------------------------------------------------- 317 | mov r1,r0 318 | adr eeptr,intEeprom 319 | b wsEepromAddressHighW 320 | ;@---------------------------------------------------------------------------- 321 | intEepromCommandW: ;@ 0xBE, r0 = value 322 | ;@---------------------------------------------------------------------------- 323 | mov r1,r0 324 | adr eeptr,intEeprom 325 | b wsEepromCommandW 326 | ;@---------------------------------------------------------------------------- 327 | intEepromReset: 328 | ;@---------------------------------------------------------------------------- 329 | ldr r0,=gSOC 330 | ldrb r0,[r0] 331 | cmp r0,#SOC_SPHINX 332 | ldrmi r2,=wsEepromMem 333 | ldreq r2,=wscEepromMem 334 | ldrhi r2,=scEepromMem 335 | mov r1,#0x080 ;@ 1kbit, 16kbit is switched to for Color _Games_. 336 | mov r3,#1 ;@ Allow protect 337 | adr eeptr,intEeprom 338 | b wsEepromReset 339 | ;@---------------------------------------------------------------------------- 340 | intEepromSetSize: ;@ r0 = size, 0=1kbit, !0=16kbit 341 | ;@---------------------------------------------------------------------------- 342 | cmp r0,#0 343 | moveq r1,#0x080 ;@ 1kbit 344 | movne r1,#0x800 ;@ 16kbit 345 | adr eeptr,intEeprom 346 | b wsEepromSetSize 347 | ;@---------------------------------------------------------------------------- 348 | .pool 349 | intEeprom: 350 | .space wsEepromSize 351 | 352 | #ifdef GBA 353 | .section .sbss ;@ For the GBA 354 | #else 355 | .section .bss 356 | #endif 357 | .align 2 358 | wsEepromMem: 359 | .space 0x80 360 | wscEepromMem: 361 | .space 0x800 362 | scEepromMem: 363 | .space 0x800 364 | ;@---------------------------------------------------------------------------- 365 | 366 | .end 367 | #endif // #ifdef __arm__ 368 | --------------------------------------------------------------------------------