├── .gitignore ├── .travis.yml ├── Makefile ├── README.md ├── arm7 ├── Makefile └── source │ └── main.c ├── arm9 ├── Makefile ├── ds_arm9_hi.mem ├── ds_arm9_hi.specs └── source │ ├── config.h │ ├── cyclone │ ├── Cyclone.h │ └── Cyclone.s │ ├── externSound.cpp │ ├── externSound.h │ ├── file.c │ ├── file.h │ ├── file_browse.cpp │ ├── file_browse.h │ ├── inifile.cpp │ ├── inifile.h │ ├── main.cpp │ ├── pico │ ├── Area.c │ ├── Cart.c │ ├── Disa.c │ ├── Disa.h │ ├── Draw.c │ ├── Draw2.c │ ├── Draw2_asm.s │ ├── Draw3.c │ ├── Functions_asm.s │ ├── Memory.cpp │ ├── Memory_asm.s │ ├── Pico.c │ ├── Pico.h │ ├── PicoInt.h │ ├── Sek.c │ ├── Timer.c │ ├── Timer.h │ ├── Utils.c │ ├── VideoPort.c │ └── sound │ │ ├── DrZ80.h │ │ ├── DrZ80.s │ │ ├── DrZ80.txt │ │ ├── driver.h │ │ ├── mz80.c │ │ ├── mz80.h │ │ ├── mz80.txt │ │ ├── sn76496.c │ │ ├── sn76496.h │ │ ├── sound.c │ │ ├── sound.h │ │ ├── ym2612.c │ │ ├── ym2612.h │ │ ├── ym2612_helper.h │ │ └── ym2612_helper.s │ ├── singleton.h │ ├── streamingaudio.c │ ├── streamingaudio.h │ ├── stringtool.cpp │ ├── stringtool.h │ ├── tonccpy.c │ └── tonccpy.h ├── fix_ndsheader.py ├── genesis-32x32.bmp └── segalogo.bmp /.gitignore: -------------------------------------------------------------------------------- 1 | # Binary File 2 | *.nds 3 | *.NDS 4 | *.cdc 5 | *.cia 6 | *.app 7 | *.srldr 8 | *.elf 9 | *.arm7 10 | *.arm9 11 | *.DS_Store 12 | *.exe 13 | *.dll 14 | 15 | # Build Directory 16 | build/ 17 | data/ 18 | 19 | # Project Files 20 | .idea/ 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: assembly 2 | 3 | os: linux 4 | sudo: false 5 | dist: trusty 6 | 7 | env: 8 | global: 9 | - DEVKITPRO=/opt/devkitpro 10 | - DEVKITARM=/opt/devkitpro/devkitARM 11 | 12 | cache: 13 | directories: 14 | - "$HOME/.local" 15 | - "$DEVKITPRO" 16 | 17 | before_install: 18 | - curl -L https://github.com/devkitPro/pacman/releases/download/devkitpro-pacman-1.0.1/devkitpro-pacman.deb -o pacman.deb 19 | 20 | install: 21 | - sudo dpkg -i pacman.deb 22 | - sudo dkp-pacman -Sy 23 | - sudo dkp-pacman -S devkitARM general-tools dstools ndstool libnds libfat-nds --noconfirm 24 | - export DEVKITPRO=/opt/devkitpro 25 | - export DEVKITARM=${DEVKITPRO}/devkitARM 26 | 27 | script: 28 | - make 29 | 30 | addons: 31 | apt: 32 | sources: 33 | - ubuntu-toolchain-r-test 34 | packages: 35 | - gcc-4.9 36 | - g++-4.9 37 | - libstdc++6 38 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | .SECONDARY: 5 | 6 | ifeq ($(strip $(DEVKITARM)),) 7 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 8 | endif 9 | 10 | include $(DEVKITARM)/ds_rules 11 | 12 | export VERSION_MAJOR := 2 13 | export VERSION_MINOR := 0 14 | export VERSION_PATCH := 2 15 | 16 | 17 | VERSION := $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH) 18 | #--------------------------------------------------------------------------------- 19 | # TARGET is the name of the output 20 | # BUILD is the directory where object files & intermediate files will be placed 21 | # SOURCES is a list of directories containing source code 22 | # INCLUDES is a list of directories containing extra header files 23 | # DATA is a list of directories containing binary files embedded using bin2o 24 | # GRAPHICS is a list of directories containing image files to be converted with grit 25 | #--------------------------------------------------------------------------------- 26 | TARGET := PicoDriveTWL 27 | BUILD := build 28 | SOURCES := source 29 | DATA := data gfx_bin 30 | 31 | #--------------------------------------------------------------------------------- 32 | # options for code generation 33 | #--------------------------------------------------------------------------------- 34 | ARCH := -mthumb -mthumb-interwork 35 | 36 | CFLAGS := -g -Wall -O2 \ 37 | -ffunction-sections -fdata-sections \ 38 | -march=armv5te -mtune=arm946e-s -fomit-frame-pointer\ 39 | -ffast-math \ 40 | $(ARCH) 41 | 42 | CFLAGS += $(INCLUDE) -DARM9 43 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=c++1z 44 | 45 | ASFLAGS := -g $(ARCH) 46 | LDFLAGS = -specs=ds_arm9.specs -g -Wl,--gc-sections $(ARCH) -Wl,-Map,$(notdir $*.map) 47 | 48 | #--------------------------------------------------------------------------------- 49 | # any extra libraries we wish to link with the project (order is important) 50 | #--------------------------------------------------------------------------------- 51 | LIBS := -lfat -lmm9 -lnds9 52 | 53 | 54 | #--------------------------------------------------------------------------------- 55 | # list of directories containing libraries, this must be the top level containing 56 | # include and lib 57 | #--------------------------------------------------------------------------------- 58 | LIBDIRS := $(LIBNDS) 59 | 60 | #--------------------------------------------------------------------------------- 61 | # no real need to edit anything past this point unless you need to add additional 62 | # rules for different file extensions 63 | #--------------------------------------------------------------------------------- 64 | ifneq ($(BUILD),$(notdir $(CURDIR))) 65 | #--------------------------------------------------------------------------------- 66 | export TOPDIR := $(CURDIR) 67 | 68 | export OUTPUT := $(CURDIR)/$(TARGET) 69 | 70 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 71 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ 72 | $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) 73 | 74 | export DEPSDIR := $(CURDIR)/$(BUILD) 75 | 76 | ifneq ($(strip $(NITRODATA)),) 77 | export NITRO_FILES := $(CURDIR)/$(NITRODATA) 78 | endif 79 | 80 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 81 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 82 | BMPFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.bmp))) 83 | PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png))) 84 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 85 | BINFILES := load.bin bootstub.bin 86 | 87 | #--------------------------------------------------------------------------------- 88 | # use CXX for linking C++ projects, CC for standard C 89 | #--------------------------------------------------------------------------------- 90 | ifeq ($(strip $(CPPFILES)),) 91 | #--------------------------------------------------------------------------------- 92 | export LD := $(CC) 93 | #--------------------------------------------------------------------------------- 94 | else 95 | #--------------------------------------------------------------------------------- 96 | export LD := $(CXX) 97 | #--------------------------------------------------------------------------------- 98 | endif 99 | #--------------------------------------------------------------------------------- 100 | 101 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 102 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 103 | 104 | export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir)) \ 105 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 106 | -I$(CURDIR)/$(BUILD) 107 | 108 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 109 | 110 | icons := $(wildcard *.bmp) 111 | 112 | ifneq (,$(findstring $(TARGET).bmp,$(icons))) 113 | export GAME_ICON := $(CURDIR)/$(TARGET).bmp 114 | else 115 | ifneq (,$(findstring icon.bmp,$(icons))) 116 | export GAME_ICON := $(CURDIR)/icon.bmp 117 | endif 118 | endif 119 | 120 | export GAME_TITLE := $(TARGET) 121 | 122 | .PHONY: bootloader bootstub clean arm7/$(TARGET).elf arm9/$(TARGET).elf 123 | 124 | all: $(TARGET).nds 125 | 126 | dist: all 127 | @rm -fr hbmenu 128 | @mkdir hbmenu 129 | @cp $(TARGET).nds hbmenu/BOOT.NDS 130 | @cp BootStrap/_BOOT_MP.NDS BootStrap/TTMENU.DAT BootStrap/_DS_MENU.DAT BootStrap/ez5sys.bin BootStrap/akmenu4.nds hbmenu 131 | @tar -cvjf $(TARGET)-$(VERSION).tar.bz2 hbmenu testfiles README.html COPYING hbmenu -X exclude.lst 132 | 133 | $(TARGET).nds: $(TARGET).arm7 $(TARGET).arm9 134 | ndstool -u 00030004 -g EPDA 00 "PICODRIVE" -c $(TARGET).nds -7 $(TARGET).arm7.elf -9 $(TARGET).arm9.elf \ 135 | -b genesis-32x32.bmp "PicoDrive TWL;Version $(VERSION);Ryan FB, RocketRobz" 136 | python27 fix_ndsheader.py $(CURDIR)/$(TARGET).nds 137 | 138 | $(TARGET).arm7: arm7/$(TARGET).elf 139 | cp arm7/$(TARGET).elf $(TARGET).arm7.elf 140 | 141 | $(TARGET).arm9: arm9/$(TARGET).elf 142 | cp arm9/$(TARGET).elf $(TARGET).arm9.elf 143 | 144 | #--------------------------------------------------------------------------------- 145 | arm7/$(TARGET).elf: 146 | @$(MAKE) -C arm7 147 | 148 | #--------------------------------------------------------------------------------- 149 | arm9/$(TARGET).elf: 150 | @$(MAKE) -C arm9 151 | 152 | #--------------------------------------------------------------------------------- 153 | #$(BUILD): 154 | #@[ -d $@ ] || mkdir -p $@ 155 | #@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 156 | #--------------------------------------------------------------------------------- 157 | clean: 158 | @echo clean ... 159 | @rm -fr data 160 | @rm -fr $(BUILD) $(TARGET).elf $(TARGET).nds 161 | @rm -fr $(TARGET).arm7.elf 162 | @rm -fr $(TARGET).arm9.elf 163 | @$(MAKE) -C arm9 clean 164 | @$(MAKE) -C arm7 clean 165 | 166 | data: 167 | @mkdir -p data 168 | 169 | bootloader: data 170 | @$(MAKE) -C bootloader 171 | 172 | bootstub: data 173 | @$(MAKE) -C bootstub 174 | 175 | #--------------------------------------------------------------------------------- 176 | else 177 | 178 | #--------------------------------------------------------------------------------- 179 | # main targets 180 | #--------------------------------------------------------------------------------- 181 | #$(OUTPUT).nds : $(OUTPUT).elf 182 | #$(OUTPUT).elf : $(OFILES) 183 | 184 | #--------------------------------------------------------------------------------- 185 | %.bin.o : %.bin 186 | #--------------------------------------------------------------------------------- 187 | @echo $(notdir $<) 188 | $(bin2o) 189 | 190 | -include $(DEPSDIR)/*.d 191 | 192 | #--------------------------------------------------------------------------------------- 193 | endif 194 | #--------------------------------------------------------------------------------------- 195 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PicoDriveTWL 2 | SEGA Genesis emulator for the Nintendo DS(i) 3 | 4 | # Credits 5 | * Ryan FB: Original PicoDriveDS source code 6 | * RocketRobz: New features 7 | * Evie11: Fixes to make it build in a modern dev environment 8 | * NightScript: Creating this repo 9 | * devkitPro: Use of devkitArm and libnds, and file browsing code from nds-hb-menu -------------------------------------------------------------------------------- /arm7/Makefile: -------------------------------------------------------------------------------- 1 | export ARM7_MAJOR := 0 2 | export ARM7_MINOR := 6 3 | export ARM7_PATCH := 0 4 | 5 | VERSTRING := $(ARM7_MAJOR).$(ARM7_MINOR).$(ARM7_PATCH) 6 | #--------------------------------------------------------------------------------- 7 | .SUFFIXES: 8 | #--------------------------------------------------------------------------------- 9 | ifeq ($(strip $(DEVKITARM)),) 10 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM) 11 | endif 12 | 13 | include $(DEVKITARM)/ds_rules 14 | #--------------------------------------------------------------------------------- 15 | # TARGET is the name of the output 16 | # BUILD is the directory where object files & intermediate files will be placed 17 | # SOURCES is a list of directories containing source code 18 | # INCLUDES is a list of directories containing extra header files 19 | #--------------------------------------------------------------------------------- 20 | TARGET := PicoDriveTWL 21 | BUILD := build 22 | SOURCES := source 23 | INCLUDES := include build 24 | 25 | #--------------------------------------------------------------------------------- 26 | # options for code generation 27 | #--------------------------------------------------------------------------------- 28 | ARCH := -march=armv4t -marm -mthumb-interwork 29 | 30 | CFLAGS := -g -Wall -O2\ 31 | -mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\ 32 | -ffast-math \ 33 | $(ARCH) 34 | 35 | CFLAGS += $(INCLUDE) -DARM7 36 | 37 | ASFLAGS := -g $(ARCH) 38 | LDFLAGS = -specs=ds_arm7.specs -g $(ARCH) -Wl,--nmagic -Wl,-Map,$(notdir $*).map 39 | 40 | 41 | #--------------------------------------------------------------------------------- 42 | # any extra libraries we wish to link with the project 43 | #--------------------------------------------------------------------------------- 44 | LIBS := -lnds7 -lmm7 45 | 46 | 47 | #--------------------------------------------------------------------------------- 48 | # list of directories containing libraries, this must be the top level containing 49 | # include and lib 50 | #--------------------------------------------------------------------------------- 51 | LIBDIRS := $(LIBNDS) 52 | 53 | 54 | #--------------------------------------------------------------------------------- 55 | # no real need to edit anything past this point unless you need to add additional 56 | # rules for different file extensions 57 | #--------------------------------------------------------------------------------- 58 | ifneq ($(BUILD),$(notdir $(CURDIR))) 59 | #--------------------------------------------------------------------------------- 60 | 61 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) 62 | 63 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 64 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 65 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 66 | 67 | export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 68 | 69 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 70 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 71 | -I$(CURDIR)/$(BUILD) 72 | 73 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 74 | 75 | export DEPSDIR := $(CURDIR)/$(BUILD) 76 | 77 | export OUTPUT := $(CURDIR)/$(TARGET) 78 | 79 | #--------------------------------------------------------------------------------- 80 | # use CXX for linking C++ projects, CC for standard C 81 | #--------------------------------------------------------------------------------- 82 | ifeq ($(strip $(CPPFILES)),) 83 | #--------------------------------------------------------------------------------- 84 | export LD := $(CC) 85 | #--------------------------------------------------------------------------------- 86 | else 87 | #--------------------------------------------------------------------------------- 88 | export LD := $(CXX) 89 | #--------------------------------------------------------------------------------- 90 | endif 91 | #--------------------------------------------------------------------------------- 92 | 93 | .PHONY: all $(BUILD) clean 94 | 95 | all : $(BUILD) 96 | 97 | #--------------------------------------------------------------------------------- 98 | $(BUILD): 99 | @[ -d $@ ] || mkdir -p $@ 100 | @$(MAKE) -C $(BUILD) -f $(CURDIR)/Makefile 101 | 102 | 103 | #--------------------------------------------------------------------------------- 104 | dist: all 105 | #--------------------------------------------------------------------------------- 106 | @tar --exclude=*CVS* --exclude=.svn -cvjf default_arm7-src-$(VERSTRING).tar.bz2 source Makefile 107 | @tar -cvjf default_arm7-$(VERSTRING).tar.bz2 default.elf 108 | 109 | #--------------------------------------------------------------------------------- 110 | install: all 111 | #--------------------------------------------------------------------------------- 112 | cp $(TARGET).elf $(LIBNDS) 113 | 114 | #--------------------------------------------------------------------------------- 115 | clean: 116 | @echo clean ... 117 | @rm -fr $(BUILD) $(TARGET).elf $(TARGET).arm7 118 | 119 | 120 | #--------------------------------------------------------------------------------- 121 | else 122 | 123 | DEPENDS := $(OFILES:.o=.d) 124 | 125 | #--------------------------------------------------------------------------------- 126 | # main targets 127 | #--------------------------------------------------------------------------------- 128 | 129 | $(OUTPUT).elf : $(OFILES) $(LIBNDS)/lib/libnds7.a 130 | 131 | -include $(DEPENDS) 132 | 133 | #--------------------------------------------------------------------------------------- 134 | endif 135 | #--------------------------------------------------------------------------------------- 136 | -------------------------------------------------------------------------------- /arm7/source/main.c: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------- 2 | 3 | default ARM7 core 4 | 5 | Copyright (C) 2005 - 2010 6 | Michael Noland (joat) 7 | Jason Rogers (dovoto) 8 | Dave Murphy (WinterMute) 9 | 10 | This software is provided 'as-is', without any express or implied 11 | warranty. In no event will the authors be held liable for any 12 | damages arising from the use of this software. 13 | 14 | Permission is granted to anyone to use this software for any 15 | purpose, including commercial applications, and to alter it and 16 | redistribute it freely, subject to the following restrictions: 17 | 18 | 1. The origin of this software must not be misrepresented; you 19 | must not claim that you wrote the original software. If you use 20 | this software in a product, an acknowledgment in the product 21 | documentation would be appreciated but is not required. 22 | 23 | 2. Altered source versions must be plainly marked as such, and 24 | must not be misrepresented as being the original software. 25 | 26 | 3. This notice may not be removed or altered from any source 27 | distribution. 28 | 29 | ---------------------------------------------------------------------------------*/ 30 | #include 31 | #include 32 | 33 | //--------------------------------------------------------------------------------- 34 | void VblankHandler(void) { 35 | //--------------------------------------------------------------------------------- 36 | } 37 | 38 | //--------------------------------------------------------------------------------- 39 | void VcountHandler() { 40 | //--------------------------------------------------------------------------------- 41 | inputGetAndSend(); 42 | } 43 | 44 | volatile bool exitflag = false; 45 | 46 | //--------------------------------------------------------------------------------- 47 | void powerButtonCB() { 48 | //--------------------------------------------------------------------------------- 49 | exitflag = true; 50 | } 51 | 52 | //--------------------------------------------------------------------------------- 53 | int main() { 54 | //--------------------------------------------------------------------------------- 55 | // clear sound registers 56 | dmaFillWords(0, (void*)0x04000400, 0x100); 57 | 58 | REG_SOUNDCNT |= SOUND_ENABLE; 59 | writePowerManagement(PM_CONTROL_REG, ( readPowerManagement(PM_CONTROL_REG) & ~PM_SOUND_MUTE ) | PM_SOUND_AMP ); 60 | powerOn(POWER_SOUND); 61 | 62 | readUserSettings(); 63 | ledBlink(0); 64 | 65 | irqInit(); 66 | // Start the RTC tracking IRQ 67 | initClockIRQ(); 68 | 69 | touchInit(); 70 | 71 | fifoInit(); 72 | 73 | mmInstall(FIFO_MAXMOD); 74 | 75 | SetYtrigger(80); 76 | 77 | installSoundFIFO(); 78 | installSystemFIFO(); 79 | 80 | irqSet(IRQ_VCOUNT, VcountHandler); 81 | irqSet(IRQ_VBLANK, VblankHandler); 82 | 83 | irqEnable( IRQ_VBLANK | IRQ_VCOUNT | IRQ_NETWORK ); 84 | 85 | setPowerButtonCB(powerButtonCB); 86 | 87 | // Keep the ARM7 mostly idle 88 | while (!exitflag) { 89 | if ( 0 == (REG_KEYINPUT & (KEY_SELECT | KEY_START | KEY_L | KEY_R))) { 90 | exitflag = true; 91 | } 92 | swiWaitForVBlank(); 93 | } 94 | return 0; 95 | } 96 | 97 | -------------------------------------------------------------------------------- /arm9/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 | include $(DEVKITARM)/ds_rules 10 | 11 | #--------------------------------------------------------------------------------- 12 | # TARGET is the name of the output 13 | # BUILD is the directory where object files & intermediate files will be placed 14 | # SOURCES is a list of directories containing source code 15 | # INCLUDES is a list of directories containing extra header files 16 | # DATA is a list of directories containing binary files embedded using bin2o 17 | # GRAPHICS is a list of directories containing image files to be converted with grit 18 | # MAXMOD_SOUNDBANK contains a directory of music and sound effect files 19 | #--------------------------------------------------------------------------------- 20 | TARGET := PicoDriveTWL 21 | BUILD := build 22 | SOURCES := source source/cyclone source/pico/sound source/pico 23 | INCLUDES := include source 24 | DATA := 25 | 26 | #--------------------------------------------------------------------------------- 27 | # options for code generation 28 | #--------------------------------------------------------------------------------- 29 | ARCH := -marm -mthumb-interwork 30 | 31 | CFLAGS := -g -Wall -O3\ 32 | -march=armv5te -mtune=arm946e-s -fomit-frame-pointer\ 33 | -ffast-math \ 34 | $(ARCH) 35 | 36 | CFLAGS += $(INCLUDE) -DARM9 -D_NO_BOOTSTUB_ 37 | CXXFLAGS := $(CFLAGS) -fno-exceptions -std=gnu++11 38 | 39 | ASFLAGS := -g $(ARCH) 40 | LDFLAGS = -specs=../ds_arm9_hi.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 41 | 42 | #--------------------------------------------------------------------------------- 43 | # any extra libraries we wish to link with the project (order is important) 44 | #--------------------------------------------------------------------------------- 45 | LIBS := -lfat -lmm9 -lnds9 46 | 47 | 48 | #--------------------------------------------------------------------------------- 49 | # list of directories containing libraries, this must be the top level containing 50 | # include and lib 51 | #--------------------------------------------------------------------------------- 52 | LIBDIRS := $(CURDIR) ../ $(LIBNDS) 53 | 54 | #--------------------------------------------------------------------------------- 55 | # no real need to edit anything past this point unless you need to add additional 56 | # rules for different file extensions 57 | #--------------------------------------------------------------------------------- 58 | ifneq ($(BUILD),$(notdir $(CURDIR))) 59 | #--------------------------------------------------------------------------------- 60 | 61 | export OUTPUT := $(CURDIR)/$(TARGET) 62 | 63 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 64 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ 65 | $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) 66 | 67 | export DEPSDIR := $(CURDIR)/$(BUILD) 68 | 69 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 70 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 71 | BMPFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.bmp))) 72 | PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png))) 73 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 74 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 75 | 76 | #--------------------------------------------------------------------------------- 77 | # use CXX for linking C++ projects, CC for standard C 78 | #--------------------------------------------------------------------------------- 79 | ifeq ($(strip $(CPPFILES)),) 80 | #--------------------------------------------------------------------------------- 81 | export LD := $(CC) 82 | #--------------------------------------------------------------------------------- 83 | else 84 | #--------------------------------------------------------------------------------- 85 | export LD := $(CXX) 86 | #--------------------------------------------------------------------------------- 87 | endif 88 | #--------------------------------------------------------------------------------- 89 | 90 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 91 | $(BMPFILES:.bmp=.o) \ 92 | $(PNGFILES:.png=.o) \ 93 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 94 | 95 | export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir)) \ 96 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 97 | -I$(CURDIR)/$(BUILD) 98 | 99 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 100 | 101 | 102 | export OUTPUT := $(CURDIR)/$(TARGET) 103 | 104 | .PHONY: $(BUILD) clean 105 | 106 | all : $(BUILD) 107 | 108 | #--------------------------------------------------------------------------------- 109 | $(BUILD): 110 | @[ -d $@ ] || mkdir -p $@ 111 | @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 112 | #--------------------------------------------------------------------------------- 113 | clean: 114 | @echo clean ... 115 | @rm -fr $(BUILD) $(TARGET).elf $(TARGET).nds *.bin 116 | 117 | #--------------------------------------------------------------------------------- 118 | else 119 | 120 | DEPENDS := $(OFILES:.o=.d) 121 | 122 | #--------------------------------------------------------------------------------- 123 | # main targets 124 | #--------------------------------------------------------------------------------- 125 | $(OUTPUT).elf : $(OFILES) 126 | 127 | #--------------------------------------------------------------------------------- 128 | %.bin.o : %.bin 129 | #--------------------------------------------------------------------------------- 130 | @echo $(notdir $<) 131 | $(bin2o) 132 | 133 | #--------------------------------------------------------------------------------- 134 | # This rule creates assembly source files using grit 135 | # grit takes an image file and a .grit describing how the file is to be processed 136 | # add additional rules like this for each image extension 137 | # you use in the graphics folders 138 | #--------------------------------------------------------------------------------- 139 | %.s %.h : %.bmp %.grit 140 | #--------------------------------------------------------------------------------- 141 | grit $< -fts -o$* 142 | 143 | 144 | #--------------------------------------------------------------------------------- 145 | %.s %.h : %.png %.grit 146 | #--------------------------------------------------------------------------------- 147 | grit $< -fts -o$* 148 | 149 | -include $(DEPSDIR)/*.d 150 | 151 | #--------------------------------------------------------------------------------------- 152 | endif 153 | #--------------------------------------------------------------------------------------- 154 | -------------------------------------------------------------------------------- /arm9/ds_arm9_hi.mem: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------------- 2 | This Source Code Form is subject to the terms of the Mozilla Public License, 3 | v. 2.0. If a copy of the MPL was not distributed with this file, You can 4 | obtain one at https://mozilla.org/MPL/2.0/. 5 | --------------------------------------------------------------------------------*/ 6 | MEMORY { 7 | ewram : ORIGIN = 0x02004000, LENGTH = 3M + 512K - 0x4000 8 | dtcm : ORIGIN = 0x0b000000, LENGTH = 16K 9 | vectors : ORIGIN = 0x01000000, LENGTH = 256 10 | itcm : ORIGIN = 0x01000100, LENGTH = 32K - 256 11 | } 12 | -------------------------------------------------------------------------------- /arm9/ds_arm9_hi.specs: -------------------------------------------------------------------------------- 1 | %rename link old_link 2 | 3 | *link: 4 | %(old_link) -T ../ds_arm9_hi.mem%s -T ds_arm9.ld%s --gc-sections 5 | 6 | *startfile: 7 | ds_arm9_crt0%O%s crti%O%s crtbegin%O%s 8 | 9 | -------------------------------------------------------------------------------- /arm9/source/config.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONFIG_H 2 | #define __CONFIG_H 3 | 4 | // #define HW_FRAME_RENDERER 5 | // #define SW_FRAME_RENDERER 6 | #define SW_SCAN_RENDERER 7 | 8 | // #define _ASM_DRAW2_C 9 | 10 | // #define ARM9_SOUND 11 | 12 | // #define USE_ZLIB 13 | 14 | #define USE_EXTRA_RAM 15 | 16 | #endif // __CONFIG_H 17 | -------------------------------------------------------------------------------- /arm9/source/cyclone/Cyclone.h: -------------------------------------------------------------------------------- 1 | 2 | // Cyclone 68000 Emulator - Header File 3 | 4 | // Most code (c) Copyright 2004 Dave, All rights reserved. 5 | // Some coding/bugfixing was done by notaz 6 | // Cyclone 68000 is free for non-commercial use. 7 | 8 | // For commercial use, separate licencing terms must be obtained. 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | extern int CycloneVer; // Version number of library 15 | 16 | struct Cyclone 17 | { 18 | unsigned int d[8]; // [r7,#0x00] 19 | unsigned int a[8]; // [r7,#0x20] 20 | unsigned int pc; // [r7,#0x40] Memory Base+PC 21 | unsigned char srh; // [r7,#0x44] Status Register high (T_S__III) 22 | unsigned char xc; // [r7,#0x45] Extend flag (____??X?) 23 | unsigned char flags; // [r7,#0x46] Flags (ARM order: ____NZCV) [68k order is XNZVC] 24 | unsigned char irq; // [r7,#0x47] IRQ level 25 | unsigned int osp; // [r7,#0x48] Other Stack Pointer (USP/SSP) 26 | unsigned int vector; // [r7,#0x4c] IRQ vector (temporary) 27 | unsigned int pad1[2]; 28 | int stopped; // [r7,#0x58] 1 == processor is in stopped state 29 | int cycles; // [r7,#0x5c] 30 | int membase; // [r7,#0x60] Memory Base (ARM address minus 68000 address) 31 | unsigned int (*checkpc)(unsigned int pc); // [r7,#0x64] - Called to recalc Memory Base+pc 32 | unsigned char (*read8 )(unsigned int a); // [r7,#0x68] 33 | unsigned short (*read16 )(unsigned int a); // [r7,#0x6c] 34 | unsigned int (*read32 )(unsigned int a); // [r7,#0x70] 35 | void (*write8 )(unsigned int a,unsigned char d); // [r7,#0x74] 36 | void (*write16)(unsigned int a,unsigned short d); // [r7,#0x78] 37 | void (*write32)(unsigned int a,unsigned int d); // [r7,#0x7c] 38 | unsigned char (*fetch8 )(unsigned int a); // [r7,#0x80] 39 | unsigned short (*fetch16)(unsigned int a); // [r7,#0x84] 40 | unsigned int (*fetch32)(unsigned int a); // [r7,#0x88] 41 | void (*IrqCallback)(int int_level); // [r7,#0x8c] - optional irq callback function, see config.h 42 | void (*ResetCallback)(); // [r7,#0x90] - if enabled in config.h, calls this whenever RESET opcode is encountered. 43 | int (*UnrecognizedCallback)(); // [r7,#0x94] - if enabled in config.h, calls this whenever unrecognized opcode is encountered. 44 | }; 45 | 46 | // used only if Cyclone was compiled with compressed jumptable, see config.h 47 | void CycloneInit(); 48 | 49 | // run cyclone. Cycles should be specified in context (pcy->cycles) 50 | void CycloneRun(struct Cyclone *pcy); 51 | 52 | // utility functions to get and set SR 53 | void CycloneSetSr(struct Cyclone *pcy, unsigned int sr); // auto-swaps a7<->osp if detects supervisor change 54 | unsigned int CycloneGetSr(struct Cyclone *pcy); 55 | 56 | #ifdef __cplusplus 57 | } // End of extern "C" 58 | #endif 59 | -------------------------------------------------------------------------------- /arm9/source/externSound.cpp: -------------------------------------------------------------------------------- 1 | #include "externSound.h" 2 | 3 | #include "Pico/PicoInt.h" 4 | #include "streamingaudio.h" 5 | #include "string.h" 6 | #include "iniFile.h" 7 | #include "tonccpy.h" 8 | #include 9 | 10 | //#define MSL_NSONGS 0 11 | //#define MSL_NSAMPS 7 12 | //#define MSL_BANKSIZE 7 13 | 14 | 15 | extern volatile s16 fade_counter; 16 | extern volatile bool fade_out; 17 | 18 | extern volatile s16* play_stream_buf; 19 | extern volatile s16* fill_stream_buf; 20 | 21 | // Number of samples filled into the fill buffer so far. 22 | extern volatile s32 filled_samples; 23 | 24 | extern volatile bool fill_requested; 25 | extern volatile s32 samples_left_until_next_fill; 26 | extern volatile s32 streaming_buf_ptr; 27 | 28 | #define SAMPLES_USED (STREAMING_BUF_LENGTH - samples_left) 29 | #define REFILL_THRESHOLD STREAMING_BUF_LENGTH >> 2 30 | 31 | #ifdef SOUND_DEBUG 32 | extern char debug_buf[256]; 33 | #endif 34 | 35 | extern volatile u32 sample_delay_count; 36 | 37 | static bool musicLoopable = true; 38 | static bool loopingPoint = false; 39 | static bool loopingPointFound = false; 40 | bool streamFound = false; 41 | 42 | SoundControl::SoundControl() 43 | : stream_is_playing(false), stream_source(NULL), startup_sample_length(0) 44 | { 45 | 46 | /*sys.mod_count = MSL_NSONGS; 47 | sys.samp_count = MSL_NSAMPS; 48 | sys.mem_bank = SOUNDBANK; 49 | sys.fifo_channel = FIFO_MAXMOD; 50 | 51 | mmInit(&sys);*/ 52 | 53 | musicLoopable = true; 54 | loopingPoint = false; 55 | loopingPointFound = false; 56 | streamFound = false; 57 | } 58 | 59 | TWL_CODE void SoundControl::twl_loadStream(const char* filenameStart, const char* filename) { 60 | if (stream_source) { 61 | streamFound = false; 62 | fclose(stream_start_source); 63 | fclose(stream_source); 64 | } 65 | 66 | loopingPoint = false; 67 | loopingPointFound = false; 68 | 69 | if (strcmp(filenameStart, "") != 0) { 70 | stream_start_source = fopen(filenameStart, "rb"); 71 | loopingPointFound = (stream_start_source); 72 | } 73 | 74 | stream_source = fopen(filename, "rb"); 75 | if (!stream_source) return; 76 | 77 | resetStreamSettings(); 78 | 79 | stream.sampling_rate = 32000; // 32000Hz 80 | stream.buffer_length = 800; // should be adequate 81 | stream.callback = on_stream_request; 82 | stream.format = MM_STREAM_16BIT_MONO; // select format 83 | stream.timer = MM_TIMER0; // use timer0 84 | stream.manual = false; // auto filling 85 | streamFound = true; 86 | 87 | if (loopingPointFound) { 88 | fseek(stream_start_source, 0, SEEK_END); 89 | size_t fileSize = ftell(stream_start_source); 90 | fseek(stream_start_source, 0, SEEK_SET); 91 | 92 | // Prep the first section of the stream 93 | fread((void*)play_stream_buf, sizeof(s16), STREAMING_BUF_LENGTH, stream_start_source); 94 | if (fileSize < STREAMING_BUF_LENGTH*sizeof(s16)) { 95 | size_t fillerSize = 0; 96 | while (fileSize+fillerSize < STREAMING_BUF_LENGTH*sizeof(s16)) { 97 | fillerSize++; 98 | } 99 | fread((void*)play_stream_buf+fileSize, 1, fillerSize, stream_source); 100 | 101 | // Fill the next section premptively 102 | fread((void*)fill_stream_buf, sizeof(s16), STREAMING_BUF_LENGTH, stream_source); 103 | 104 | loopingPoint = true; 105 | } else { 106 | // Fill the next section premptively 107 | fread((void*)fill_stream_buf, sizeof(s16), STREAMING_BUF_LENGTH, stream_start_source); 108 | fileSize -= STREAMING_BUF_LENGTH*sizeof(s16); 109 | if (fileSize < STREAMING_BUF_LENGTH*sizeof(s16)) { 110 | size_t fillerSize = 0; 111 | while (fileSize+fillerSize < STREAMING_BUF_LENGTH*sizeof(s16)) { 112 | fillerSize++; 113 | } 114 | fread((void*)fill_stream_buf+fileSize, 1, fillerSize, stream_source); 115 | 116 | loopingPoint = true; 117 | } 118 | } 119 | } else { 120 | // Prep the first section of the stream 121 | fread((void*)play_stream_buf, sizeof(s16), STREAMING_BUF_LENGTH, stream_source); 122 | 123 | // Fill the next section premptively 124 | fread((void*)fill_stream_buf, sizeof(s16), STREAMING_BUF_LENGTH, stream_source); 125 | 126 | loopingPoint = true; 127 | } 128 | 129 | streamFound = true; 130 | } 131 | void SoundControl::loadStream(const char* filenameStart, const char* filename) { 132 | if (!isDSiMode()) return; 133 | snd().twl_loadStream(filenameStart, filename); 134 | } 135 | 136 | void SoundControl::beginStream() { 137 | if (!streamFound || stream_is_playing) return; 138 | 139 | // open the stream 140 | stream_is_playing = true; 141 | mmStreamOpen(&stream); 142 | SetYtrigger(0); 143 | } 144 | 145 | void SoundControl::stopStream() { 146 | if (!streamFound || !stream_is_playing) return; 147 | 148 | stream_is_playing = false; 149 | mmStreamClose(); 150 | } 151 | 152 | void SoundControl::closeStream() { 153 | if (!streamFound || !stream_is_playing) return; 154 | 155 | streamFound = false; 156 | stream_is_playing = false; 157 | mmStreamClose(); 158 | } 159 | 160 | TWL_CODE void SoundControl::twl_resetStream() { 161 | if (!streamFound) return; 162 | 163 | resetStreamSettings(); 164 | 165 | fseek(stream_start_source, 0, SEEK_SET); 166 | fseek(stream_source, 0, SEEK_SET); 167 | 168 | // Prep the first section of the stream 169 | fread((void*)play_stream_buf, sizeof(s16), STREAMING_BUF_LENGTH, loopingPointFound ? stream_start_source : stream_source); 170 | 171 | // Fill the next section premptively 172 | fread((void*)fill_stream_buf, sizeof(s16), STREAMING_BUF_LENGTH, loopingPointFound ? stream_start_source : stream_source); 173 | } 174 | void SoundControl::resetStream() { 175 | if (!isDSiMode()) return; 176 | snd().twl_resetStream(); 177 | } 178 | 179 | void SoundControl::fadeOutStream() { 180 | fade_out = true; 181 | } 182 | 183 | void SoundControl::cancelFadeOutStream() { 184 | fade_out = false; 185 | fade_counter = FADE_STEPS; 186 | } 187 | 188 | void SoundControl::setStreamDelay(u32 delay) { 189 | sample_delay_count = delay; 190 | } 191 | 192 | 193 | // Samples remaining in the fill buffer. 194 | #define SAMPLES_LEFT_TO_FILL (abs(STREAMING_BUF_LENGTH - filled_samples)) 195 | 196 | // Samples that were already streamed and need to be refilled into the buffer. 197 | #define SAMPLES_TO_FILL (abs(streaming_buf_ptr - filled_samples)) 198 | 199 | // Updates the background music fill buffer 200 | // Fill the amount of samples that were used up between the 201 | // last fill request and this. 202 | 203 | // Precondition Invariants: 204 | // filled_samples <= STREAMING_BUF_LENGTH 205 | // filled_samples <= streaming_buf_ptr 206 | 207 | // Postcondition Invariants: 208 | // filled_samples <= STREAMING_BUF_LENGTH 209 | // filled_samples <= streaming_buf_ptr 210 | // fill_requested == false 211 | TWL_CODE volatile void SoundControl::twl_updateStream() { 212 | 213 | if (!streamFound || !stream_is_playing) return; 214 | if (fill_requested && filled_samples < STREAMING_BUF_LENGTH) { 215 | 216 | // Reset the fill request 217 | fill_requested = false; 218 | int instance_filled = 0; 219 | 220 | // Either fill the max amount, or fill up the buffer as much as possible. 221 | int instance_to_fill = std::min(SAMPLES_LEFT_TO_FILL, SAMPLES_TO_FILL); 222 | 223 | // If we don't read enough samples, loop from the beginning of the file. 224 | instance_filled = fread((s16*)fill_stream_buf + filled_samples, sizeof(s16), instance_to_fill, loopingPoint ? stream_source : stream_start_source); 225 | if (instance_filled < instance_to_fill) { 226 | if (musicLoopable) fseek(stream_source, 0, SEEK_SET); 227 | int i = fread((s16*)fill_stream_buf + filled_samples + instance_filled, 228 | sizeof(s16), (instance_to_fill - instance_filled), stream_source); 229 | if (i==0) { 230 | toncset((s16*)fill_stream_buf + filled_samples + instance_filled, 0, (instance_to_fill - instance_filled)*sizeof(s16)); 231 | } else { 232 | instance_filled += i; 233 | } 234 | loopingPoint = true; 235 | } 236 | 237 | #ifdef SOUND_DEBUG 238 | sprintf(debug_buf, "FC: SAMPLES_LEFT_TO_FILL: %li, SAMPLES_TO_FILL: %li, instance_filled: %i, filled_samples %li, to_fill: %i", SAMPLES_LEFT_TO_FILL, SAMPLES_TO_FILL, instance_filled, filled_samples, instance_to_fill); 239 | nocashMessage(debug_buf); 240 | #endif 241 | 242 | // maintain invariant 0 < filled_samples <= STREAMING_BUF_LENGTH 243 | filled_samples = std::min(filled_samples + instance_filled, STREAMING_BUF_LENGTH); 244 | 245 | 246 | } else if (fill_requested && filled_samples >= STREAMING_BUF_LENGTH) { 247 | // filled_samples == STREAMING_BUF_LENGTH is the only possible case 248 | // but we'll keep it at gte to be safe. 249 | filled_samples = 0; 250 | // fill_count = 0; 251 | } 252 | 253 | } 254 | volatile void SoundControl::updateStream() { 255 | if (!isDSiMode()) return; 256 | snd().twl_updateStream(); 257 | } 258 | 259 | extern bool playSound; 260 | extern bool soundPaused; 261 | extern mm_sfxhand sndHandlers[48]; 262 | static u8 currentMusId = 0; 263 | static u8 prevSndId = 0; 264 | 265 | extern u8 musFirstID; 266 | extern u8 musLastID; 267 | 268 | extern u8 sndFirstID; 269 | extern u8 sndLastID; 270 | extern u8 sndStopID; 271 | 272 | extern u8 pauseID; 273 | extern u8 unpauseID; 274 | 275 | extern u16 snd68000addr[2]; 276 | extern u16 sndZ80addr[2]; 277 | 278 | extern u16 pause68000addr; 279 | extern u16 pauseZ80addr; 280 | 281 | extern char sndFilePath[3][256]; 282 | static char musFilePath[2][256] = {0}; 283 | 284 | bool MusicPlayRAM(void) { 285 | if (!playSound || snd68000addr[0]==0) return false; 286 | 287 | u8 soundId = 0; 288 | if (Pico.ram[snd68000addr[0]] >= musFirstID && Pico.ram[snd68000addr[0]] <= musLastID) { 289 | soundId = Pico.ram[snd68000addr[0]]; 290 | } else if (Pico.ram[snd68000addr[1]] >= musFirstID && Pico.ram[snd68000addr[1]] <= musLastID) { 291 | soundId = Pico.ram[snd68000addr[1]]; 292 | } 293 | if (soundId==0 || currentMusId==soundId) { 294 | return false; 295 | } 296 | snd().stopStream(); 297 | currentMusId = soundId; 298 | 299 | //printf ("\x1b[22;1H"); 300 | //printf ("Now Loading..."); 301 | 302 | char musicIdText[16]; 303 | sprintf(musicIdText, "MUSIC-%X", soundId); 304 | 305 | CIniFile soundSettings(sndFilePath[0]); 306 | musicLoopable = soundSettings.GetInt(musicIdText, "Loop", true); 307 | 308 | // External sound 309 | snprintf(musFilePath[0], 256, "%s%X_start.raw", sndFilePath[2], soundId); 310 | snprintf(musFilePath[1], 256, "%s%X.raw", sndFilePath[2], soundId); 311 | snd().loadStream(musFilePath[0], musFilePath[1]); 312 | 313 | //printf ("\x1b[22;1H"); 314 | //printf (" "); 315 | 316 | snd().beginStream(); 317 | 318 | return true; 319 | } 320 | 321 | void SoundPlayRAM(void) { 322 | if (!playSound || snd68000addr[0]==0) return; 323 | 324 | if (pause68000addr != 0) { 325 | if (Pico.ram[pause68000addr] == pauseID && !soundPaused) { 326 | snd().stopStream(); 327 | mmEffectCancelAll(); 328 | soundPaused = true; 329 | return; 330 | } else if (Pico.ram[pause68000addr] == unpauseID && soundPaused) { 331 | snd().beginStream(); 332 | soundPaused = false; 333 | return; 334 | } 335 | } 336 | 337 | if (Pico.ram[snd68000addr[0]] == sndStopID || Pico.ram[snd68000addr[1]] == sndStopID) { 338 | snd().closeStream(); 339 | mmEffectCancelAll(); 340 | return; 341 | } 342 | 343 | u8 soundId = 0; 344 | if (Pico.ram[snd68000addr[0]] >= sndFirstID && Pico.ram[snd68000addr[0]] <= sndLastID) { 345 | soundId = Pico.ram[snd68000addr[0]]; 346 | } else if (Pico.ram[snd68000addr[1]] >= sndFirstID && Pico.ram[snd68000addr[1]] <= sndLastID) { 347 | soundId = Pico.ram[snd68000addr[1]]; 348 | } 349 | if (soundId==0) return; 350 | 351 | soundId -= sndFirstID; 352 | 353 | // External sound 354 | if (sndHandlers[prevSndId] != NULL) 355 | mmEffectRelease( sndHandlers[prevSndId] ); 356 | sndHandlers[soundId] = mmEffect(soundId); 357 | 358 | prevSndId = soundId; 359 | } 360 | 361 | void SoundPlayZ80(void) { 362 | if (!playSound || sndZ80addr[0]==0) return; 363 | 364 | if (pauseZ80addr != 0) { 365 | if (Pico.zram[pauseZ80addr] == pauseID && !soundPaused) { 366 | snd().stopStream(); 367 | mmEffectCancelAll(); 368 | soundPaused = true; 369 | return; 370 | } else if (Pico.zram[pauseZ80addr] == unpauseID && soundPaused) { 371 | snd().beginStream(); 372 | soundPaused = false; 373 | return; 374 | } 375 | } 376 | 377 | u8 soundId = 0; 378 | if (Pico.zram[sndZ80addr[0]] >= sndFirstID && Pico.zram[sndZ80addr[0]] <= sndLastID) { 379 | soundId = Pico.zram[sndZ80addr[0]]; 380 | } else if (Pico.zram[sndZ80addr[1]] >= sndFirstID && Pico.zram[sndZ80addr[1]] <= sndLastID) { 381 | soundId = Pico.zram[sndZ80addr[1]]; 382 | } 383 | if (soundId==0) return; 384 | 385 | soundId -= sndFirstID; 386 | 387 | // External sound 388 | if (sndHandlers[prevSndId] != NULL) 389 | mmEffectRelease( sndHandlers[prevSndId] ); 390 | sndHandlers[soundId] = mmEffect(soundId); 391 | 392 | prevSndId = soundId; 393 | } 394 | -------------------------------------------------------------------------------- /arm9/source/externSound.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef __PICOTWL_SOUND__ 3 | #define __PICOTWL_SOUND__ 4 | #include 5 | #include 6 | #include 7 | #include "singleton.h" 8 | #include 9 | 10 | /* 11 | * Handles playing sound effects and the streaming background music control. 12 | * See streamingaudio.c for a technical overview of how streaming works. 13 | */ 14 | class SoundControl { 15 | public: 16 | SoundControl(); 17 | 18 | // Refill the stream buffers 19 | volatile void twl_updateStream(); 20 | volatile void updateStream(); 21 | 22 | void twl_loadStream(const char* filenameStart, const char* filename); 23 | void loadStream(const char* filenameStart, const char* filename); 24 | void beginStream(); 25 | void stopStream(); 26 | void closeStream(); 27 | void twl_resetStream(); 28 | void resetStream(); 29 | void fadeOutStream(); 30 | void cancelFadeOutStream(); 31 | 32 | // Sets the number of samples of silence to 33 | // stream before continuing. 34 | void setStreamDelay(u32 stream_delay); 35 | 36 | u32 getStartupSoundLength() { return startup_sample_length; } 37 | 38 | private: 39 | mm_stream stream; 40 | mm_ds_system sys; 41 | bool stream_is_playing; 42 | FILE* stream_start_source; 43 | FILE* stream_source; 44 | u32 startup_sample_length; 45 | }; 46 | 47 | typedef singleton soundCtl_s; 48 | inline SoundControl &snd() { return soundCtl_s::instance(); } 49 | 50 | extern bool MusicPlayRAM(void); 51 | extern void SoundPlayRAM(void); 52 | extern void SoundPlayZ80(void); 53 | 54 | #endif -------------------------------------------------------------------------------- /arm9/source/file.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "pico/PicoInt.h" 4 | #include "tonccpy.h" 5 | 6 | #define cacheAmount 4 7 | 8 | extern char* romSpace; 9 | 10 | char fileName[256]; 11 | 12 | static int cachedPages[cacheAmount] = {0}; 13 | static int currentPage = 0; 14 | 15 | void initCachedPages(void) { 16 | for (int i = 0; i < cacheAmount; i++) { 17 | cachedPages[i] = 0; 18 | } 19 | } 20 | 21 | void loadRomBank(int page, int i) { 22 | extern FILE* romfile; 23 | extern bool UsingExtendedMemory; 24 | 25 | if(!UsingExtendedMemory) return; 26 | 27 | if (page == 0) { 28 | // Read first-loaded bytes 29 | if (isDSiMode()) { 30 | DC_FlushAll(); 31 | dmaCopyWords(0, Pico.rom, Pico.rom+0x80000+(i*0x80000), 0x80000); 32 | } else { 33 | tonccpy(Pico.rom+0x80000+(i*0x80000), Pico.rom, 0x80000); 34 | } 35 | return; 36 | } 37 | 38 | for (int i2 = 0; i2 < cacheAmount; i2++) { 39 | if (cachedPages[i2] == page) { 40 | if (isDSiMode()) { 41 | DC_FlushAll(); 42 | dmaCopyWords(0, (char*)romSpace+(i2*0x80000), Pico.rom+0x80000+(i*0x80000), 0x80000); 43 | } else { 44 | tonccpy(Pico.rom+0x80000+(i*0x80000), (char*)romSpace+(i2*0x80000), 0x80000); 45 | } 46 | return; 47 | } 48 | } 49 | 50 | char* bankCache = (char*)romSpace+(currentPage*0x80000); 51 | 52 | romfile = fopen(fileName, "rb"); 53 | 54 | u32 size = 0; 55 | fseek(romfile,0,SEEK_END); 56 | size = ftell(romfile); 57 | 58 | fseek(romfile, page*0x80000, SEEK_SET); 59 | fread(bankCache, 1, 0x80000, romfile); 60 | fclose(romfile); 61 | 62 | // Check for SMD: 63 | if ((size&0x3fff)==0x200) { 64 | DecodeSmd((unsigned char*)bankCache,0x80000); 65 | } // Decode and byteswap SMD 66 | else Byteswap((unsigned char*)bankCache,0x80000); // Just byteswap 67 | 68 | if (isDSiMode()) { 69 | DC_FlushAll(); 70 | dmaCopyWords(0, bankCache, Pico.rom+0x80000+(i*0x80000), 0x80000); 71 | } else { 72 | tonccpy(Pico.rom+0x80000+(i*0x80000), bankCache, 0x80000); 73 | } 74 | cachedPages[currentPage] = page; 75 | currentPage++; 76 | if (currentPage >= cacheAmount) currentPage = 0; 77 | 78 | /*iprintf("\x1b[14;0HFile : %s",fileName); 79 | iprintf("\x1b[17;0HPage : %d ",page); 80 | iprintf("\x1b[18;0HROM src: %X ",(int)page*0x80000); 81 | iprintf("\x1b[19;0HROM dst: %X ",(int)0x80000+(i*0x80000)); 82 | iprintf("\x1b[20;0HRAM dst: %X ",(int)Pico.rom+0x80000+(i*0x80000));*/ 83 | } 84 | -------------------------------------------------------------------------------- /arm9/source/file.h: -------------------------------------------------------------------------------- 1 | #ifndef __PICOTWL_FILE__ 2 | #define __PICOTWL_FILE__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | extern char fileName[256]; 9 | 10 | extern void loadRomBank(int page, int i); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | #endif -------------------------------------------------------------------------------- /arm9/source/file_browse.cpp: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | Copyright (C) 2005 - 2017 3 | Michael "Chishm" Chisholm 4 | Dave "WinterMute" Murphy 5 | Claudio "sverx" 6 | 7 | This program is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU General Public License 9 | as published by the Free Software Foundation; either version 2 10 | of the License, or (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 | 21 | ------------------------------------------------------------------*/ 22 | 23 | #include "file_browse.h" 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | #define SCREEN_COLS 32 34 | #define ENTRIES_PER_SCREEN 22 35 | #define ENTRIES_START_ROW 2 36 | #define ENTRY_PAGE_LENGTH 10 37 | 38 | using namespace std; 39 | 40 | struct DirEntry { 41 | string name; 42 | bool isDirectory; 43 | } ; 44 | 45 | bool nameEndsWith (const string& name, const vector extensionList) { 46 | 47 | if (name.size() == 0) return false; 48 | 49 | if (extensionList.size() == 0) return true; 50 | 51 | for (int i = 0; i < (int)extensionList.size(); i++) { 52 | const string ext = extensionList.at(i); 53 | if ( strcasecmp (name.c_str() + name.size() - ext.size(), ext.c_str()) == 0) return true; 54 | } 55 | return false; 56 | } 57 | 58 | bool dirEntryPredicate (const DirEntry& lhs, const DirEntry& rhs) { 59 | 60 | if (!lhs.isDirectory && rhs.isDirectory) { 61 | return false; 62 | } 63 | if (lhs.isDirectory && !rhs.isDirectory) { 64 | return true; 65 | } 66 | return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0; 67 | } 68 | 69 | void getDirectoryContents (vector& dirContents, const vector extensionList) { 70 | struct stat st; 71 | 72 | dirContents.clear(); 73 | 74 | DIR *pdir = opendir ("."); 75 | 76 | if (pdir == NULL) { 77 | iprintf ("Unable to open the directory.\n"); 78 | } else { 79 | 80 | while(true) { 81 | DirEntry dirEntry; 82 | 83 | struct dirent* pent = readdir(pdir); 84 | if(pent == NULL) break; 85 | 86 | stat(pent->d_name, &st); 87 | dirEntry.name = pent->d_name; 88 | dirEntry.isDirectory = (st.st_mode & S_IFDIR) ? true : false; 89 | 90 | if (dirEntry.name.compare(".") != 0 && (dirEntry.isDirectory || nameEndsWith(dirEntry.name, extensionList))) { 91 | dirContents.push_back (dirEntry); 92 | } 93 | 94 | } 95 | 96 | closedir(pdir); 97 | } 98 | 99 | sort(dirContents.begin(), dirContents.end(), dirEntryPredicate); 100 | } 101 | 102 | void getDirectoryContents (vector& dirContents) { 103 | vector extensionList; 104 | getDirectoryContents (dirContents, extensionList); 105 | } 106 | 107 | void showDirectoryContents (const vector& dirContents, int startRow) { 108 | char path[PATH_MAX]; 109 | 110 | 111 | getcwd(path, PATH_MAX); 112 | 113 | // Clear the screen 114 | iprintf ("\x1b[2J"); 115 | 116 | // Print the path 117 | if (strlen(path) < SCREEN_COLS) { 118 | iprintf ("%s", path); 119 | } else { 120 | iprintf ("%s", path + strlen(path) - SCREEN_COLS); 121 | } 122 | 123 | // Move to 2nd row 124 | iprintf ("\x1b[1;0H"); 125 | // Print line of dashes 126 | iprintf ("--------------------------------"); 127 | 128 | // Print directory listing 129 | for (int i = 0; i < ((int)dirContents.size() - startRow) && i < ENTRIES_PER_SCREEN; i++) { 130 | const DirEntry* entry = &dirContents.at(i + startRow); 131 | char entryName[SCREEN_COLS + 1]; 132 | 133 | // Set row 134 | iprintf ("\x1b[%d;0H", i + ENTRIES_START_ROW); 135 | 136 | if (entry->isDirectory) { 137 | strncpy (entryName, entry->name.c_str(), SCREEN_COLS); 138 | entryName[SCREEN_COLS - 3] = '\0'; 139 | iprintf (" [%s]", entryName); 140 | } else { 141 | strncpy (entryName, entry->name.c_str(), SCREEN_COLS); 142 | entryName[SCREEN_COLS - 1] = '\0'; 143 | iprintf (" %s", entryName); 144 | } 145 | } 146 | } 147 | 148 | string browseForFile (const vector& extensionList) { 149 | int pressed = 0; 150 | int screenOffset = 0; 151 | int fileOffset = 0; 152 | vector dirContents; 153 | 154 | getDirectoryContents (dirContents, extensionList); 155 | showDirectoryContents (dirContents, screenOffset); 156 | 157 | while (true) { 158 | // Clear old cursors 159 | for (int i = ENTRIES_START_ROW; i < ENTRIES_PER_SCREEN + ENTRIES_START_ROW; i++) { 160 | iprintf ("\x1b[%d;0H ", i); 161 | } 162 | // Show cursor 163 | iprintf ("\x1b[%d;0H*", fileOffset - screenOffset + ENTRIES_START_ROW); 164 | 165 | // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do 166 | do { 167 | scanKeys(); 168 | pressed = keysDownRepeat(); 169 | swiWaitForVBlank(); 170 | } while (!pressed); 171 | 172 | if (pressed & KEY_UP) fileOffset -= 1; 173 | if (pressed & KEY_DOWN) fileOffset += 1; 174 | if (pressed & KEY_LEFT) fileOffset -= ENTRY_PAGE_LENGTH; 175 | if (pressed & KEY_RIGHT) fileOffset += ENTRY_PAGE_LENGTH; 176 | 177 | if (fileOffset < 0) fileOffset = dirContents.size() - 1; // Wrap around to bottom of list 178 | if (fileOffset > ((int)dirContents.size() - 1)) fileOffset = 0; // Wrap around to top of list 179 | 180 | // Scroll screen if needed 181 | if (fileOffset < screenOffset) { 182 | screenOffset = fileOffset; 183 | showDirectoryContents (dirContents, screenOffset); 184 | } 185 | if (fileOffset > screenOffset + ENTRIES_PER_SCREEN - 1) { 186 | screenOffset = fileOffset - ENTRIES_PER_SCREEN + 1; 187 | showDirectoryContents (dirContents, screenOffset); 188 | } 189 | 190 | if (pressed & KEY_A) { 191 | DirEntry* entry = &dirContents.at(fileOffset); 192 | if (entry->isDirectory) { 193 | iprintf("Entering directory\n"); 194 | // Enter selected directory 195 | chdir (entry->name.c_str()); 196 | getDirectoryContents (dirContents, extensionList); 197 | screenOffset = 0; 198 | fileOffset = 0; 199 | showDirectoryContents (dirContents, screenOffset); 200 | } else { 201 | // Clear the screen 202 | iprintf ("\x1b[2J"); 203 | // Return the chosen file 204 | return entry->name; 205 | } 206 | } 207 | 208 | if (pressed & KEY_B) { 209 | // Go up a directory 210 | chdir (".."); 211 | getDirectoryContents (dirContents, extensionList); 212 | screenOffset = 0; 213 | fileOffset = 0; 214 | showDirectoryContents (dirContents, screenOffset); 215 | } 216 | 217 | if (pressed & KEY_SELECT) { 218 | return "NULL"; 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /arm9/source/file_browse.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | Copyright (C) 2005 - 2017 3 | Michael "Chishm" Chisholm 4 | Dave "WinterMute" Murphy 5 | 6 | This program is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU General Public License 8 | as published by the Free Software Foundation; either version 2 9 | of the License, or (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | 20 | ------------------------------------------------------------------*/ 21 | 22 | #ifndef FILE_BROWSE_H 23 | #define FILE_BROWSE_H 24 | 25 | #include 26 | #include 27 | 28 | std::string browseForFile (const std::vector& extensionList); 29 | 30 | 31 | 32 | #endif //FILE_BROWSE_H 33 | -------------------------------------------------------------------------------- /arm9/source/inifile.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | inifile.cpp 3 | Copyright (C) 2007 Acekard, www.acekard.com 4 | Copyright (C) 2007-2009 somebody 5 | Copyright (C) 2009 yellow wood goblin 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include "inifile.h" 24 | #include "stringtool.h" 25 | 26 | static bool freadLine(FILE *f, std::string &str) 27 | { 28 | str.clear(); 29 | __read: 30 | char p = 0; 31 | 32 | size_t readed = fread(&p, 1, 1, f); 33 | if (0 == readed) 34 | { 35 | str = ""; 36 | return false; 37 | } 38 | if ('\n' == p || '\r' == p) 39 | { 40 | str = ""; 41 | return true; 42 | } 43 | 44 | while (p != '\n' && p != '\r' && readed) 45 | { 46 | str += p; 47 | readed = fread(&p, 1, 1, f); 48 | } 49 | 50 | if (str.empty() || "" == str) 51 | { 52 | goto __read; 53 | } 54 | 55 | return true; 56 | } 57 | 58 | static void trimString(std::string &str) 59 | { 60 | size_t first = str.find_first_not_of(" \t"), last; 61 | if (first == str.npos) 62 | { 63 | str = ""; 64 | } 65 | else 66 | { 67 | last = str.find_last_not_of(" \t"); 68 | if (first > 0 || (last + 1) < str.length()) 69 | str = str.substr(first, last - first + 1); 70 | } 71 | } 72 | 73 | CIniFile::CIniFile() 74 | { 75 | m_bLastResult = false; 76 | m_bModified = false; 77 | m_bReadOnly = false; 78 | } 79 | 80 | CIniFile::CIniFile(const std::string &filename) 81 | { 82 | m_sFileName = filename; 83 | m_bLastResult = false; 84 | m_bModified = false; 85 | m_bReadOnly = false; 86 | LoadIniFile(m_sFileName); 87 | } 88 | 89 | CIniFile::~CIniFile() 90 | { 91 | if (m_FileContainer.size() > 0) 92 | { 93 | m_FileContainer.clear(); 94 | } 95 | } 96 | 97 | void CIniFile::SetString(const std::string &Section, const std::string &Item, const std::string &Value) 98 | { 99 | if (GetFileString(Section, Item) != Value) 100 | { 101 | SetFileString(Section, Item, Value); 102 | m_bModified = true; 103 | } 104 | } 105 | 106 | void CIniFile::SetInt(const std::string &Section, const std::string &Item, int Value) 107 | { 108 | std::string strtemp = formatString("%d", Value); 109 | 110 | if (GetFileString(Section, Item) != strtemp) 111 | { 112 | SetFileString(Section, Item, strtemp); 113 | m_bModified = true; 114 | } 115 | } 116 | 117 | std::string CIniFile::GetString(const std::string &Section, const std::string &Item) 118 | { 119 | return GetFileString(Section, Item); 120 | } 121 | 122 | std::string CIniFile::GetString(const std::string &Section, const std::string &Item, const std::string &DefaultValue) 123 | { 124 | std::string temp = GetString(Section, Item); 125 | if (!m_bLastResult) 126 | { 127 | SetString(Section, Item, DefaultValue); 128 | temp = DefaultValue; 129 | } 130 | return temp; 131 | } 132 | 133 | void CIniFile::GetStringVector(const std::string &Section, const std::string &Item, std::vector &strings, char delimiter) 134 | { 135 | std::string strValue = GetFileString(Section, Item); 136 | strings.clear(); 137 | size_t pos; 138 | while ((pos = strValue.find(delimiter), strValue.npos != pos)) 139 | { 140 | const std::string string = strValue.substr(0, pos); 141 | if (string.length()) 142 | { 143 | strings.push_back(string); 144 | } 145 | strValue = strValue.substr(pos + 1, strValue.npos); 146 | } 147 | if (strValue.length()) 148 | { 149 | strings.push_back(strValue); 150 | } 151 | } 152 | 153 | void CIniFile::SetStringVector(const std::string &Section, const std::string &Item, std::vector &strings, char delimiter) 154 | { 155 | std::string strValue; 156 | for (size_t ii = 0; ii < strings.size(); ++ii) 157 | { 158 | if (ii) 159 | strValue += delimiter; 160 | strValue += strings[ii]; 161 | } 162 | SetString(Section, Item, strValue); 163 | } 164 | 165 | int CIniFile::GetInt(const std::string &Section, const std::string &Item) 166 | { 167 | std::string value = GetFileString(Section, Item); 168 | if (value.size() > 2 && '0' == value[0] && ('x' == value[1] || 'X' == value[1])) 169 | return strtol(value.c_str(), NULL, 16); 170 | else 171 | return strtol(value.c_str(), NULL, 10); 172 | } 173 | 174 | int CIniFile::GetInt(const std::string &Section, const std::string &Item, int DefaultValue) 175 | { 176 | int temp; 177 | temp = GetInt(Section, Item); 178 | if (!m_bLastResult) 179 | { 180 | SetInt(Section, Item, DefaultValue); 181 | temp = DefaultValue; 182 | } 183 | return temp; 184 | } 185 | 186 | bool CIniFile::LoadIniFile(const std::string &FileName) 187 | { 188 | //dbg_printf("load %s\n",FileName.c_str()); 189 | if (FileName != "") 190 | m_sFileName = FileName; 191 | 192 | FILE *f = fopen(FileName.c_str(), "rb"); 193 | 194 | if (NULL == f) 195 | return false; 196 | 197 | //check for utf8 bom. 198 | char bom[3]; 199 | if (fread(bom, 3, 1, f) == 1 && bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) 200 | ; 201 | else 202 | fseek(f, 0, SEEK_SET); 203 | 204 | std::string strline(""); 205 | m_FileContainer.clear(); 206 | 207 | while (freadLine(f, strline)) 208 | { 209 | trimString(strline); 210 | if (strline != "" && ';' != strline[0] && '/' != strline[0] && '!' != strline[0]) 211 | m_FileContainer.push_back(strline); 212 | } 213 | 214 | fclose(f); 215 | 216 | m_bLastResult = false; 217 | m_bModified = false; 218 | 219 | return true; 220 | } 221 | 222 | bool CIniFile::SaveIniFileModified(const std::string &FileName) 223 | { 224 | if (m_bModified == true) 225 | { 226 | return SaveIniFile(FileName); 227 | } 228 | 229 | return true; 230 | } 231 | 232 | bool CIniFile::SaveIniFile(const std::string &FileName) 233 | { 234 | if (FileName != "") 235 | m_sFileName = FileName; 236 | 237 | FILE *f = fopen(m_sFileName.c_str(), "wb"); 238 | if (NULL == f) 239 | { 240 | return false; 241 | } 242 | 243 | for (size_t ii = 0; ii < m_FileContainer.size(); ii++) 244 | { 245 | std::string &strline = m_FileContainer[ii]; 246 | size_t notSpace = strline.find_first_not_of(' '); 247 | strline = strline.substr(notSpace); 248 | if (strline.find('[') == 0 && ii > 0) 249 | { 250 | if (!m_FileContainer[ii - 1].empty() && m_FileContainer[ii - 1] != "") 251 | fwrite("\r\n", 1, 2, f); 252 | } 253 | if (!strline.empty() && strline != "") 254 | { 255 | fwrite(strline.c_str(), 1, strline.length(), f); 256 | fwrite("\r\n", 1, 2, f); 257 | } 258 | } 259 | 260 | fclose(f); 261 | 262 | m_bModified = false; 263 | 264 | return true; 265 | } 266 | 267 | std::string CIniFile::GetFileString(const std::string &Section, const std::string &Item) 268 | { 269 | std::string strline; 270 | std::string strSection; 271 | std::string strItem; 272 | std::string strValue; 273 | 274 | size_t ii = 0; 275 | size_t iFileLines = m_FileContainer.size(); 276 | 277 | if (m_bReadOnly) 278 | { 279 | SectionCache::iterator it = m_Cache.find(Section); 280 | if ((it != m_Cache.end())) 281 | ii = it->second; 282 | } 283 | 284 | m_bLastResult = false; 285 | 286 | if (iFileLines >= 0) 287 | { 288 | while (ii < iFileLines) 289 | { 290 | strline = m_FileContainer[ii++]; 291 | 292 | size_t rBracketPos = 0; 293 | if ('[' == strline[0]) 294 | rBracketPos = strline.find(']'); 295 | if (rBracketPos > 0 && rBracketPos != std::string::npos) 296 | { 297 | strSection = strline.substr(1, rBracketPos - 1); 298 | if (m_bReadOnly) 299 | m_Cache.insert(std::make_pair(strSection, ii - 1)); 300 | if (strSection == Section) 301 | { 302 | while (ii < iFileLines) 303 | { 304 | strline = m_FileContainer[ii++]; 305 | size_t equalsignPos = strline.find('='); 306 | if (equalsignPos != strline.npos) 307 | { 308 | size_t last = equalsignPos ? strline.find_last_not_of(" \t", equalsignPos - 1) : strline.npos; 309 | if (last == strline.npos) 310 | strItem = ""; 311 | else 312 | strItem = strline.substr(0, last + 1); 313 | 314 | if (strItem == Item) 315 | { 316 | size_t first = strline.find_first_not_of(" \t", equalsignPos + 1); 317 | if (first == strline.npos) 318 | strValue = ""; 319 | else 320 | strValue = strline.substr(first); 321 | m_bLastResult = true; 322 | return strValue; 323 | } 324 | } 325 | else if ('[' == strline[0]) 326 | { 327 | break; 328 | } 329 | } 330 | break; 331 | } 332 | } 333 | } 334 | } 335 | return std::string(""); 336 | } 337 | 338 | void CIniFile::SetFileString(const std::string &Section, const std::string &Item, const std::string &Value) 339 | { 340 | std::string strline; 341 | std::string strSection; 342 | std::string strItem; 343 | 344 | if (m_bReadOnly) 345 | return; 346 | 347 | size_t ii = 0; 348 | size_t iFileLines = m_FileContainer.size(); 349 | 350 | while (ii < iFileLines) 351 | { 352 | strline = m_FileContainer[ii++]; 353 | 354 | size_t rBracketPos = 0; 355 | if ('[' == strline[0]) 356 | rBracketPos = strline.find(']'); 357 | if (rBracketPos > 0 && rBracketPos != std::string::npos) 358 | { 359 | strSection = strline.substr(1, rBracketPos - 1); 360 | if (strSection == Section) 361 | { 362 | while (ii < iFileLines) 363 | { 364 | strline = m_FileContainer[ii++]; 365 | size_t equalsignPos = strline.find('='); 366 | if (equalsignPos != strline.npos) 367 | { 368 | size_t last = equalsignPos ? strline.find_last_not_of(" \t", equalsignPos - 1) : strline.npos; 369 | if (last == strline.npos) 370 | strItem = ""; 371 | else 372 | strItem = strline.substr(0, last + 1); 373 | 374 | if (Item == strItem) 375 | { 376 | ReplaceLine(ii - 1, Item + " = " + Value); 377 | return; 378 | } 379 | } 380 | else if ('[' == strline[0]) 381 | { 382 | InsertLine(ii - 1, Item + " = " + Value); 383 | return; 384 | } 385 | } 386 | InsertLine(ii, Item + " = " + Value); 387 | return; 388 | } 389 | } 390 | } 391 | 392 | InsertLine(ii, "[" + Section + "]"); 393 | InsertLine(ii + 1, Item + " = " + Value); 394 | return; 395 | } 396 | 397 | bool CIniFile::InsertLine(size_t line, const std::string &str) 398 | { 399 | m_FileContainer.insert(m_FileContainer.begin() + line, str); 400 | return true; 401 | } 402 | 403 | bool CIniFile::ReplaceLine(size_t line, const std::string &str) 404 | { 405 | m_FileContainer[line] = str; 406 | return true; 407 | } 408 | -------------------------------------------------------------------------------- /arm9/source/inifile.h: -------------------------------------------------------------------------------- 1 | /* 2 | common/inifile.h 3 | Copyright (C) 2007 Acekard, www.acekard.com 4 | Copyright (C) 2007-2009 somebody 5 | Copyright (C) 2009-2010 yellow wood goblin 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #ifndef _INIFILE_H_ 22 | #define _INIFILE_H_ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | class CIniFile 29 | { 30 | public: 31 | CIniFile(); 32 | CIniFile(const std::string& filename); 33 | virtual ~CIniFile(); 34 | 35 | public: 36 | bool LoadIniFile(const std::string& FileName); 37 | bool SaveIniFile(const std::string& FileName); 38 | bool SaveIniFileModified(const std::string& FileName); 39 | 40 | std::string GetString(const std::string& Section,const std::string& Item,const std::string& DefaultValue); 41 | void SetString(const std::string& Section,const std::string& Item,const std::string& Value); 42 | int GetInt(const std::string& Section,const std::string& Item,int DefaultValue); 43 | void SetInt(const std::string& Section,const std::string& Item,int Value); 44 | void GetStringVector(const std::string& Section,const std::string& Item,std::vector& strings,char delimiter=','); 45 | void SetStringVector(const std::string& Section,const std::string& Item,std::vector& strings,char delimiter=','); 46 | protected: 47 | std::string m_sFileName; 48 | typedef std::vector StringArray; 49 | StringArray m_FileContainer; 50 | bool m_bLastResult; 51 | bool m_bModified; 52 | bool m_bReadOnly; 53 | typedef std::map SectionCache; 54 | SectionCache m_Cache; 55 | 56 | bool InsertLine(size_t line,const std::string& str); 57 | bool ReplaceLine(size_t line,const std::string& str); 58 | 59 | void SetFileString(const std::string& Section,const std::string& Item,const std::string& Value); 60 | std::string GetFileString(const std::string& Section,const std::string& Item); 61 | 62 | std::string GetString(const std::string& Section,const std::string& Item); 63 | int GetInt(const std::string& Section,const std::string& Item); 64 | }; 65 | 66 | #endif // _INIFILE_H_ 67 | 68 | -------------------------------------------------------------------------------- /arm9/source/pico/Area.c: -------------------------------------------------------------------------------- 1 | // This is part of Pico Library 2 | 3 | // Most code (c) Copyright 2004 Dave, All rights reserved. 4 | // Some code (c) Copyright 2006 notaz, All rights reserved. 5 | // Free for non-commercial use. 6 | 7 | // For commercial use, separate licencing terms must be obtained. 8 | 9 | 10 | #include "PicoInt.h" 11 | 12 | // ym2612 13 | #include "sound/ym2612.h" 14 | extern void *ym2612_regs; 15 | 16 | // sn76496 17 | #ifdef ARM9_SOUND 18 | extern int *sn76496_regs; 19 | #endif 20 | 21 | int (*PicoAcb)(struct PicoArea *)=NULL; // Area callback for each block of memory 22 | void *PmovFile=NULL; 23 | int PmovAction=0; 24 | arearw *areaRead = (arearw *) fread; // read and write function pointers for 25 | arearw *areaWrite = (arearw *) fwrite; // gzip save state ability 26 | 27 | 28 | // Scan one variable and callback 29 | static int ScanVar(void *data,int len,char *name) 30 | { 31 | struct PicoArea pa={NULL,0,NULL}; 32 | if (PicoAcb) { 33 | pa.data=data; pa.len=len; pa.name=name; 34 | return PicoAcb(&pa); 35 | } 36 | return 1; 37 | } 38 | 39 | #define SCAN_VAR(x,y) ScanVar(&x,sizeof(x),y); 40 | #define SCANP(x) ScanVar(&Pico.x,sizeof(Pico.x),#x); 41 | 42 | // Pack the cpu into a common format: 43 | static int PackCpu(unsigned char *cpu) 44 | { 45 | unsigned int pc=0; 46 | 47 | #ifdef EMU_A68K 48 | memcpy(cpu,M68000_regs.d,0x40); 49 | pc=M68000_regs.pc; 50 | *(unsigned char *)(cpu+0x44)=(unsigned char)M68000_regs.ccr; 51 | *(unsigned char *)(cpu+0x45)=(unsigned char)M68000_regs.srh; 52 | *(unsigned int *)(cpu+0x48)=M68000_regs.isp; 53 | #endif 54 | 55 | #ifdef EMU_C68K 56 | memcpy(cpu,PicoCpu.d,0x40); 57 | pc=PicoCpu.pc-PicoCpu.membase; 58 | *(unsigned char *)(cpu+0x44)=PicoCpu.flags; 59 | *(unsigned char *)(cpu+0x45)=PicoCpu.srh; 60 | *(unsigned int *)(cpu+0x48)=PicoCpu.osp; 61 | #endif 62 | 63 | *(unsigned int *)(cpu+0x40)=pc; 64 | return 0; 65 | } 66 | 67 | static int UnpackCpu(unsigned char *cpu) 68 | { 69 | unsigned int pc=0; 70 | 71 | pc=*(unsigned int *)(cpu+0x40); 72 | 73 | #ifdef EMU_A68K 74 | memcpy(M68000_regs.d,cpu,0x40); 75 | M68000_regs.pc=pc; 76 | M68000_regs.ccr=*(unsigned char *)(cpu+0x44); 77 | M68000_regs.srh=*(unsigned char *)(cpu+0x45); 78 | M68000_regs.isp=*(unsigned int *)(cpu+0x48); 79 | #endif 80 | 81 | #ifdef EMU_C68K 82 | memcpy(PicoCpu.d,cpu,0x40); 83 | PicoCpu.membase=0; 84 | PicoCpu.pc =PicoCpu.checkpc(pc); // Base pc 85 | PicoCpu.flags=*(unsigned char *)(cpu+0x44); 86 | PicoCpu.srh=*(unsigned char *)(cpu+0x45); 87 | PicoCpu.osp=*(unsigned int *)(cpu+0x48); 88 | #endif 89 | return 0; 90 | } 91 | 92 | // Scan the contents of the virtual machine's memory for saving or loading 93 | static int PicoAreaScan(int action,unsigned int ver) 94 | { 95 | unsigned char cpu[0x60]; 96 | unsigned char cpu_z80[0x60]; 97 | //int ret; 98 | 99 | memset(&cpu,0,sizeof(cpu)); 100 | memset(&cpu_z80,0,sizeof(cpu_z80)); 101 | 102 | if (action&4) 103 | { 104 | Pico.m.scanline=0; 105 | 106 | // Scan all the memory areas: 107 | SCANP(ram) SCANP(vram) SCANP(zram) SCANP(cram) SCANP(vsram) 108 | 109 | // Pack, scan and unpack the cpu data: 110 | if((PmovAction&3)==1) PackCpu(cpu); 111 | //SekInit(); // notaz: do we really have to do this here? 112 | //PicoMemInit(); 113 | SCAN_VAR(cpu,"cpu") 114 | if((PmovAction&3)==2) UnpackCpu(cpu); 115 | 116 | SCAN_VAR(Pico.m ,"misc") 117 | SCAN_VAR(Pico.video,"video") 118 | 119 | 120 | if(ver == 0x0030) { // zram was being saved incorrectly in 0x0030 (byteswaped?) 121 | Byteswap(Pico.zram, 0x2000); 122 | return 0; // do not try to load sound stuff 123 | } 124 | 125 | //SCAN_VAR(Pico.s ,"sound") 126 | // notaz: save/load z80, YM2612, sn76496 states instead of Pico.s (which is unused anyway) 127 | if(PicoOpt&7) { 128 | #ifdef ARM9_SOUND 129 | if((PmovAction&3)==1) z80_pack(cpu_z80); 130 | ret = SCAN_VAR(cpu_z80,"cpu_z80") 131 | // do not unpack if we fail to load z80 state 132 | if((PmovAction&3)==2) { 133 | if(ret) z80_reset(); 134 | else z80_unpack(cpu_z80); 135 | } 136 | #endif 137 | } 138 | if(PicoOpt&3) 139 | #ifdef ARM9_SOUND 140 | ScanVar(sn76496_regs,28*4,"SN76496state"); // regs and other stuff 141 | #endif 142 | if(PicoOpt&1) { 143 | #ifdef ARM9_SOUND 144 | ScanVar(ym2612_regs, 0x200+4, "YM2612state"); // regs + addr line 145 | if((PmovAction&3)==2) YM2612PicoStateLoad(); // reload YM2612 state from it's regs 146 | #endif 147 | } 148 | } 149 | 150 | return 0; 151 | } 152 | 153 | // --------------------------------------------------------------------------- 154 | // Helper code to save/load to a file handle 155 | 156 | // Area callback for each piece of Megadrive memory, read or write to the file: 157 | static int StateAcb(struct PicoArea *pa) 158 | { 159 | int ret=0; 160 | if (PmovFile==NULL) return 1; 161 | 162 | if ((PmovAction&3)==1) ret = areaWrite(pa->data,1,pa->len,PmovFile); 163 | if ((PmovAction&3)==2) ret = areaRead (pa->data,1,pa->len,PmovFile); 164 | return (ret != pa->len); 165 | } 166 | 167 | // Save or load the state from PmovFile: 168 | int PmovState() 169 | { 170 | int minimum=0; 171 | unsigned char head[32]; 172 | 173 | memset(head,0,sizeof(head)); 174 | 175 | // Find out minimal compatible version: 176 | //PicoAreaScan(PmovAction&0xc,&minimum); 177 | minimum = 0x0021; 178 | 179 | memcpy(head,"Pico",4); 180 | *(unsigned int *)(head+0x8)=PicoVer; 181 | *(unsigned int *)(head+0xc)=minimum; 182 | 183 | // Scan header: 184 | if (PmovAction&1) areaWrite(head,1,sizeof(head),PmovFile); 185 | if (PmovAction&2) areaRead (head,1,sizeof(head),PmovFile); 186 | 187 | // Scan memory areas: 188 | PicoAcb=StateAcb; 189 | PicoAreaScan(PmovAction,*(unsigned int *)(head+0x8)); 190 | PicoAcb=NULL; 191 | return 0; 192 | } 193 | 194 | int PmovUpdate() 195 | { 196 | int ret=0; 197 | if (PmovFile==NULL) return 1; 198 | 199 | if ((PmovAction&3)==0) return 0; 200 | 201 | PicoPad[1]=0; // Make sure pad #2 is blank 202 | if (PmovAction&1) ret=areaWrite(PicoPad,1,2,PmovFile); 203 | if (PmovAction&2) ret=areaRead (PicoPad,1,2,PmovFile); 204 | 205 | if (ret!=2) 206 | { 207 | // End of file 208 | fclose((FILE *) PmovFile); PmovFile=NULL; 209 | PmovAction=0; 210 | } 211 | 212 | return 0; 213 | } 214 | -------------------------------------------------------------------------------- /arm9/source/pico/Cart.c: -------------------------------------------------------------------------------- 1 | // This is part of Pico Library 2 | 3 | // Most code (c) Copyright 2004 Dave, All rights reserved. 4 | // Some code (c) Copyright 2006 notaz, All rights reserved. 5 | // Free for non-commercial use. 6 | 7 | // For commercial use, separate licencing terms must be obtained. 8 | 9 | 10 | #include "PicoInt.h" 11 | char* romSpace; 12 | 13 | void Byteswap(unsigned char *data,int len) 14 | { 15 | int i=0; 16 | 17 | if (len<2) return; // Too short 18 | 19 | do 20 | { 21 | unsigned short *pd=(unsigned short *)(data+i); 22 | int value=*pd; // Get 2 bytes 23 | 24 | value=(value<<8)|(value>>8); // Byteswap it 25 | *pd=(unsigned short)value; // Put 2b ytes 26 | i+=2; 27 | } 28 | while (i+2<=len); 29 | } 30 | 31 | // Interleve a 16k block and byteswap 32 | static int InterleveBlock(unsigned char *dest,unsigned char *src) 33 | { 34 | int i=0; 35 | for (i=0;i<0x2000;i++) dest[(i<<1) ]=src[ i]; // Odd 36 | for (i=0;i<0x2000;i++) dest[(i<<1)+1]=src[0x2000+i]; // Even 37 | return 0; 38 | } 39 | 40 | // Decode a SMD file 41 | int DecodeSmd(unsigned char *data,int len) 42 | { 43 | unsigned char *temp=NULL; 44 | int i=0; 45 | 46 | temp=(unsigned char *)malloc(0x4000); 47 | if (temp==NULL) return 1; 48 | memset(temp,0,0x4000); 49 | 50 | // Interleve each 16k block and shift down by 0x200: 51 | for (i=0; i+0x4200<=len; i+=0x4000) 52 | { 53 | InterleveBlock(temp,data+0x200+i); // Interleve 16k to temporary buffer 54 | memcpy(data+i,temp,0x4000); // Copy back in 55 | } 56 | 57 | free(temp); 58 | return 0; 59 | } 60 | 61 | int PicoCartLoad(FILE *f,unsigned char **prom,unsigned int *psize) 62 | { 63 | int size=0; 64 | if (f==NULL) return 1; 65 | 66 | fseek(f,0,SEEK_END); size=ftell(f); fseek(f,0,SEEK_SET); 67 | 68 | size=(size+3)&~3; // Round up to a multiple of 4 69 | 70 | fread(romSpace,1,size,f); // Load up the rom 71 | fclose(f); 72 | 73 | // Check for SMD: 74 | if ((size&0x3fff)==0x200) { DecodeSmd((unsigned char*)romSpace,size); size-=0x200; } // Decode and byteswap SMD 75 | else Byteswap((unsigned char*)romSpace,size); // Just byteswap 76 | 77 | if (prom) *prom=(unsigned char*)romSpace; 78 | if (psize) *psize=size; 79 | 80 | return 0; 81 | } 82 | 83 | // Insert/remove a cartridge: 84 | int PicoCartInsert(unsigned char *rom,unsigned int romsize) 85 | { 86 | // Make sure movie playing/recording is stopped: 87 | if (PmovFile) fclose(PmovFile); 88 | PmovFile=NULL; PmovAction=0; 89 | 90 | // notaz: add a 68k "jump one op back" opcode to the end of ROM. 91 | // This will hang the emu, but will prevent nasty crashes. 92 | // note: 4 bytes are padded to every ROM 93 | *(unsigned long *)(rom+romsize) = 0xFEFFFA4E; 94 | 95 | // notaz: sram 96 | SRam.resize=1; 97 | Pico.rom=rom; 98 | Pico.romsize=romsize; 99 | 100 | return PicoReset(1); 101 | } 102 | 103 | 104 | #ifdef __SYMBIAN32__ 105 | 106 | // notaz 107 | #include "../unzip.h" 108 | 109 | // nearly same as PicoCartLoad, but works with zipfiles 110 | int CartLoadZip(const char *fname, unsigned char **prom, unsigned int *psize) 111 | { 112 | unsigned char *rom=0; 113 | struct zipent* zipentry; 114 | int size; 115 | ZIP *zipfile = openzip(fname); 116 | 117 | if(!zipfile) return 1; 118 | 119 | // find first bin or smd 120 | while((zipentry = readzip(zipfile)) != 0) 121 | { 122 | char *ext; 123 | if(strlen(zipentry->name) < 5) continue; 124 | ext = zipentry->name+strlen(zipentry->name)-4; 125 | 126 | if(!strcasecmp(ext, ".bin") || !strcasecmp(ext, ".smd") || !strcasecmp(ext, ".gen")) break; 127 | } 128 | 129 | if(!zipentry) { 130 | closezip(zipfile); 131 | return 4; // no roms 132 | } 133 | 134 | size = zipentry->uncompressed_size; 135 | 136 | size=(size+3)&~3; // Round up to a multiple of 4 137 | 138 | // Allocate space for the rom plus padding 139 | rom=(unsigned char *)malloc(size+4); 140 | if (rom==NULL) { closezip(zipfile); return 1; } 141 | //memset(rom,0,size+4); 142 | 143 | if(readuncompresszip(zipfile, zipentry, (char *)rom) != 0) { 144 | free(rom); 145 | rom = 0; 146 | closezip(zipfile); 147 | return 5; // unzip failed 148 | } 149 | 150 | closezip(zipfile); 151 | 152 | // Check for SMD: 153 | if ((size&0x3fff)==0x200) { DecodeSmd(rom,size); size-=0x200; } // Decode and byteswap SMD 154 | else Byteswap(rom,size); // Just byteswap 155 | 156 | if (prom) *prom=rom; 157 | if (psize) *psize=size; 158 | 159 | return 0; 160 | } 161 | 162 | #endif 163 | -------------------------------------------------------------------------------- /arm9/source/pico/Disa.h: -------------------------------------------------------------------------------- 1 | 2 | // Dave's Disa 68000 Disassembler 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #if defined(__GNUC__) || defined(_WIN32_WCE) 9 | #define CPU_CALL 10 | #else 11 | #define CPU_CALL __fastcall 12 | #endif 13 | 14 | extern unsigned int DisaPc; 15 | extern char *DisaText; // Text buffer to write in 16 | 17 | extern unsigned short (CPU_CALL *DisaWord)(unsigned int a); 18 | int DisaGetEa(char *t,int ea,int size); 19 | 20 | int DisaGet(); 21 | 22 | #ifdef __cplusplus 23 | } // End of extern "C" 24 | #endif 25 | -------------------------------------------------------------------------------- /arm9/source/pico/Draw3.c: -------------------------------------------------------------------------------- 1 | #ifdef HW_FRAME_RENDERER 2 | 3 | #include 4 | #include 5 | 6 | #include "PicoInt.h" 7 | #include "Timer.h" 8 | 9 | #ifdef __MARM__ 10 | #define START_ROW 1 // which row of tiles to start rendering at? 11 | #define END_ROW 27 // ..end 12 | #else 13 | #define START_ROW 0 14 | #define END_ROW 28 15 | #endif 16 | #define TILE_ROWS END_ROW-START_ROW 17 | 18 | int currpri = 0; 19 | int mapoffset, tileoffset; 20 | 21 | static int TileXnormYnorm(int addr) { 22 | ((uint16*)BG_MAP_RAM(0))[addr+tileoffset] = tileoffset; 23 | // dmaCopy((void*)(Pico.vram+addr),(void*)(BG_TILE_RAM(2)+(mapoffset*8*8)),8*8); 24 | mapoffset++; 25 | return 0; 26 | } 27 | 28 | static int TileXflipYnorm(int addr) { 29 | ((uint16*)BG_MAP_RAM(0))[addr+tileoffset] = tileoffset; 30 | // dmaCopy((void*)(Pico.vram+addr),(void*)(BG_TILE_RAM(2)+(mapoffset*8*8)),8*8); 31 | mapoffset++; 32 | return 0; 33 | } 34 | 35 | static int TileXnormYflip(int addr) { 36 | ((uint16*)BG_MAP_RAM(0))[addr+tileoffset] = tileoffset; 37 | // dmaCopy((void*)(Pico.vram+addr),(void*)(BG_TILE_RAM(2)+(mapoffset*8*8)),8*8); 38 | mapoffset++; 39 | return 0; 40 | } 41 | 42 | static int TileXflipYflip(int addr) { 43 | ((uint16*)BG_MAP_RAM(0))[addr+tileoffset] = tileoffset; 44 | // dmaCopy((void*)(Pico.vram+addr),(void*)(BG_TILE_RAM(2)+(mapoffset*8*8)),8*8); 45 | mapoffset++; 46 | return 0; 47 | } 48 | 49 | static void PushTile(int addr) { 50 | // dmaCopy(Pico.vram+addr,(uint16*)BG_TILE_RAM(4)+(tileoffset*8*8),8*8); 51 | unsigned int pack = 0; 52 | unsigned int t = 0; 53 | 54 | int i; 55 | uint16 pd[4]; 56 | for(i=0; i < 8; i++, addr+=2) { 57 | pack=*(unsigned int *)(Pico.vram+addr); // Get 8 pixels 58 | if(!pack) continue; 59 | 60 | t=pack&0x0000f000; if (t) { t=t>>12; pd[0]=((unsigned short)t)<<8; } 61 | t=pack&0x00000f00; if (t) { t=t>> 8; pd[0]|=(unsigned short)t; } 62 | t=pack&0x000000f0; if (t) { t=t>> 4; pd[1]=((unsigned short)t)<<8; } 63 | t=pack&0x0000000f; if (t) { t=t ; pd[1]|=(unsigned short)t; } 64 | t=pack&0xf0000000; if (t) { t=t>>28; pd[2]=((unsigned short)t)<<8; } 65 | t=pack&0x0f000000; if (t) { t=t>>24; pd[2]|=(unsigned short)t; } 66 | t=pack&0x00f00000; if (t) { t=t>>20; pd[3]=((unsigned short)t)<<8; } 67 | t=pack&0x000f0000; if (t) { t=t>>16; pd[3]|=(unsigned short)t; } 68 | 69 | dmaCopy(pd,(uint16*)BG_TILE_RAM(4)+(tileoffset*8*8)+(i*8),8); 70 | } 71 | } 72 | 73 | static void DrawLayerFull(int plane, int planestart, int planeend) 74 | { 75 | struct PicoVideo *pvid=&Pico.video; 76 | static char shift[4]={5,6,6,7}; // 32,64 or 128 sized tilemaps 77 | int width, height, ymask, htab; 78 | int nametab, hscroll=0, vscroll, cells; 79 | unsigned short *scrpos; 80 | int blank=-1, xmask, nametab_row, trow; 81 | 82 | // parse ranges 83 | cells = (planeend>>16)-(planestart>>16); 84 | planestart = planestart<<16>>16; 85 | planeend = planeend<<16>>16; 86 | 87 | // Work out the Tiles to draw 88 | 89 | htab=pvid->reg[13]<<9; // Horizontal scroll table address 90 | // if ( pvid->reg[11]&2) htab+=Scanline<<1; // Offset by line 91 | // if ((pvid->reg[11]&1)==0) htab&=~0xf; // Offset by tile 92 | htab+=plane; // A or B 93 | 94 | if(!(pvid->reg[11]&3)) { // full screen scroll 95 | // Get horizontal scroll value 96 | hscroll=Pico.vram[htab&0x7fff]; 97 | htab = 0; // this marks that we don't have to update scroll value 98 | } 99 | 100 | // Work out the name table size: 32 64 or 128 tiles (0-3) 101 | width=pvid->reg[16]; 102 | height=(width>>4)&3; width&=3; 103 | 104 | xmask=(1<reg[2]&0x38)<< 9; // A 109 | else nametab=(pvid->reg[4]&0x07)<<12; // B 110 | 111 | iprintf("\x1b[18;0HWidth: %d\tHeight: %d",width,height); 112 | 113 | // Get vertical scroll value: 114 | vscroll=Pico.vsram[plane]; 115 | if(vscroll&7) planeend++; // we have vertically clipped tiles due to vscroll, so we need 1 more row 116 | 117 | 118 | 119 | for(trow = planestart; trow < planeend; trow++) { // current tile row 120 | int cellc=cells,tilex,dx; 121 | 122 | // Find the tile row in the name table 123 | //ts.line=(vscroll+Scanline)&ymask; 124 | //ts.nametab+=(ts.line>>3)<>3))&ymask)<>3; 136 | dx=((hscroll-1)&7)+1; 137 | if(dx != 8) cellc++; // have hscroll, do more cells 138 | 139 | 140 | int tileaddr = ((Pico.vram[nametab_row+(tilex&xmask)])&0x7ff)<<4; 141 | PushTile(tileaddr); 142 | 143 | tileoffset++; 144 | 145 | for (; cellc; dx+=8,tilex++,cellc--) 146 | { 147 | int code=0,addr=0,zero=0; 148 | unsigned short *pal=NULL; 149 | 150 | code=Pico.vram[nametab_row+(tilex&xmask)]; 151 | if (code==blank) continue; 152 | 153 | if ((code>>15) != currpri) { // high priority tile 154 | continue; 155 | } 156 | 157 | // Get tile address/2: 158 | addr=(code&0x7ff)<<4; 159 | 160 | // pal=PicoCramHigh+((code>>9)&0x30); 161 | 162 | switch((code>>11)&3) { 163 | case 0: zero=TileXnormYnorm(dx/8); break; 164 | case 1: zero=TileXflipYnorm(dx/8); break; 165 | case 2: zero=TileXnormYflip(dx/8); break; 166 | case 3: zero=TileXflipYflip(dx/8); break; 167 | } 168 | if(zero) blank=code; // We know this tile is blank now 169 | } 170 | 171 | 172 | } 173 | 174 | } 175 | 176 | static void DrawAllSpritesFull(int prio, int maxwidth) 177 | { 178 | struct PicoVideo *pvid=&Pico.video; 179 | int table=0,maskrange=0; 180 | int i,u,link=0; 181 | unsigned char spin[80]; // Sprite index 182 | int y_min=START_ROW*8, y_max=END_ROW*8; // for a simple sprite masking 183 | 184 | table=pvid->reg[5]&0x7f; 185 | if (pvid->reg[12]&1) table&=0x7e; // Lowest bit 0 in 40-cell mode 186 | table<<=8; // Get sprite table address/2 187 | 188 | for (i=u=0; u < 80; u++) 189 | { 190 | unsigned int *sprite=NULL; 191 | int code, code2, sx, sy, height; 192 | 193 | sprite=(unsigned int *)(Pico.vram+((table+(link<<2))&0x7ffc)); // Find sprite 194 | 195 | // get sprite info 196 | code = sprite[0]; 197 | 198 | // check if it is not hidden vertically 199 | sy = (code&0x1ff)-0x80; 200 | height = (((code>>24)&3)+1)<<3; 201 | if(sy+height <= y_min || sy > y_max) goto nextsprite; 202 | 203 | // masking sprite? 204 | code2=sprite[1]; 205 | sx = (code2>>16)&0x1ff; 206 | if(!sx) { 207 | int to = sy+height; // sy ~ from 208 | if(maskrange) { 209 | // try to merge with previous range 210 | if((maskrange>>16)+1 >= sy && (maskrange>>16) <= to && (maskrange&0xffff) < sy) sy = (maskrange&0xffff); 211 | else if((maskrange&0xffff)-1 <= to && (maskrange&0xffff) >= sy && (maskrange>>16) > to) to = (maskrange>>16); 212 | } 213 | // support only very simple masking (top and bottom of screen) 214 | if(sy <= y_min && to+1 > y_min) y_min = to+1; 215 | else if(to >= y_max && sy-1 < y_max) y_max = sy-1; 216 | else maskrange=sy|(to<<16); 217 | 218 | goto nextsprite; 219 | } 220 | 221 | // priority 222 | if(((code2>>15)&1) != prio) goto nextsprite; // wrong priority 223 | 224 | // check if sprite is not hidden horizontally 225 | sx -= 0x78; // Get X coordinate + 8 226 | if(sx <= -8*3 || sx >= maxwidth) goto nextsprite; 227 | 228 | // sprite is good, save it's index 229 | spin[i++]=(unsigned char)link; 230 | 231 | nextsprite: 232 | // Find next sprite 233 | link=(code>>16)&0x7f; 234 | if(!link) break; // End of sprites 235 | } 236 | 237 | // Go through sprites backwards: 238 | for (i-- ;i>=0; i--) 239 | { 240 | unsigned int *sprite=NULL; 241 | sprite=(unsigned int *)(Pico.vram+((table+(spin[i]<<2))&0x7ffc)); // Find sprite 242 | 243 | // DrawSpriteFull(sprite); 244 | } 245 | } 246 | 247 | static void DrawDisplayFull() 248 | { 249 | SetupProfile(); 250 | BeginProfile(); 251 | struct PicoVideo *pvid=&Pico.video; 252 | int win, edge=0, vwind=0, hwin=0; 253 | int planestart=START_ROW, planeend=END_ROW; // plane A start/end when window shares display with plane A (in tile rows or columns) 254 | int winstart=START_ROW, winend=END_ROW; // same for window 255 | int maxw, maxcolc; // max width and col cells 256 | 257 | if(pvid->reg[12]&1) { 258 | maxw = 328; maxcolc = 40; 259 | } else { 260 | maxw = 264; maxcolc = 32; 261 | } 262 | 263 | // horizontal window? 264 | if((win=pvid->reg[0x12])) { 265 | hwin=1; // hwindow shares display with plane A 266 | if(win == 0x80) { 267 | // fullscreen window 268 | hwin=2; 269 | } else if(win < 0x80) { 270 | // window on the top 271 | planestart = winend = win&0x1f; 272 | if(planestart <= START_ROW) hwin=0; // window not visible in our drawing region 273 | if(planestart >= END_ROW) hwin=2; 274 | } else if(win > 0x80) { 275 | // window at the bottom 276 | planeend = winstart = (win&0x1f); 277 | if(planeend >= END_ROW) hwin=0; 278 | } 279 | } 280 | 281 | // check for vertical window, but only if there is no horizontal 282 | if(!hwin) { 283 | win=pvid->reg[0x11]; 284 | edge=win&0x1f; 285 | if (win&0x80) { 286 | if(!edge) hwin=2; 287 | else if(edge < (maxcolc>>1)) { 288 | // window is on the right 289 | vwind=1; 290 | planeend=winstart=edge<<1; 291 | planestart=0; winend=maxcolc; 292 | } 293 | } else { 294 | if(edge >= (maxcolc>>1)) hwin=2; 295 | else if(edge) { 296 | // window is on the left 297 | vwind=1; 298 | winend=planestart=edge<<1; 299 | winstart=0; planeend=maxcolc; 300 | } 301 | } 302 | } 303 | 304 | currpri = 0; 305 | DrawLayerFull(1, START_ROW, (maxcolc<<16)|END_ROW); 306 | DrawLayerFull(0, START_ROW, (maxcolc<<16)|END_ROW); 307 | EndProfile("DrawDisplayFull"); 308 | } 309 | 310 | static int UpdatePalette() 311 | { 312 | int c=0; 313 | 314 | // Update palette: 315 | for (c=0;c<64;c++) BG_PALETTE[c]=(unsigned short)PicoCram(Pico.cram[c]); 316 | Pico.m.dirtyPal=0; 317 | 318 | return 0; 319 | } 320 | 321 | void PicoFrameFull() 322 | { 323 | // Draw screen 324 | // iprintf("\x1b[18;0HPicoFrameFull hit"); 325 | 326 | tileoffset = 0; 327 | mapoffset = 0; 328 | 329 | UpdatePalette(); 330 | 331 | if (Pico.video.reg[1]&0x40) DrawDisplayFull(); 332 | 333 | iprintf("\x1b[19;0HTile offset:\t%d \n",tileoffset); 334 | iprintf("Map offset:\t%d ",mapoffset); 335 | } 336 | 337 | #endif // HW_FRAME_RENDERER 338 | -------------------------------------------------------------------------------- /arm9/source/pico/Memory_asm.s: -------------------------------------------------------------------------------- 1 | .extern Pico 2 | .extern PicoCpu 3 | .extern PicoPad 4 | .extern PicoOpt 5 | .extern SRam 6 | 7 | @----------------------------------------------------------------------------------------------------- 8 | @ ALL FUNCTIONS RELATED TO MEMORY.C 9 | @----------------------------------------------------------------------------------------------------- 10 | .global PicoCheckPc @ pc (r0) 11 | PicoCheckPc: 12 | ldr r3, =(PicoCpu+0x60) @r3 = &PicoCpu.membase 13 | ldr r1, [r3] @r1 = PicoCpu.membase (value) 14 | sub r0, r0, r1 15 | bic r0, r0, #-16777216 16 | and r1, r0, #14680064 17 | cmp r1, #14680064 18 | bne .endif1pcp 19 | ldr r2, =(Pico) @pico.ram 20 | and r1, r0, #16711680 21 | sub r2, r2, r1 22 | str r2, [r3] @stored PicoCpu.membase 23 | add r0, r0, r2 @return value = PicoCpu.membase + pc 24 | bx lr 25 | .endif1pcp: 26 | ldr r2, =(Pico+0x22200) @pico.rom 27 | ldr r2, [r2] 28 | str r2, [r3] @stored PicoCpu.membase 29 | add r0, r0, r2 @return value = PicoCpu.membase + pc 30 | bx lr 31 | 32 | @--------------------------------------------------------------------------- 33 | .global PadRead @int i (r0) 34 | PadRead: 35 | stmfd sp!, {r4,lr} 36 | mov r4, r0 @backup of r0 37 | ldr r1, =PicoPad 38 | ldr r1, [r1, r0, lsl #2] 39 | mvn r1, r1 @r1 = pad 40 | ldr r2, =(Pico+0x22000) @pico.ioports 41 | add r3, r0, #1 42 | ldrb r2, [r2, r3] 43 | and r2, r2, #0x40 @r2 = TH 44 | ldr r3, =PicoOpt 45 | ldr r3, [r3] 46 | ands r3, r3, #0x20 47 | beq .endif1pr 48 | ldr r3, =(Pico+0x2220A) @pico.m.padTHPhase 49 | ldr r3, [r3, r0] @r3 = phase [r0 is free] 50 | cmp r3, #2 51 | bne .else1pr 52 | cmp r2, #0 53 | bne .else1pr 54 | and r0, r1, #0xC0 @ [r3 is free] 55 | asr r0, r0, #2 56 | b .endpr 57 | .else1pr: 58 | cmp r3, #3 59 | bne .endif1pr 60 | cmp r2, #0 61 | beq .else2pr 62 | and r0, r1, #0x30 @ [r3 is free] 63 | asr r3, r1, #8 64 | and r3, r3, #0xF 65 | orr r0, r0, r3 66 | b .endpr 67 | .else2pr: 68 | and r0, r1, #0xC0 69 | asr r0, r0, #2 70 | orr r0, r0, #0x0F 71 | b .endpr 72 | .endif1pr: 73 | cmp r2, #0 74 | beq .else3pr 75 | and r0, r1, #0x3F 76 | b .endpr 77 | .else3pr: 78 | and r0, r1, #0xC0 79 | asr r0, r0, #2 80 | and r3, r1, #3 81 | orr r0, r0, r3 82 | .endpr: @ [r2 is free] 83 | ldr r3, =(Pico+0x22000) @pico.ioports 84 | add r2, r3, r4 85 | add r3, r3, r4 86 | ldrb r3, [r3, #1] @pico.ioports[i+1] 87 | ldrb r2, [r2, #4] @pico.ioports[i+4] 88 | and r2, r3, r2 89 | orr r0, r0, r2 90 | ldmfd sp!, {r4,pc} 91 | 92 | @--------------------------------------------------------------------------- 93 | .global OtherRead16 @ unsigned int a (r0) 94 | OtherRead16: 95 | stmfd sp!, {r1-r4,lr} 96 | ldr r3, =0xffc000 97 | and r3, r3, r0 98 | cmp r3, #0xa00000 99 | bne .endif1or16 100 | lsl r0, r0, #19 101 | lsr r0, r0, #19 102 | ldr r3, =PicoOpt 103 | ldr r3, [r3] 104 | ands r3, r3, #4 105 | bne .endif2or16 106 | ldr r3, =(Pico+0x22214) @r3 = &pico.m.z80_lastaddr 107 | ldrh r2, [r3] @r2 = pico.m.z80_lastaddr 108 | cmp r0, r2 109 | bne .endif3or16 110 | ldr r1, =(Pico+0x22216) @r1 = &Pico.m.z80_fakeval 111 | ldrh r0, [r1] @r0 = Pico.m.z80_fakeval 112 | add r0, r0, #1 113 | strh r0, [r1] 114 | sub r0, r0, #1 115 | b .endor16 116 | .endif3or16: 117 | ldr r1, =(Pico+0x22216) @r1 = &Pico.m.z80_fakeval 118 | mov r4, #0 119 | strh r4, [r1] 120 | strh r0, [r3] 121 | .endif2or16: 122 | ldr r1, =(Pico+0x20000) @r1 = Pico.zram 123 | add r2, r1, r0 124 | ldrb r3, [r2] 125 | add r2, r2, #1 126 | ldrb r2, [r2] 127 | orr r0, r2, r3, lsl #8 @r0 = (Pico.zram[a]<<8)|Pico.zram[a+1] 128 | b .endor16 129 | .endif1or16: 130 | bic r3, r0, #-16777216 131 | bic r3, r3, #3 132 | mov r1, #0xA0000 133 | orr r1, r1, #0x400 134 | cmp r3, r1 135 | bne .endif4or16 136 | ldr r1, =(Pico+0x22208) 137 | ldrb r2, [r1] 138 | and r0, r2, #3 @r0 = Pico.m.rotate&3 139 | add r2, r2, #1 140 | strb r2, [r1] 141 | b .endor16 142 | .endif4or16: 143 | bic r1, r0, #-16777216 144 | bic r1, r1, #31 145 | cmp r1, #0xA10000 146 | bne .endif5or16 147 | lsr r0, r0, #1 148 | and r0, r0, #0xF 149 | cmp r0, #0 150 | beq .case0or16 151 | cmp r0, #1 152 | beq .case1or16 153 | cmp r0, #2 154 | beq .case2or16 155 | ldr r2, =(Pico+0x22000) 156 | add r0, r2, r0 157 | ldrb r0, [r0] @r0 = Pico.ioports[aa] 158 | orr r0, r0, lsr #8 159 | b .endor16 160 | .case0or16: 161 | ldr r2, =(Pico+0x2220F) 162 | ldrb r0, [r2] @r0 = Pico.m.hardware 163 | orr r0, r0, lsr #8 164 | b .endor16 165 | .case1or16: 166 | mov r0, #0 167 | bl PadRead 168 | ldr r1, =(Pico+0x22000) 169 | ldrb r1, [r1, #1] 170 | and r1, r1, #0x80 171 | orr r0, r0, r1 @r0 = PadRead(0)|Pico.ioports[1]&0x80 172 | orr r0, r0, lsr #8 173 | b .endor16 174 | .case2or16: 175 | mov r0, #1 176 | bl PadRead 177 | ldr r1, =(Pico+0x22000) 178 | ldrb r1, [r1, #2] 179 | and r1, r1, #0x80 180 | orr r0, r0, r1 @r0 = PadRead(1)|Pico.ioports[2]&0x80 181 | orr r0, r0, lsr #8 182 | b .endor16 183 | .endif5or16: 184 | ldr r2, =0xA11100 @Needs to be improved 185 | cmp r0, r2 186 | bne .endif6or16 187 | ldr r1, =(Pico+0x22209) 188 | ldrb r0, [r1] 189 | lsl r0, r0, #8 @r0 = Pico.m.z80Run << 8 190 | b .endor16 191 | .endif6or16: 192 | bic r1, r0, #-16777216 193 | bic r1, r1, #31 194 | cmp r1, #0xC00000 195 | bne .endif7or16 196 | bl PicoVideoRead @r0 = PicoVideoRead(a) 197 | orr r0, r0, lsr #8 198 | b .endor16 199 | .endif7or16: 200 | mov r0, #0 @default return value (r0 = 0) 201 | .endor16: 202 | ldmfd sp!, {r1-r4,pc} 203 | 204 | @--------------------------------------------------------------------------- 205 | .global PicoRead8 @unsigned int a (r0) 206 | PicoRead8: 207 | stmfd sp!, {r4,lr} 208 | bic r0, r0, #-16777216 209 | and r1, r0, #0xE00000 210 | cmp r1, #0xE00000 211 | bne .endif1pr8 212 | eor r1, r0, #1 213 | lsl r1, r1, #16 214 | lsr r1, r1, #16 215 | ldr r2, =Pico 216 | add r0, r2, r1 @r0 = Pico.ram+((a^1)&0xffff) 217 | ldrb r0, [r0] 218 | b .endpr8 219 | .endif1pr8: 220 | ldr r2, =(SRam) 221 | ldr r1, [r2, #0x4] @r1 = Sram.start 222 | ldr r3, [r2, #0x8] @r3 = Sram.end 223 | cmp r0, r1 224 | bcc .endif2pr8 225 | cmp r0, r3 226 | bhi .endif2pr8 227 | ldr r3, =(Pico+0x22211) 228 | ldrb r4, [r3] 229 | ands r4, r4, #1 230 | beq .endif2pr8 231 | ldr r2, [r2] @ r2 = Sram.data 232 | sub r2, r2, r1 233 | add r0, r2, r0 @ r0 = SRam.data-SRam.start+a 234 | ldrb r0, [r0] 235 | b .endpr8 236 | .endif2pr8: 237 | ldr r2, =(Pico+0x22200) 238 | ldr r1, [r2, #4] @r1 = Pico.romsize 239 | cmp r0, r1 240 | bcs .endif3pr8 241 | eor r0, r0, #1 242 | ldr r2, [r2] @r2 = Pico.rom 243 | add r0, r2, r0 244 | ldrb r0, [r0] @r0 = Pico.rom+(a^1) 245 | b .endpr8 246 | .endif3pr8: 247 | mov r4, r0 @backup of variable a 248 | bic r0, r0, #1 @Now a is temporarly changed 249 | bl OtherRead16 250 | ands r1, r4, #1 251 | lsreq r0, r0, #8 252 | .endpr8: 253 | and r0, r0, #0xFF 254 | ldmfd sp!, {r4,pc} 255 | 256 | @--------------------------------------------------------------------------- 257 | .global PicoRead16 @unsigned int a (r0) 258 | PicoRead16: 259 | stmfd sp!, {r4,lr} 260 | bic r0, r0, #-16777216 261 | bic r0, r0, #1 262 | and r1, r0, #0xE00000 263 | cmp r1, #0xE00000 264 | bne .endif1pr16 265 | lsl r1, r0, #16 266 | lsr r1, r1, #16 267 | ldr r2, =Pico 268 | add r0, r2, r1 @r0 = Pico.ram+(a^&0xffff) 269 | ldrh r0, [r0] 270 | b .endpr16 271 | .endif1pr16: 272 | ldr r2, =(SRam) 273 | ldr r1, [r2, #0x4] @r1 = Sram.start 274 | ldr r3, [r2, #0x8] @r3 = Sram.end 275 | cmp r0, r1 276 | bcc .endif2pr16 277 | cmp r0, r3 278 | bhi .endif2pr16 279 | ldr r3, =(Pico+0x22211) 280 | ldrb r4, [r3] 281 | ands r4, r4, #1 282 | beq .endif2pr16 283 | ldr r2, [r2] @ r2 = Sram.data 284 | sub r2, r2, r1 285 | add r0, r2, r0 @ r0 = SRam.data-SRam.start+a 286 | ldrh r0, [r0] 287 | b .endpr16 288 | .endif2pr16: 289 | ldr r2, =(Pico+0x22200) 290 | ldr r1, [r2, #4] @r1 = Pico.romsize 291 | cmp r0, r1 292 | bcs .endif3pr16 293 | ldr r2, [r2] @r2 = Pico.rom 294 | add r0, r2, r0 295 | ldrh r0, [r0] @r0 = Pico.rom+a 296 | b .endpr16 297 | .endif3pr16: 298 | bl OtherRead16 299 | .endpr16: 300 | lsl r0, r0, #16 301 | lsr r0, r0, #16 302 | ldmfd sp!, {r4,pc} 303 | 304 | @--------------------------------------------------------------------------- 305 | .global PicoRead32 @unsigned int a (r0) 306 | PicoRead32: 307 | stmfd sp!, {r4,lr} 308 | bic r0, r0, #-16777216 309 | bic r0, r0, #1 310 | and r1, r0, #0xE00000 311 | cmp r1, #0xE00000 312 | bne .endif1pr32 313 | lsl r1, r0, #16 314 | lsr r1, r1, #16 315 | ldr r2, =Pico 316 | add r0, r2, r1 @r0 = pm = Pico.ram+(a^&0xffff) 317 | ldrh r1, [r0] 318 | ldrh r2, [r0, #2] 319 | orr r0, r2, r1, lsl #16 @r0 = (pm[0]<<16)|pm[1]; 320 | b .endpr32 321 | .endif1pr32: 322 | ldr r2, =(SRam) 323 | ldr r1, [r2, #0x4] @r1 = Sram.start 324 | ldr r3, [r2, #0x8] @r3 = Sram.end 325 | cmp r0, r1 326 | bcc .endif2pr32 327 | cmp r0, r3 328 | bhi .endif2pr32 329 | ldr r3, =(Pico+0x22211) 330 | ldrb r4, [r3] 331 | ands r4, r4, #1 332 | beq .endif2pr32 333 | ldr r2, [r2] @ r2 = Sram.data 334 | sub r2, r2, r1 335 | add r0, r2, r0 336 | ldrh r1, [r0] @ SRam.data-SRam.start+a 337 | ldrh r2, [r0, #2] @ SRam.data-SRam.start+a+2 338 | orr r0, r2, r1, lsl #16 @ r0 = *(u8 *)(SRam.data-SRam.start+a)<<16 | *(u8 *)(SRam.data-SRam.start+a+2) 339 | b .endpr32 340 | .endif2pr32: 341 | ldr r2, =(Pico+0x22200) 342 | ldr r1, [r2, #4] @r1 = Pico.romsize 343 | cmp r0, r1 344 | bcs .endif3pr32 345 | ldr r2, [r2] @r2 = Pico.rom 346 | add r0, r2, r0 347 | ldrh r1, [r0] @Pico.rom+a 348 | ldrh r2, [r0, #2] @Pico.rom+a+2 349 | orr r0, r2, r1, lsl #16 @ r0 = (pm[0]<<16)|pm[1] 350 | b .endpr32 351 | .endif3pr32: 352 | add r4, r0, #2 353 | bl OtherRead16 354 | mov r1, r0 355 | mov r0, r4 356 | bl OtherRead16 357 | orr r0, r0, r1, lsl #16 @ r0 = (OtherRead16(a)<<16) | OtherRead16(a+2) 358 | .endpr32: 359 | ldmfd sp!, {r4,pc} 360 | -------------------------------------------------------------------------------- /arm9/source/pico/Pico.c: -------------------------------------------------------------------------------- 1 | // This is part of Pico Library 2 | 3 | // Most code (c) Copyright 2004 Dave, All rights reserved. 4 | // Some code (c) Copyright 2006 notaz, All rights reserved. 5 | // Free for non-commercial use. 6 | 7 | // For commercial use, separate licencing terms must be obtained. 8 | 9 | 10 | #include "PicoInt.h" 11 | #include "sound/sound.h" 12 | 13 | #include "Timer.h" 14 | #ifdef PROFILE 15 | #include 16 | #include 17 | #endif 18 | 19 | #ifdef ARM9_SOUND 20 | #define SOUND_OPT 0xf 21 | #else 22 | #define SOUND_OPT 0 23 | #endif 24 | 25 | #ifdef SW_SCAN_RENDERER 26 | #define VIDEO_OPT 0 27 | #else 28 | #define VIDEO_OPT 0x10 29 | #endif 30 | 31 | #ifdef SW_SCAN_RENDERER 32 | void PicoFrameFull() {} 33 | #endif 34 | 35 | int PicoVer=0x0080; 36 | struct Pico Pico; 37 | // int PicoOpt=0xffffffff; // enable everything 38 | // int PicoOpt=0x10; // frame-based rendering 39 | // int PicoOpt = 0; // enable nothing, scanline rendering (broken in new ver) 40 | // int PicoOpt=0x17; // mono sound 41 | // int PicoOpt=0x1f; // stereo sound 42 | int PicoOpt = VIDEO_OPT | SOUND_OPT; 43 | int PicoSkipFrame=0; // skip rendering frame? 44 | static int cycles_68k_vblank,cycles_z80_vblank,cycles_68k_block,cycles_z80_block,cycles_z80_line,z80lines; 45 | 46 | // notaz: sram 47 | struct PicoSRAM SRam; 48 | 49 | int PicoPad[2]; // Joypads, format is SACB RLDU 50 | 51 | // to be called once on emu init 52 | int PicoInit() 53 | { 54 | // Blank space for state: 55 | memset(&Pico,0,sizeof(Pico)); 56 | memset(&PicoPad,0,sizeof(PicoPad)); 57 | 58 | // Init CPUs: 59 | SekInit(); 60 | #ifdef ARM9_SOUND 61 | z80_init(); // init even if we aren't going to use it 62 | #endif 63 | 64 | // Setup memory callbacks: 65 | PicoMemInit(); 66 | 67 | // notaz: sram 68 | SRam.data=0; 69 | SRam.resize=1; 70 | 71 | return 0; 72 | } 73 | 74 | // to be called once on emu exit 75 | void PicoExit() 76 | { 77 | #ifdef ARM9_SOUND 78 | z80_exit(); 79 | #endif 80 | 81 | // notaz: sram 82 | if(SRam.data) free(SRam.data); SRam.data=0; 83 | } 84 | 85 | int PicoReset(int hard) 86 | { 87 | unsigned int region=0; 88 | int support=0,hw=0,i=0; 89 | unsigned char pal=0; 90 | 91 | #ifdef PROFILE 92 | Timer_Init(); 93 | #endif 94 | 95 | if (Pico.romsize<=0) return 1; 96 | 97 | SekReset(); 98 | #ifdef ARM9_SOUND 99 | z80_reset(); 100 | #endif 101 | 102 | // reset VDP state, VRAM and PicoMisc 103 | memset(&Pico.video,0,sizeof(Pico.video)); 104 | memset(&Pico.vram,0,sizeof(Pico.vram)); 105 | memset(&Pico.m,0,sizeof(Pico.m)); 106 | 107 | if(hard) { 108 | // clear all memory of the emulated machine 109 | memset(&Pico.ram,0,(unsigned int)&Pico.rom-(unsigned int)&Pico.ram); 110 | } 111 | 112 | // Read cartridge region data: 113 | region=PicoRead32(0x1f0); 114 | 115 | for (i=0;i<4;i++) 116 | { 117 | int c=0; 118 | 119 | c=region>>(i<<3); c&=0xff; 120 | if (c<=' ') continue; 121 | 122 | if (c=='J') support|=1; 123 | else if (c=='U') support|=4; 124 | else if (c=='E') support|=8; 125 | else 126 | { 127 | // New style code: 128 | char s[2]={0,0}; 129 | s[0]=(char)c; 130 | support|=strtol(s,NULL,16); 131 | } 132 | } 133 | 134 | // Try to pick the best hardware value for English/50hz: 135 | if (support&8) { hw=0xc0; pal=1; } // Europe 136 | else if (support&4) hw=0x80; // USA 137 | else if (support&2) { hw=0x40; pal=1; } // Japan PAL 138 | else if (support&1) hw=0x00; // Japan NTSC 139 | else hw=0x80; // USA 140 | 141 | Pico.m.hardware=(unsigned char)(hw|0x20); // No disk attached 142 | Pico.m.pal=pal; 143 | Pico.video.status &= ~1; 144 | Pico.video.status |= pal; 145 | 146 | #ifdef ARM9_SOUND 147 | sound_reset(PicoOpt); 148 | #endif 149 | 150 | // notaz: sram 151 | if(SRam.resize) { 152 | int sram_size = 0; 153 | if(*(Pico.rom+0x1B1) == 'R' && *(Pico.rom+0x1B0) == 'A' && (*(Pico.rom+0x1B3) & 0x40)) { 154 | SRam.start = PicoRead32(0x1B4) & 0xFFFF00; 155 | SRam.end = PicoRead32(0x1B8); 156 | sram_size = SRam.end - SRam.start + 1; 157 | } 158 | 159 | if(sram_size <= 0 || sram_size >= 0x40000) { 160 | SRam.start = 0x200000; 161 | SRam.end = 0x203FFF; 162 | sram_size = 0x004000; 163 | } 164 | 165 | if(SRam.data) free(SRam.data); 166 | SRam.data = (unsigned char *) calloc(sram_size, 1); 167 | if(!SRam.data) return 1; 168 | SRam.resize=0; 169 | } 170 | 171 | // enable sram access by default if it doesn't overlap with ROM 172 | if(Pico.romsize <= SRam.start) Pico.m.sram_reg = 1; 173 | 174 | for (int i = 1; i <= 7; i++) { 175 | Pico.m.romBank[i-1] = i; 176 | } 177 | extern void initCachedPages(void); 178 | initCachedPages(); 179 | 180 | return 0; 181 | } 182 | 183 | static int CheckIdle() 184 | { 185 | #if 1 186 | unsigned char state[0x88]; 187 | 188 | memset(state,0,sizeof(state)); 189 | 190 | // See if the state is the same after 2 steps: 191 | SekState(state); SekRun(0); SekRun(0); SekState(state+0x44); 192 | if (memcmp(state,state+0x44,0x44)==0) return 1; 193 | #else 194 | unsigned char state[0x44]; 195 | static unsigned char oldstate[0x44]; 196 | 197 | SekState(state); 198 | if(memcmp(state,oldstate,0x40)==0) return 1; 199 | memcpy(oldstate, state, 0x40); 200 | #endif 201 | 202 | return 0; 203 | } 204 | 205 | // Accurate but slower frame which does hints 206 | static int PicoFrameHints() 207 | { 208 | struct PicoVideo *pv=&Pico.video; 209 | int total=0,total_z80=0,aim=0,aim_z80=0,cycles_68k,cycles_z80,loop_from; 210 | int y=0; 211 | int hint=0x400; // Hint counter 212 | 213 | if(Pico.m.pal) { 214 | cycles_68k = (int) ((double) OSC_PAL / 7 / 50 / 312 + 0.4); // should compile to a constant 215 | cycles_z80 = (int) ((double) OSC_PAL / 15 / 50 / 312 + 0.4); 216 | loop_from = -88; 217 | } else { 218 | cycles_68k = (int) ((double) OSC_NTSC / 7 / 60 / 262 + 0.4); // 488 219 | cycles_z80 = (int) ((double) OSC_NTSC / 15 / 60 / 262 + 0.4); // 228 220 | loop_from = -38; 221 | } 222 | 223 | pv->status|=0x08; // Go into vblank 224 | 225 | #ifdef ARM9_SOUND 226 | if(PicoOpt&4) 227 | z80_resetCycles(); 228 | #endif 229 | 230 | for (y=loop_from;y<224;y++) 231 | { 232 | // pad delay (for 6 button pads) 233 | if(PicoOpt&0x20) { 234 | if(Pico.m.padDelay[0]++ > 25) Pico.m.padTHPhase[0]=0; 235 | //if(Pico.m.padDelay[1]++ > 25) Pico.m.padTHPhase[1]=0; 236 | } 237 | 238 | if (y==0) 239 | { 240 | hint=pv->reg[10]; // Load H-Int counter 241 | if (pv->reg[1]&0x40) pv->status&=~8; // Come out of vblank if display enabled 242 | SekInterrupt(0); // Cancel v interrupt 243 | pv->status&=~0x80; 244 | // draw a frame just after vblank in anternative render mode 245 | if(!PicoSkipFrame && (PicoOpt&0x10)) 246 | PicoFrameFull(); 247 | } 248 | 249 | aim+=cycles_68k; 250 | // run for a little while in hblank state 251 | //pv->status|=4; 252 | //total+=SekRun(aim-total-404); 253 | //pv->status&=~4; 254 | 255 | // H-Interrupts: 256 | if (hint < 0) 257 | { 258 | hint=pv->reg[10]; // Reload H-Int counter 259 | if (pv->reg[0]&0x10) SekInterrupt(4); 260 | } 261 | 262 | // V-Interrupt: 263 | if (y == loop_from+1) // == loop_from ? 264 | { 265 | pv->status|=0x80; // V-Int happened 266 | if(pv->reg[1]&0x20) SekInterrupt(6); 267 | #ifdef ARM9_SOUND 268 | if(Pico.m.z80Run && (PicoOpt&4)) z80_int(); 269 | #endif 270 | } 271 | 272 | Pico.m.scanline=(short)y; 273 | 274 | // Run scanline: 275 | total+=SekRun(aim-total); 276 | #ifdef ARM9_SOUND 277 | if((PicoOpt&4) && Pico.m.z80Run) { 278 | aim_z80+=cycles_z80; 279 | total_z80+=z80_run(aim_z80-total_z80); 280 | } 281 | 282 | if(PicoOpt&1) 283 | sound_timers_and_dac(y-loop_from); 284 | #endif 285 | 286 | // even if we are in PAL mode, we draw 224 lines only (although we sould do 240 in some cases) 287 | if(y>=0 && !PicoSkipFrame && !(PicoOpt&0x10)) PicoLine(y); 288 | 289 | hint--; 290 | } 291 | 292 | return 0; 293 | } 294 | 295 | // Simple frame without H-Ints 296 | static int PicoFrameSimple() 297 | { 298 | #ifdef ARM9_SOUND 299 | int total_z80=0,aim_z80=0,z80line=0; 300 | #endif 301 | int total,aim,y,sects; 302 | 303 | if(Pico.m.pal && z80lines!=88) { 304 | cycles_68k_vblank = (int) ((double) OSC_PAL / 7 / 50 / 312 + 0.4) * 88; 305 | cycles_z80_vblank = (int) ((double) OSC_PAL / 15 / 50 / 312 + 0.4) * 88; 306 | cycles_68k_block = (int) ((double) OSC_PAL / 7 / 50 / 312 + 0.4) * 14; 307 | cycles_z80_block = (int) ((double) OSC_PAL / 15 / 50 / 312 + 0.4) * 14; 308 | cycles_z80_line = (int) ((double) OSC_PAL / 15 / 50 / 312 + 0.4); 309 | z80lines = 88; 310 | } else if (!Pico.m.pal && z80lines!=38) { 311 | cycles_68k_vblank = (int) ((double) OSC_NTSC / 7 / 60 / 262 + 0.4) * 38; // 7790 312 | cycles_z80_vblank = (int) ((double) OSC_NTSC / 15 / 60 / 262 + 0.4) * 38; 313 | cycles_68k_block = (int) ((double) OSC_NTSC / 7 / 60 / 262 + 0.4) * 14; 314 | cycles_z80_block = (int) ((double) OSC_NTSC / 15 / 60 / 262 + 0.4) * 14; 315 | cycles_z80_line = (int) ((double) OSC_NTSC / 15 / 60 / 262 + 0.4); // 228 316 | z80lines = 38; 317 | } 318 | 319 | Pico.m.scanline=-100; 320 | 321 | #ifdef ARM9_SOUND 322 | if(PicoOpt&4) 323 | z80_resetCycles(); 324 | #endif 325 | 326 | // V-Blanking period: 327 | if (Pico.video.reg[1]&0x20) SekInterrupt(6); // Set IRQ 328 | Pico.video.status|=0x88; // V-Int happened / go into vblank 329 | 330 | // SetupProfile(); 331 | 332 | // BeginProfile(); 333 | total=SekRun(cycles_68k_vblank); 334 | 335 | #ifdef ARM9_SOUND 336 | if((PicoOpt&4) && Pico.m.z80Run) { 337 | z80_int(); 338 | if(PicoOpt&1) { 339 | // we have ym2612 enabled, so we have to run Z80 in lines, so we could update DAC and timers 340 | for(; z80line < z80lines; z80line++) { 341 | aim_z80+=cycles_z80_line; 342 | total_z80+=z80_run(aim_z80-total_z80); 343 | sound_timers_and_dac(z80line); 344 | } 345 | } else { 346 | aim_z80=cycles_z80_vblank; 347 | total_z80=z80_run(aim_z80); 348 | } 349 | } 350 | #endif 351 | 352 | // 6 button pad: let's just say it timed out now 353 | Pico.m.padTHPhase[0]=Pico.m.padTHPhase[1]=0; 354 | 355 | // Active Scan: 356 | if (Pico.video.reg[1]&0x40) Pico.video.status&=~8; // Come out of vblank if display is enabled 357 | SekInterrupt(0); // Clear IRQ 358 | 359 | // Run in sections: 360 | for (aim=cycles_68k_vblank+cycles_68k_block, sects=16; sects; aim+=cycles_68k_block, sects--) 361 | { 362 | if (CheckIdle()) break; 363 | 364 | total+=SekRun(aim-total); 365 | #ifdef ARM9_SOUND 366 | if((PicoOpt&4) && Pico.m.z80Run) { 367 | if(PicoOpt&1) { 368 | z80lines += 14; // 14 lines per section 369 | for(; z80line < z80lines; z80line++) { 370 | aim_z80+=cycles_z80_line; 371 | total_z80+=z80_run(aim_z80-total_z80); 372 | sound_timers_and_dac(z80line); 373 | } 374 | } else { 375 | aim_z80+=cycles_z80_block; 376 | total_z80+=z80_run(aim_z80-total_z80); 377 | } 378 | } 379 | #endif 380 | } 381 | 382 | #ifdef ARM9_SOUND 383 | // todo: detect z80 idle too? 384 | if(sects && (PicoOpt&5) == 5 && Pico.m.z80Run) { 385 | z80lines += 14*sects; 386 | for(; z80line < z80lines; z80line++) { 387 | aim_z80+=cycles_z80_line; 388 | total_z80+=z80_run(aim_z80-total_z80); 389 | sound_timers_and_dac(z80line); 390 | } 391 | } 392 | #endif 393 | 394 | // Dave was running 17 sections, but it looks like there should be only 16 395 | // it is probably some sort of overhead of this method, 396 | // because some games (Sensible Soccer, Second Samurai) no longer boot without this 397 | if(!sects) { 398 | aim+=cycles_68k_block; 399 | total+=SekRun(aim-total); 400 | #ifdef ARM9_SOUND 401 | if((PicoOpt&4) && Pico.m.z80Run) { 402 | aim_z80+=cycles_z80_block; 403 | total_z80+=z80_run(aim_z80-total_z80); 404 | } 405 | #endif 406 | } 407 | // EndProfile("Emulation"); 408 | 409 | // BeginProfile(); 410 | if(!PicoSkipFrame) { 411 | if(!(PicoOpt&0x10)) 412 | // Draw the screen 413 | for (y=0;y<224;y++) PicoLine(y); 414 | else PicoFrameFull(); 415 | } 416 | // EndProfile("PicoFrameFull"); 417 | 418 | return 0; 419 | } 420 | 421 | int PicoFrame() 422 | { 423 | int hints; 424 | 425 | //if (Pico.rom==NULL) return 1; // No Rom plugged in 426 | 427 | //PmovUpdate(); 428 | 429 | hints=Pico.video.reg[0]&0x10; 430 | // don't use hints in alternative render mode, as hint effects will not be rendered anyway 431 | if(PicoOpt&0x10) hints = 0; 432 | 433 | //if(Pico.video.reg[12]&0x2) Pico.video.status ^= 0x10; // change odd bit in interlace mode 434 | 435 | // clear sound buffer 436 | #ifdef ARM9_SOUND 437 | if(PsndOut) memset(PsndOut, 0, (PicoOpt & 8) ? (PsndLen<<2) : (PsndLen<<1)); 438 | #endif 439 | 440 | if(hints) 441 | PicoFrameHints(); 442 | else PicoFrameSimple(); 443 | 444 | #ifdef ARM9_SOUND 445 | if(PsndOut) sound_render(); 446 | #endif 447 | 448 | return 0; 449 | } 450 | 451 | static int DefaultCram(int cram) 452 | { 453 | int high=0x0841; 454 | // Convert 0000bbbb ggggrrrr 455 | // to rrrr1ggg g10bbbb1 456 | high|=(cram&0x00f)<<12; // Red 457 | high|=(cram&0x0f0)<< 3; // Green 458 | high|=(cram&0xf00)>> 7; // Blue 459 | return high; 460 | } 461 | 462 | // Cram directly to BMP16 BG values for writing to an exrot BG 463 | static int BGCram(int cram) 464 | { 465 | // Convert 0000bbbbggggrrrr 466 | // to 1bbbb0gggg0rrrr0 467 | // 0000111100000000 B 3840 468 | // 0000000011110000 G 240 469 | // 0000000000001111 R 15 470 | // and w/ 1111101111011110 64478 471 | // or w/ 1000000000000000 32768 472 | return 32768|(((cram&3840)<<3)|((cram&240)<<2)|((cram&15)<<1)); 473 | } 474 | 475 | // Function to convert Megadrive Cram into a native colour: 476 | // int (*PicoCram)(int cram)=DefaultCram; 477 | int (*PicoCram)(int cram)=BGCram; 478 | -------------------------------------------------------------------------------- /arm9/source/pico/Pico.h: -------------------------------------------------------------------------------- 1 | 2 | // -------------------- Pico Library -------------------- 3 | 4 | // Pico Library - Header File 5 | 6 | // Most code (c) Copyright 2004 Dave, All rights reserved. 7 | // Some code (c) Copyright 2006 notaz, All rights reserved. 8 | // Free for non-commercial use. 9 | 10 | // For commercial use, separate licencing terms must be obtained. 11 | 12 | #include 13 | #include 14 | // #include "fat/gba_nds_fat.h" 15 | #include "config.h" 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | // Pico.cpp 22 | extern int PicoVer; 23 | extern int PicoOpt; // bits LSb->MSb: enable_ym2612&dac, enable_sn76496, enable_z80, stereo_sound, alt_renderer, 6button_gamepad 24 | extern int PicoSkipFrame; // skip rendering frame, but still do sound (if enabled) and emulation stuff 25 | int PicoInit(); 26 | void PicoExit(); 27 | int PicoReset(int hard); 28 | int PicoFrame(); 29 | extern int PicoPad[2]; // Joypads, format is MXYZ SACB RLDU 30 | extern int (*PicoCram)(int cram); // Callback to convert colour ram 0000bbb0 ggg0rrr0 31 | 32 | // Area.cpp 33 | typedef size_t (arearw)(void *p, size_t _size, size_t _n, void *file); 34 | struct PicoArea { void *data; int len; char *name; }; 35 | extern int (*PicoAcb)(struct PicoArea *); // Area callback for each block of memory 36 | extern void *PmovFile; // this can be FILE* or gzFile 37 | extern arearw *areaRead; // read and write function pointers for 38 | extern arearw *areaWrite; // gzip save state ability 39 | extern int PmovAction; 40 | // &1=for reading &2=for writing &4=volatile &8=non-volatile 41 | //int PicoAreaScan(int action,int *pmin); 42 | // Save or load the state from PmovFile: 43 | int PmovState(); 44 | int PmovUpdate(); 45 | 46 | // Cart.c 47 | int PicoCartLoad(FILE *f,unsigned char **prom,unsigned int *psize); 48 | int PicoCartInsert(unsigned char *rom,unsigned int romsize); 49 | // notaz 50 | int CartLoadZip(const char *fname, unsigned char **prom, unsigned int *psize); 51 | void Byteswap(unsigned char *data,int len); 52 | int DecodeSmd(unsigned char *data,int len); 53 | 54 | // Draw.c 55 | extern int (*PicoScan)(unsigned int num,unsigned short *data); 56 | //extern int PicoMask; // Mask of which layers to draw // notaz: removed because it is unused anyway 57 | 58 | // Draw2.c 59 | // stuff below is optional 60 | extern unsigned short *PicoCramHigh; // pointer to CRAM buff (0x40 shorts), converted to native device color (works only with 16bit for now) 61 | extern void (*PicoPrepareCram)(); // prepares PicoCramHigh for renderer to use 62 | 63 | // Sek.c 64 | extern char PicoStatus[]; 65 | 66 | // Psnd.c 67 | //extern unsigned char PicoSreg[]; 68 | 69 | // sound.c 70 | extern int PsndRate,PsndLen; 71 | extern short *PsndOut; 72 | void sound_reset(int mask); 73 | void z80_pack(unsigned char *data); 74 | void z80_unpack(unsigned char *data); 75 | void z80_reset(); 76 | 77 | // Utils.c 78 | extern int PicuAnd; 79 | int PicuQuick(unsigned short *dest,unsigned short *src); 80 | int PicuShrink(unsigned short *dest,int destLen,unsigned short *src,int srcLen); 81 | int PicuShrinkReverse(unsigned short *dest,int destLen,unsigned short *src,int srcLen); 82 | int PicuMerge(unsigned short *dest,int destLen,unsigned short *src,int srcLen); 83 | 84 | #ifdef __cplusplus 85 | } // End of extern "C" 86 | #endif 87 | -------------------------------------------------------------------------------- /arm9/source/pico/PicoInt.h: -------------------------------------------------------------------------------- 1 | // Pico Library - Header File 2 | 3 | // Most code (c) Copyright 2004 Dave, All rights reserved. 4 | // Some code (c) Copyright 2006 notaz, All rights reserved. 5 | // Free for non-commercial use. 6 | 7 | // For commercial use, separate licencing terms must be obtained. 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | #include "Pico.h" 14 | 15 | 16 | // test 17 | //#ifdef ARM 18 | //#include "../clibc/faststr.h" 19 | //#endif 20 | 21 | #if defined(__MARM__) || defined(_WIN32_WCE) || defined(ARM9) 22 | #define EMU_C68K // Use the Cyclone 68000 emulator 23 | #else 24 | #define EMU_A68K // Use the 'A68K' (Make68K) Assembler 68000 emulator 25 | #endif 26 | 27 | #if defined(ARM) || defined(GP32) || defined (__MARM__) || defined(ARM9) 28 | #define CPU_CALL 29 | #else 30 | #define CPU_CALL __fastcall 31 | #endif 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | 38 | // ----------------------- 68000 CPU ----------------------- 39 | #ifdef EMU_A68K 40 | // The format of the data in a68k.asm (at the _M68000_regs location) 41 | struct A68KContext 42 | { 43 | unsigned int d[8],a[8]; 44 | unsigned int isp,srh,ccr,xc,pc,irq,sr; 45 | int (*IrqCallback) (int nIrq); 46 | unsigned int ppc; 47 | void *pResetCallback; 48 | unsigned int sfc,dfc,usp,vbr; 49 | unsigned int AsmBank,CpuVersion; 50 | }; 51 | struct A68KContext M68000_regs; 52 | extern int m68k_ICount; 53 | #define SekCycles m68k_ICount // cycles left for this line 54 | #endif 55 | 56 | #ifdef EMU_C68K 57 | #include "cyclone/Cyclone.h" 58 | extern struct Cyclone PicoCpu; 59 | #define SekCycles PicoCpu.cycles 60 | #endif 61 | // --------------------------------------------------------- 62 | 63 | // main oscillator clock which controls timing 64 | #define OSC_NTSC 53693100 65 | #define OSC_PAL 53203424 66 | 67 | struct PicoVideo 68 | { 69 | unsigned char reg[0x20]; 70 | unsigned int command; // 32-bit Command 71 | unsigned char pending; // 1 if waiting for second half of 32-bit command 72 | unsigned char type; // Command type (v/c/vsram read/write) 73 | unsigned short addr; // Read/Write address 74 | int status; // Status bits 75 | unsigned char pad[0x14]; 76 | }; 77 | 78 | struct PicoMisc 79 | { 80 | unsigned char rotate; 81 | unsigned char z80Run; 82 | unsigned char padTHPhase[2]; // phase of gamepad TH switches 83 | short scanline; // -38||-88 to 223 84 | char dirtyPal; // Is the palette dirty 85 | unsigned char hardware; // Hardware value for country 86 | unsigned char pal; // 1=PAL 0=NTSC 87 | unsigned char sram_reg; // notaz: SRAM mode register. bit0: allow read? bit1: deny write? 88 | unsigned short z80_bank68k; 89 | unsigned short z80_lastaddr; // this is for Z80 faking 90 | unsigned short z80_fakeval; 91 | unsigned char padDelay[2]; // gamepad phase time outs, so we count a delay 92 | unsigned char sram_changed; 93 | unsigned char pad1[0xd]; 94 | int romBank[7]; 95 | }; 96 | 97 | // some assembly stuff depend on these, do not touch! 98 | struct Pico 99 | { 100 | unsigned char ram[0x10000]; // 0x00000 scratch ram 101 | unsigned short vram[0x8000]; // 0x10000 102 | unsigned char zram[0x2000]; // 0x20000 Z80 ram 103 | unsigned char ioports[0x10]; 104 | unsigned int pad[0x3c]; // unused 105 | unsigned short cram[0x40]; // 0x22100 106 | unsigned short vsram[0x40]; // 0x22180 107 | 108 | unsigned char *rom; // 0x22200 109 | unsigned int romsize; // 0x22204 110 | 111 | struct PicoMisc m; 112 | struct PicoVideo video; 113 | }; 114 | 115 | // notaz: sram 116 | struct PicoSRAM 117 | { 118 | unsigned char *data; // actual data 119 | unsigned int start; // start address in 68k address space 120 | unsigned int end; 121 | unsigned char resize; // 1=SRAM size changed and needs to be reallocated on PicoReset 122 | unsigned char pad[3]; 123 | }; 124 | 125 | // Draw.c 126 | int PicoLine(int scan); 127 | 128 | // Draw2.c 129 | void PicoFrameFull(); 130 | 131 | // Memory.c 132 | int PicoInitPc(u32 pc); 133 | unsigned short CPU_CALL PicoRead16(u32 a); 134 | u32 CPU_CALL PicoRead32(u32 a); 135 | int PicoMemInit(); 136 | void PicoDasm(int start,int len); 137 | unsigned char z80_read(unsigned short a); 138 | unsigned short z80_read16(unsigned short a); 139 | void z80_write(unsigned char data, unsigned short a); 140 | void z80_write16(unsigned short data, unsigned short a); 141 | 142 | // Pico.c 143 | extern struct Pico Pico; 144 | extern struct PicoSRAM SRam; 145 | 146 | // Sek.c 147 | int SekInit(); 148 | int SekReset(); 149 | int SekRun(int cyc); 150 | int SekInterrupt(int irq); 151 | int SekPc(); 152 | void SekState(unsigned char *data); 153 | 154 | // Sine.c 155 | //extern short Sine[]; 156 | 157 | // Psnd.c 158 | //int PsndReset(); 159 | //int PsndFm(int a,int d); 160 | //int PsndRender(); 161 | 162 | // VideoPort.c 163 | void PicoVideoWrite(unsigned int a,unsigned int d); 164 | unsigned int PicoVideoRead(unsigned int a); 165 | 166 | #ifdef __DEBUG_PRINT 167 | // External: 168 | //int dprintf(char *Format, ...); 169 | void dprintf(char *format, ...); 170 | #endif 171 | 172 | #ifdef __cplusplus 173 | } // End of extern "C" 174 | #endif 175 | -------------------------------------------------------------------------------- /arm9/source/pico/Sek.c: -------------------------------------------------------------------------------- 1 | // This is part of Pico Library 2 | 3 | // Most code (c) Copyright 2004 Dave, All rights reserved. 4 | // Some code (c) Copyright 2006 notaz, All rights reserved. 5 | // Free for non-commercial use. 6 | 7 | // For commercial use, separate licencing terms must be obtained. 8 | 9 | 10 | #include "PicoInt.h" 11 | 12 | 13 | #ifdef EMU_C68K 14 | // ---------------------- Cyclone 68000 ---------------------- 15 | 16 | struct Cyclone PicoCpu; 17 | 18 | int SekInit() 19 | { 20 | // CycloneInit(); 21 | memset(&PicoCpu,0,sizeof(PicoCpu)); 22 | return 0; 23 | } 24 | 25 | // Reset the 68000: 26 | int SekReset() 27 | { 28 | if (Pico.rom==NULL) return 1; 29 | 30 | memset(&PicoCpu,0,PicoCpu.pad1-PicoCpu.d); // clear all regs 31 | PicoCpu.stopped=0; 32 | PicoCpu.srh =0x27; // Supervisor mode 33 | PicoCpu.a[7]=PicoCpu.read32(0); // Stack Pointer 34 | PicoCpu.membase=0; 35 | PicoCpu.pc=PicoCpu.checkpc(PicoCpu.read32(4)); // Program Counter 36 | 37 | return 0; 38 | } 39 | 40 | 41 | // Run the 68000 for 'cyc' number of cycles and return the number of cycles actually executed 42 | static inline int DoRun(int cyc) 43 | { 44 | PicoCpu.cycles=cyc; 45 | CycloneRun(&PicoCpu); 46 | return cyc-PicoCpu.cycles; 47 | } 48 | 49 | int SekInterrupt(int irq) 50 | { 51 | PicoCpu.irq=irq; // cancel 52 | return 0; 53 | } 54 | 55 | int SekPc() { return PicoCpu.pc-PicoCpu.membase; } 56 | 57 | void SekState(unsigned char *data) 58 | { 59 | memcpy(data,PicoCpu.d,0x44); 60 | } 61 | 62 | #endif 63 | 64 | #ifdef EMU_A68K 65 | // ---------------------- A68K ---------------------- 66 | 67 | void __cdecl M68000_RUN(); 68 | void __cdecl M68000_RESET(); 69 | int m68k_ICount=0; 70 | unsigned int mem_amask=0xffffff; // 24-bit bus 71 | unsigned int mame_debug=0,cur_mrhard=0,m68k_illegal_opcode=0,illegal_op=0,illegal_pc=0,opcode_entry=0; // filler 72 | 73 | static int IrqCallback(int i) { i; return -1; } 74 | static int DoReset() { return 0; } 75 | static int (*ResetCallback)()=DoReset; 76 | 77 | #pragma warning (disable:4152) 78 | 79 | int SekInit() 80 | { 81 | memset(&M68000_regs,0,sizeof(M68000_regs)); 82 | M68000_regs.IrqCallback=IrqCallback; 83 | M68000_regs.pResetCallback=ResetCallback; 84 | M68000_RESET(); // Init cpu emulator 85 | return 0; 86 | } 87 | 88 | int SekReset() 89 | { 90 | // Reset CPU: fetch SP and PC 91 | M68000_regs.srh=0x27; // Supervisor mode 92 | M68000_regs.a[7]=PicoRead32(0); 93 | M68000_regs.pc =PicoRead32(4); 94 | PicoInitPc(M68000_regs.pc); 95 | 96 | return 0; 97 | } 98 | 99 | static __inline int DoRun(int cyc) 100 | { 101 | m68k_ICount=cyc; 102 | M68000_RUN(); 103 | return cyc-m68k_ICount; 104 | } 105 | 106 | int SekInterrupt(int irq) 107 | { 108 | M68000_regs.irq=irq; // raise irq (gets lowered after taken) 109 | return 0; 110 | } 111 | 112 | int SekPc() { return M68000_regs.pc; } 113 | 114 | void SekState(unsigned char *data) 115 | { 116 | memcpy(data, M68000_regs.d, 0x40); 117 | memcpy(data+0x40,&M68000_regs.pc,0x04); 118 | } 119 | 120 | #endif 121 | 122 | #ifdef EMU_NULL 123 | // ----------------------------------------------------------- 124 | int SekInit() { return 0; } 125 | int SekReset() { return 0; } 126 | static inline int DoRun(int cyc) { return cyc; } 127 | int SekInterrupt(int) { return 0; } 128 | 129 | int SekPc() 130 | { 131 | return 0; 132 | } 133 | 134 | void SekState(unsigned char *) { } 135 | 136 | #endif 137 | 138 | int SekRun(int cyc) 139 | { 140 | int did=0; 141 | 142 | did=DoRun(cyc); 143 | 144 | return did; 145 | } 146 | -------------------------------------------------------------------------------- /arm9/source/pico/Timer.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* Lazy: 4 | * Since TIMER1_DATA will overflow in 65536 milliseconds, it is necessary 5 | * to add 65536 to this variable every time TIMER1_DATA overflows. 6 | * So, the total time elapsed since Timer_Init in milliseconds is g_timerBaseMS + TIMER1_DATA. 7 | */ 8 | u32 g_timerBaseMS = 0; 9 | 10 | /* nds_timer1_overflow: 11 | * Adds 65536 to the base millisecond counter so we 12 | * don't lose any time when TIMER1_DATA rolls over. 13 | */ 14 | static void nds_timer1_overflow( void ) { 15 | g_timerBaseMS+= 65536; 16 | } 17 | 18 | /* Timer_GetMS: 19 | * Returns: The time in milliseconds since Timer_Init was called. 20 | */ 21 | int Timer_GetMS( void ) { 22 | return g_timerBaseMS + TIMER1_DATA; 23 | } 24 | 25 | /* Timer_Sleep: 26 | * Waits until ( usec ) microseconds have passed. 27 | */ 28 | void Timer_Sleep( int usec ) { 29 | swiDelay( usec ); 30 | } 31 | 32 | /* Timer_Init: 33 | * Initialize NDS hardware timers. 34 | */ 35 | void Timer_Init( void ) { 36 | /* Timer0 will overflow roughly every 0.98 milliseconds */ 37 | TIMER0_CR = TIMER_ENABLE | TIMER_DIV_1; 38 | TIMER0_DATA = 32768; 39 | 40 | /* When timer0 overflows, TIMER1_DATA will be incremented by 1. 41 | * When timer1 overflows 65536 is added to g_timerBaseMS so we don't lose 42 | * any time. 43 | */ 44 | TIMER1_CR = TIMER_CASCADE | TIMER_IRQ_REQ; 45 | TIMER1_DATA = 0; 46 | 47 | /* Set and enable the interrupts for timer 1 */ 48 | irqSet( IRQ_TIMER1, nds_timer1_overflow ); 49 | irqEnable( IRQ_TIMER1 ); 50 | } 51 | -------------------------------------------------------------------------------- /arm9/source/pico/Timer.h: -------------------------------------------------------------------------------- 1 | extern u32 g_timerBaseMS; 2 | void Timer_Init(); 3 | #define MILLISECOND_COUNTER ( TIMER1_DATA + g_timerBaseMS ) 4 | 5 | #ifndef PROFILE 6 | #define SetupProfile( ) 7 | #define BeginProfile( ) 8 | #define EndProfile(fname) 9 | #else 10 | #define SetupProfile( ) int ____startMS_ = 0; 11 | #define BeginProfile( ) ____startMS_ = MILLISECOND_COUNTER; 12 | #define EndProfile(fname) iprintf( "Function %s took %dms \n", fname, ( MILLISECOND_COUNTER - ____startMS_ ) ); 13 | #endif 14 | -------------------------------------------------------------------------------- /arm9/source/pico/Utils.c: -------------------------------------------------------------------------------- 1 | // This is part of Pico Library 2 | 3 | // Most code (c) Copyright 2004 Dave, All rights reserved. 4 | // Some code (c) Copyright 2006 notaz, All rights reserved. 5 | // Free for non-commercial use. 6 | 7 | // For commercial use, separate licencing terms must be obtained. 8 | 9 | 10 | #include "PicoInt.h" 11 | 12 | int PicuAnd=0xf7de; 13 | 14 | // Quick low-quality conversion of 320 to 176: 15 | int PicuQuick(unsigned short *dest,unsigned short *src) 16 | { 17 | unsigned short *end=NULL; 18 | 19 | src+=13; end=src+290; 20 | dest++; 21 | 22 | do 23 | { 24 | *dest++=*src++; 25 | *dest++=*src; src+=2; 26 | *dest++=*src; src+=2; 27 | *dest++=*src++; 28 | *dest++=*src; src+=2; 29 | *dest++=*src; src+=2; 30 | } 31 | while (src>=1; bias+=destLen; } 49 | *dest++=(unsigned short)pa; 50 | 51 | pa=*src++; bias-=sub; 52 | if (bias<0) { pa+=*src++; pa>>=1; bias+=destLen; } 53 | *dest++=(unsigned short)pa; 54 | } 55 | while (dest>=1; bias+=destLen; } 73 | *(--dest)=(unsigned short)pa; 74 | 75 | pa=*src++; bias-=sub; 76 | if (bias<0) { pa+=*src++; pa>>=1; bias+=destLen; } 77 | *(--dest)=(unsigned short)pa; 78 | } 79 | while (dest>end); 80 | 81 | return 0; 82 | } 83 | 84 | int PicuMerge(unsigned short *dest,int destLen,unsigned short *src,int srcLen) 85 | { 86 | unsigned short *end=NULL; 87 | int bias=0,pa=0,mask=PicuAnd,sub=0; 88 | 89 | end=dest+destLen; 90 | sub=srcLen-destLen; 91 | 92 | do 93 | { 94 | pa=*src++; bias-=sub; 95 | if (bias<0) { pa+=*src++; pa>>=1; bias+=destLen; } 96 | pa&=mask; pa+=(*dest)&mask; pa>>=1; 97 | *dest++=(unsigned short)pa; 98 | 99 | pa=*src++; bias-=sub; 100 | if (bias<0) { pa+=*src++; pa>>=1; bias+=destLen; } 101 | pa&=mask; pa+=(*dest)&mask; pa>>=1; 102 | *dest++=(unsigned short)pa; 103 | } 104 | while (dest>8); // If address is odd, bytes are swapped 26 | a>>=1; 27 | 28 | switch (Pico.video.type) 29 | { 30 | case 1: Pico.vram [a&0x7fff]=sd; break; 31 | case 3: Pico.cram [a&0x003F]=sd; break; 32 | case 5: Pico.vsram[a&0x003f]=sd; break; 33 | } 34 | 35 | AutoIncrement(); 36 | } 37 | */ 38 | 39 | unsigned int VideoRead(); 40 | /* 41 | static unsigned int VideoRead() 42 | { 43 | unsigned int a=0,d=0; 44 | 45 | a=Pico.video.addr; a>>=1; 46 | 47 | switch (Pico.video.type) 48 | { 49 | case 0: d=Pico.vram [a&0x7fff]; break; 50 | case 8: d=Pico.cram [a&0x003f]; break; 51 | case 4: d=Pico.vsram[a&0x003f]; break; 52 | } 53 | 54 | AutoIncrement(); 55 | return d; 56 | } 57 | */ 58 | 59 | int GetDmaSource(); 60 | /* 61 | static int GetDmaSource() 62 | { 63 | struct PicoVideo *pvid=&Pico.video; 64 | int source=0; 65 | source =pvid->reg[0x15]<<1; 66 | source|=pvid->reg[0x16]<<9; 67 | source|=pvid->reg[0x17]<<17; 68 | return source; 69 | } 70 | */ 71 | 72 | int GetDmaLength(); 73 | /* 74 | static int GetDmaLength() 75 | { 76 | struct PicoVideo *pvid=&Pico.video; 77 | int len=0; 78 | // 16-bit words to transfer: 79 | len =pvid->reg[0x13]; 80 | len|=pvid->reg[0x14]<<8; 81 | return len; 82 | } 83 | */ 84 | 85 | static void DmaSlow(int source,int len) 86 | { 87 | int i=0,max=0; 88 | 89 | if (source>=0x800000 && source<0xe00000) return; // Invalid source address 90 | 91 | /// Clip Cram DMA size (Todds Adventures in Slime World): 92 | if (Pico.video.type==3) { max=0x80-Pico.video.addr; if (len>max) len=max; } 93 | 94 | for (i=0;i>=1; // Length specifies number of bytes 106 | 107 | for (i=0;i> 8) & 0xFF); 122 | 123 | len=GetDmaLength(); 124 | 125 | //for (i=0;i<=len;i++) VideoWrite(data); 126 | 127 | // from Charles MacDonald's genvdp.txt: 128 | // Write lower byte to address specified 129 | vram[*a ^ 1] = (unsigned char) (data & 0xFF); 130 | 131 | for(i=0;ireg[1]&0x10)==0) return; // DMA not enabled 147 | 148 | len=GetDmaLength(); 149 | 150 | method=pvid->reg[0x17]>>6; 151 | source=GetDmaSource(); 152 | if (method< 2) DmaSlow(source,len); // 68000 to VDP 153 | if (method==3) DmaCopy(source,len); // VRAM Copy 154 | } 155 | 156 | static void CommandChange() 157 | { 158 | struct PicoVideo *pvid=&Pico.video; 159 | unsigned int cmd=0,addr=0; 160 | 161 | cmd=pvid->command; 162 | 163 | // Get type of transfer 0xc0000030 (v/c/vsram read/write) 164 | pvid->type=(unsigned char)(((cmd>>2)&0xc)|(cmd>>30)); 165 | 166 | // Get address 0x3fff0003 167 | addr =(cmd>>16)&0x3fff; 168 | addr|=(cmd<<14)&0xc000; 169 | pvid->addr=(unsigned short)addr; 170 | 171 | // Check for dma: 172 | if (cmd&0x80) CommandDma(); 173 | } 174 | 175 | void PicoVideoWrite(unsigned int a,unsigned int d) 176 | { 177 | struct PicoVideo *pvid=&Pico.video; 178 | 179 | a&=0x1c; 180 | d=(unsigned short)d; 181 | 182 | if (a==0x00) // Data port 0 or 2 183 | { 184 | if (pvid->pending) CommandChange(); 185 | pvid->pending=0; 186 | 187 | // If a DMA fill has been set up, do it 188 | if ((pvid->command&0x80) && (pvid->reg[1]&0x10) && (pvid->reg[0x17]>>6)==2) 189 | { 190 | DmaFill(d); 191 | } 192 | else 193 | { 194 | VideoWrite(d); 195 | } 196 | return; 197 | } 198 | 199 | if (a==0x04) // Command port 4 or 6 200 | { 201 | if (pvid->pending) 202 | { 203 | // Low word of command: 204 | pvid->command&=0xffff0000; 205 | pvid->command|=d; 206 | pvid->pending=0; 207 | CommandChange(); 208 | return; 209 | } 210 | 211 | if ((d&0xc000)==0x8000) 212 | { 213 | // Register write: 214 | int num=(d>>8)&0x1f; 215 | pvid->reg[num]=(unsigned char)d; 216 | return; 217 | } 218 | 219 | // High word of command: 220 | pvid->command&=0x0000ffff; 221 | pvid->command|=d<<16; 222 | pvid->pending=1; 223 | } 224 | } 225 | 226 | unsigned int PicoVideoRead(unsigned int a); 227 | /* 228 | unsigned int PicoVideoRead(unsigned int a) 229 | { 230 | unsigned int d=0; 231 | 232 | a&=0x1c; 233 | 234 | if (a==0x00) { d=VideoRead(); goto end; } 235 | 236 | if (a==0x04) 237 | { 238 | d=Pico.video.status; 239 | d|=0x3400; // always set bits 240 | d|=0x0020; // sprite collision 241 | 242 | // Toggle fifo full empty (who uses that stuff?): 243 | if (Pico.m.rotate&4) d|=0x0100; else d|=0x0200; 244 | if (Pico.m.rotate&2) d|=0x0004; // Toggle in/out of H-Blank 245 | Pico.m.rotate++; 246 | 247 | goto end; 248 | } 249 | 250 | if ((a&0x1c)==0x08) 251 | { 252 | if (Pico.m.scanline>-100) d=Pico.m.scanline; // V-Counter 253 | else d=Pico.m.rotate++; // Fudge 254 | 255 | d&=0xff; d<<=8; 256 | d|=((500-SekCycles)>>1)&0xff; // very preliminary H-counter. Seems to be enough for Comix Zone/Sonic 3D Blast 257 | goto end; 258 | } 259 | 260 | end: 261 | 262 | return d; 263 | } 264 | */ -------------------------------------------------------------------------------- /arm9/source/pico/sound/DrZ80.h: -------------------------------------------------------------------------------- 1 | 2 | /* DrZ80 - Header File */ 3 | 4 | /* All code (c) Copyright 2004 Reesy, All rights reserved. */ 5 | /* DrZ80 is free for non-commercial use. */ 6 | 7 | /* For commercial use, separate licencing terms must be obtained. */ 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | #ifndef DRZ80_H 14 | #define DRZ80_H 15 | 16 | extern int DrZ80Ver; /* Version number of library */ 17 | 18 | struct DrZ80 19 | { 20 | unsigned int Z80A; // 0x00 - A Register: 0xAA------ 21 | unsigned int Z80F; // 0x04 - F Register: 0x------FF 22 | unsigned int Z80BC; // 0x08 - BC Registers: 0xBBCC---- 23 | unsigned int Z80DE; // 0x0C - DE Registers: 0xDDEE---- 24 | unsigned int Z80HL; // 0x10 - HL Registers: 0xHHLL---- 25 | unsigned int Z80PC; // 0x14 - PC Program Counter (Memory Base + PC) 26 | unsigned int Z80PC_BASE; // 0x18 - PC Program Counter (Memory Base) 27 | unsigned int Z80SP; // 0x1C - SP Stack Pointer (Memory Base + PC) 28 | unsigned int Z80SP_BASE; // 0x20 - SP Stack Pointer (Memory Base) 29 | unsigned int Z80IX; // 0x24 - IX Index Register 30 | unsigned int Z80IY; // 0x28 - IY Index Register 31 | unsigned int Z80I; // 0x2C - I Interrupt Register 32 | unsigned int Z80A2; // 0x30 - A' Register: 0xAA------ 33 | unsigned int Z80F2; // 0x34 - F' Register: 0x------FF 34 | unsigned int Z80BC2; // 0x38 - B'C' Registers: 0xBBCC---- 35 | unsigned int Z80DE2; // 0x3C - D'E' Registers: 0xDDEE---- 36 | unsigned int Z80HL2; // 0x40 - H'L' Registers: 0xHHLL---- 37 | unsigned char Z80_IRQ; // 0x44 - Set IRQ Number 38 | unsigned char Z80IF; // 0x45 - Interrupt Flags: bit1=_IFF1, bit2=_IFF2, bit3=_HALT 39 | unsigned char Z80IM; // 0x46 - Set IRQ Mode 40 | unsigned char spare; // 0x47 - N/A 41 | unsigned int z80irqvector; // 0x48 - Set IRQ Vector i.e. 0xFF=RST 42 | void (*z80_irq_callback )(void); 43 | void (*z80_write8 )(unsigned char d,unsigned short a); 44 | void (*z80_write16 )(unsigned short d,unsigned short a); 45 | unsigned char (*z80_in)(unsigned char p); 46 | void (*z80_out )(unsigned char p,unsigned char d); 47 | unsigned char (*z80_read8)(unsigned short a); 48 | unsigned short (*z80_read16)(unsigned short a); 49 | unsigned int (*z80_rebaseSP)(unsigned short new_sp); 50 | unsigned int (*z80_rebasePC)(unsigned short new_pc); 51 | }; 52 | 53 | // returns number of cycles left (usually negative) 54 | extern int DrZ80Run(struct DrZ80 *pcy,unsigned int cyc); 55 | 56 | #endif 57 | 58 | #ifdef __cplusplus 59 | } /* End of extern "C" */ 60 | #endif 61 | -------------------------------------------------------------------------------- /arm9/source/pico/sound/DrZ80.txt: -------------------------------------------------------------------------------- 1 | 2 | ___________________________________________________________________________ 3 | 4 | DrZ80 (c) Copyright 2004 Reesy. Free for non-commercial use 5 | 6 | Reesy's e-mail: drsms_reesy(atsymbol)yahoo.co.uk 7 | Replace (atsymbol) with @ 8 | 9 | ___________________________________________________________________________ 10 | 11 | 12 | What is it? 13 | ----------- 14 | 15 | DrZ80 is an emulator for the Z80 microprocessor, written in ARM 32-bit assembly. 16 | It is aimed at chips such as ARM7 and ARM9 cores, StrongARM and XScale, to interpret Z80 17 | code as fast as possible. 18 | 19 | Flags are mapped onto ARM flags whenever possible, which speeds up the processing of an opcode. 20 | 21 | ARM Register Usage 22 | ------------------ 23 | 24 | See source code for up to date of register usage, however a summary is here: 25 | 26 | r0-3: Temporary registers 27 | r3 : Pointer to Opcode Jump table 28 | r4 : T-States remaining 29 | r5 : Pointer to Cpu Context 30 | r6 : Current PC + Memory Base (i.e. pointer to next opcode) 31 | r7 : Z80 A Register in top 8 bits (i.e. 0xAA000000) 32 | r8 : Z80 F Register (Flags) (NZCV) in lowest four bits 33 | r9 : Z80 BC Register pair in top 16 bits (i.e. 0xBBCC0000) 34 | r10 : Z80 DE Register pair in top 16 bits (i.e. 0xDDEE0000) 35 | r11 : Z80 HL Register pair in top 16 bits (i.e. 0xHHLL0000) 36 | r12 : Z80 Stack + Memory Base (i.e. pointer to current stack in host system memory) 37 | 38 | ( note: r3,r12 are always preserved when calling external memory functions ) 39 | 40 | How to Compile 41 | -------------- 42 | 43 | The core is targeted for the GNU compiler, so to compile just add the "DrZ80.o" object 44 | to your makefile and that should be it. 45 | 46 | If you want to compile it seperately, use: as -o DrZ80.o DrZ80.s 47 | 48 | ( note: If you want to use DrZ80 with a different compiler you will need to run 49 | some sort of parser through the source to make the syntax of the source 50 | compatible with your target compiler ) 51 | 52 | 53 | Adding to your project 54 | ---------------------- 55 | 56 | To add DrZ80 to your project, add DrZ80.o, and include DrZ80.h 57 | There is one structure: 'struct DrZ80', and three functions: DrZ80Run,DrZ80RaiseInt 58 | and DrZ80_irq_callback. 59 | 60 | Don't worry if this seem very minimal - its all you need to run as many Z80s as you want. 61 | It works with both C and C++. 62 | 63 | ( Note: DrZ80_irq_callback is just a pointer to an irq call back function that needs 64 | to be written by you ) 65 | 66 | Declaring a Memory handlers 67 | --------------------------- 68 | 69 | Before you can reset or execute Z80 opcodes you must first set up a set of memory handlers. 70 | There are 8 functions you have to set up per CPU, like this: 71 | 72 | unsigned int z80_rebaseSP(unsigned short new_sp); 73 | unsigned int z80_rebasePC(unsigned short new_pc); 74 | unsigned char z80_read8(unsigned short a); 75 | unsigned short z80_read16(unsigned short a); 76 | void z80_write8(unsigned char d,unsigned short a); 77 | void z80_write16(unsigned short d,unsigned short a); 78 | unsigned char z80_in(unsigned char p); 79 | void z80_out(unsigned char p,unsigned char d); 80 | 81 | You can think of these functions representing the Z80's memory bus. 82 | The Read and Write functions are called whenever the Z80 reads or writes memory. 83 | The In and Out functions are called whenever the Z80 uses the I/O ports. 84 | The z80_rebasePC and z80_rebaseSP functions are to do with creating direct memory 85 | pointers in the host machines memory, I will explain more about this later. 86 | 87 | Declaring a CPU Context 88 | ----------------------- 89 | 90 | To declare a CPU simple declare a struct Cyclone in your code. For example to declare 91 | two Z80s: 92 | 93 | struct DrZ80 MyCpu; 94 | struct DrZ80 MyCpu2; 95 | 96 | It's probably a good idea to initialise the memory to zero: 97 | 98 | memset(&MyCpu, 0,sizeof(MyCpu)); 99 | memset(&MyCpu2,0,sizeof(MyCpu2)); 100 | 101 | Next point to your memory handlers: 102 | 103 | MyCpu.z80_rebasePC=z80_rebasePC; 104 | MyCpu.z80_rebaseSP=z80_rebaseSP; 105 | MyCpu.z80_read8 =z80_read8; 106 | MyCpu.z80_read16 =z80_read16; 107 | MyCpu.z80_write8 =z80_write8; 108 | MyCpu.z80_write16 =z80_write16; 109 | MyCpu.z80_in =z80_in; 110 | MyCpu.z80_out =z80_out; 111 | 112 | Now you are nearly ready to reset the Z80, except you need one more function: checkpc(). 113 | 114 | The z80_rebasePC() function 115 | --------------------------- 116 | 117 | When DrZ80 reads opcodes, it doesn't use a memory handler every time, this would be 118 | far too slow, instead it uses a direct pointer to ARM memory. 119 | For example if your Rom image was at 0x3000000 and the program counter was $206, 120 | Cyclone's program counter would be 0x3000206. 121 | 122 | The difference between an ARM address and a Z80 address is also stored in a variable called 123 | 'pc_membase'. In the above example it's 0x3000000. To retrieve the real PC, DrZ80 just 124 | subtracts 'pc_membase'. 125 | 126 | Everytime the Z80 PC is modified, i.e. by a jump,branch intructions or by an interupt, DrZ80 127 | calls the z80_rebasePC function. If the PC is in a different bank, for example Ram instead 128 | of Rom, change 'pc_membase', recalculate the new PC and return it. 129 | 130 | The z80_rebaseSP() function 131 | --------------------------- 132 | 133 | When DrZ80 pushs/pops to the Z80 stack pointer, it doesn't use a memory handler every time. In 134 | order to gain more speed a direct pointer to ARM memory is used. For example if your Ram was at 135 | 0x3000000 and the z80 stack pointer counter was 0xD000, DrZ80's stack pointer would be 0x300D000. 136 | 137 | The difference between an ARM address and a Z80 address is also stored in a variable called 138 | 'sp_membase'. In the above example it's 0x3000000. To retrieve the real SP, DrZ80 just 139 | subtracts 'sp_membase'. 140 | 141 | Everytime the Z80 SP is modified ( i.e. set with a new value LD SP,NN etc ) DrZ80 142 | calls the z80_rebaseSP function. If the SP is in a different bank, for example Rom instead 143 | of Ram, change 'sp_membase', recalculate the new SP and return it. 144 | 145 | -------------------------------------------------------------------------------- /arm9/source/pico/sound/driver.h: -------------------------------------------------------------------------------- 1 | #ifndef __GNUC__ 2 | #pragma warning (disable:4100) 3 | #pragma warning (disable:4244) 4 | #pragma warning (disable:4245) 5 | #pragma warning (disable:4710) 6 | #pragma warning (disable:4018) // signed/unsigned 7 | #endif 8 | 9 | /* compiler dependence */ 10 | #ifndef OSD_CPU_H 11 | #define OSD_CPU_H 12 | typedef unsigned char UINT8; /* unsigned 8bit */ 13 | typedef unsigned short UINT16; /* unsigned 16bit */ 14 | typedef unsigned int UINT32; /* unsigned 32bit */ 15 | typedef signed char INT8; /* signed 8bit */ 16 | typedef signed short INT16; /* signed 16bit */ 17 | typedef signed int INT32; /* signed 32bit */ 18 | #endif 19 | 20 | #define LSB_FIRST 1 21 | 22 | #ifndef INLINE 23 | #define INLINE static __inline 24 | #endif 25 | 26 | 27 | #ifdef __DEBUG_PRINT 28 | // External: 29 | //int dprintf(char *Format, ...); 30 | void dprintf2(char *format, ...); 31 | #endif 32 | 33 | -------------------------------------------------------------------------------- /arm9/source/pico/sound/mz80.h: -------------------------------------------------------------------------------- 1 | /* Multi-Z80 32 Bit emulator */ 2 | 3 | /* Copyright 1996, Neil Bradley, All rights reserved 4 | * 5 | * License agreement: 6 | * 7 | * The mZ80 emulator may be distributed in unmodified form to any medium. 8 | * 9 | * mZ80 May not be sold, or sold as a part of a commercial package without 10 | * the express written permission of Neil Bradley (neil@synthcom.com). This 11 | * includes shareware. 12 | * 13 | * Modified versions of mZ80 may not be publicly redistributed without author 14 | * approval (neil@synthcom.com). This includes distributing via a publicly 15 | * accessible LAN. You may make your own source modifications and distribute 16 | * mZ80 in object only form. 17 | * 18 | * mZ80 Licensing for commercial applications is available. Please email 19 | * neil@synthcom.com for details. 20 | * 21 | * Synthcom Systems, Inc, and Neil Bradley will not be held responsible for 22 | * any damage done by the use of mZ80. It is purely "as-is". 23 | * 24 | * If you use mZ80 in a freeware application, credit in the following text: 25 | * 26 | * "Multi-Z80 CPU emulator by Neil Bradley (neil@synthcom.com)" 27 | * 28 | * must accompany the freeware application within the application itself or 29 | * in the documentation. 30 | * 31 | * Legal stuff aside: 32 | * 33 | * If you find problems with mZ80, please email the author so they can get 34 | * resolved. If you find a bug and fix it, please also email the author so 35 | * that those bug fixes can be propogated to the installed base of mZ80 36 | * users. If you find performance improvements or problems with mZ80, please 37 | * email the author with your changes/suggestions and they will be rolled in 38 | * with subsequent releases of mZ80. 39 | * 40 | * The whole idea of this emulator is to have the fastest available 32 bit 41 | * Multi-z80 emulator for the PC, giving maximum performance. 42 | */ 43 | 44 | /* General z80 based defines */ 45 | 46 | #ifndef _MZ80_H_ 47 | #define _MZ80_H_ 48 | 49 | #ifndef UINT32 50 | #define UINT32 unsigned long int 51 | #endif 52 | 53 | #ifndef UINT16 54 | #define UINT16 unsigned short int 55 | #endif 56 | 57 | #ifndef UINT8 58 | #define UINT8 unsigned char 59 | #endif 60 | 61 | #ifndef INT32 62 | #define INT32 signed long int 63 | #endif 64 | 65 | #ifndef INT16 66 | #define INT16 signed short int 67 | #endif 68 | 69 | #ifndef INT8 70 | #define INT8 signed char 71 | #endif 72 | 73 | #ifdef __cplusplus 74 | extern "C" { 75 | #endif 76 | 77 | #ifndef _MEMORYREADWRITEBYTE_ 78 | #define _MEMORYREADWRITEBYTE_ 79 | 80 | struct MemoryWriteByte 81 | { 82 | UINT32 lowAddr; 83 | UINT32 highAddr; 84 | void (*memoryCall)(UINT32, UINT8, struct MemoryWriteByte *); 85 | void *pUserArea; 86 | }; 87 | 88 | struct MemoryReadByte 89 | { 90 | UINT32 lowAddr; 91 | UINT32 highAddr; 92 | UINT8 (*memoryCall)(UINT32, struct MemoryReadByte *); 93 | void *pUserArea; 94 | }; 95 | 96 | #endif // _MEMORYREADWRITEBYTE_ 97 | 98 | struct z80PortWrite 99 | { 100 | UINT16 lowIoAddr; 101 | UINT16 highIoAddr; 102 | void (*IOCall)(UINT16, UINT8, struct z80PortWrite *); 103 | void *pUserArea; 104 | }; 105 | 106 | struct z80PortRead 107 | { 108 | UINT16 lowIoAddr; 109 | UINT16 highIoAddr; 110 | UINT16 (*IOCall)(UINT16, struct z80PortRead *); 111 | void *pUserArea; 112 | }; 113 | 114 | struct z80TrapRec 115 | { 116 | UINT16 trapAddr; 117 | UINT8 skipCnt; 118 | UINT8 origIns; 119 | }; 120 | 121 | typedef union 122 | { 123 | UINT32 af; 124 | 125 | struct 126 | { 127 | #ifdef WORDS_BIGENDIAN 128 | UINT16 wFiller; 129 | UINT8 a; 130 | UINT8 f; 131 | #else 132 | UINT8 f; 133 | UINT8 a; 134 | UINT16 wFiller; 135 | #endif 136 | } half; 137 | } reg_af; 138 | 139 | #define z80AF z80af.af 140 | #define z80A z80af.half.a 141 | #define z80F z80af.half.f 142 | 143 | typedef union 144 | { 145 | UINT32 bc; 146 | 147 | struct 148 | { 149 | #ifdef WORDS_BIGENDIAN 150 | UINT16 wFiller; 151 | UINT8 b; 152 | UINT8 c; 153 | #else 154 | UINT8 c; 155 | UINT8 b; 156 | UINT16 wFiller; 157 | #endif 158 | } half; 159 | } reg_bc; 160 | 161 | #define z80BC z80bc.bc 162 | #define z80B z80bc.half.b 163 | #define z80C z80bc.half.c 164 | 165 | typedef union 166 | { 167 | UINT32 de; 168 | 169 | struct 170 | { 171 | #ifdef WORDS_BIGENDIAN 172 | UINT16 wFiller; 173 | UINT8 d; 174 | UINT8 e; 175 | #else 176 | UINT8 e; 177 | UINT8 d; 178 | UINT16 wFiller; 179 | #endif 180 | } half; 181 | } reg_de; 182 | 183 | #define z80DE z80de.de 184 | #define z80D z80de.half.d 185 | #define z80E z80de.half.e 186 | 187 | typedef union 188 | { 189 | UINT32 hl; 190 | 191 | struct 192 | { 193 | #ifdef WORDS_BIGENDIAN 194 | UINT16 wFiller; 195 | UINT8 h; 196 | UINT8 l; 197 | #else 198 | UINT8 l; 199 | UINT8 h; 200 | UINT16 wFiller; 201 | #endif 202 | } half; 203 | } reg_hl; 204 | 205 | #define z80HL z80hl.hl 206 | #define z80H z80hl.half.h 207 | #define z80L z80hl.half.l 208 | 209 | #define z80SP z80sp.sp 210 | 211 | typedef union 212 | { 213 | UINT32 ix; 214 | 215 | struct 216 | { 217 | #ifdef WORDS_BIGENDIAN 218 | UINT16 wFiller; 219 | UINT8 xh; 220 | UINT8 xl; 221 | #else 222 | UINT8 xl; 223 | UINT8 xh; 224 | UINT16 wFiller; 225 | #endif 226 | } half; 227 | } reg_ix; 228 | 229 | #define z80IX z80ix.ix 230 | #define z80XH z80ix.half.xh 231 | #define z80XL z80ix.half.xl 232 | 233 | typedef union 234 | { 235 | UINT32 iy; 236 | 237 | struct 238 | { 239 | #ifdef WORDS_BIGENDIAN 240 | UINT16 wFiller; 241 | UINT8 yh; 242 | UINT8 yl; 243 | #else 244 | UINT8 yl; 245 | UINT8 yh; 246 | UINT16 wFiller; 247 | #endif 248 | } half; 249 | } reg_iy; 250 | 251 | #define z80IY z80iy.iy 252 | #define z80YH z80iy.half.yh 253 | #define z80YL z80iy.half.yl 254 | 255 | struct mz80context 256 | { 257 | UINT8 *z80Base; 258 | struct MemoryReadByte *z80MemRead; 259 | struct MemoryWriteByte *z80MemWrite; 260 | struct z80PortRead *z80IoRead; 261 | struct z80PortWrite *z80IoWrite; 262 | UINT32 z80clockticks; 263 | UINT32 z80iff; 264 | UINT32 z80interruptMode; 265 | UINT32 z80halted; 266 | 267 | reg_af z80af; 268 | reg_bc z80bc; 269 | reg_de z80de; 270 | reg_hl z80hl; 271 | UINT32 z80afprime; 272 | UINT32 z80bcprime; 273 | UINT32 z80deprime; 274 | UINT32 z80hlprime; 275 | reg_ix z80ix; 276 | reg_iy z80iy; 277 | UINT32 z80sp; 278 | UINT32 z80pc; 279 | UINT32 z80nmiAddr; 280 | UINT32 z80intAddr; 281 | UINT32 z80rCounter; 282 | UINT8 z80i; 283 | UINT8 z80r; 284 | UINT8 z80intPending; 285 | }; 286 | 287 | // These are the enumerations used for register access. DO NOT ALTER THEIR 288 | // ORDER! It must match the same order as in the mz80.c/mz80.asm files! 289 | 290 | enum 291 | { 292 | #ifndef CPUREG_PC 293 | CPUREG_PC = 0, 294 | #endif 295 | CPUREG_Z80_AF = 1, 296 | CPUREG_Z80_BC, 297 | CPUREG_Z80_DE, 298 | CPUREG_Z80_HL, 299 | CPUREG_Z80_AFPRIME, 300 | CPUREG_Z80_BCPRIME, 301 | CPUREG_Z80_DEPRIME, 302 | CPUREG_Z80_HLPRIME, 303 | CPUREG_Z80_IX, 304 | CPUREG_Z80_IY, 305 | CPUREG_Z80_SP, 306 | CPUREG_Z80_I, 307 | CPUREG_Z80_R, 308 | CPUREG_Z80_A, 309 | CPUREG_Z80_B, 310 | CPUREG_Z80_C, 311 | CPUREG_Z80_D, 312 | CPUREG_Z80_E, 313 | CPUREG_Z80_H, 314 | CPUREG_Z80_L, 315 | CPUREG_Z80_F, 316 | CPUREG_Z80_CARRY, 317 | CPUREG_Z80_NEGATIVE, 318 | CPUREG_Z80_PARITY, 319 | CPUREG_Z80_OVERFLOW, 320 | CPUREG_Z80_HALFCARRY, 321 | CPUREG_Z80_ZERO, 322 | CPUREG_Z80_SIGN, 323 | CPUREG_Z80_IFF1, 324 | CPUREG_Z80_IFF2, 325 | 326 | // Leave this here! 327 | 328 | CPUREG_Z80_MAX_INDEX 329 | }; 330 | 331 | extern UINT32 mz80exec(UINT32); 332 | extern UINT32 mz80GetContextSize(void); 333 | extern UINT32 mz80GetElapsedTicks(UINT32); 334 | extern void mz80ReleaseTimeslice(void); 335 | extern void mz80GetContext(void *); 336 | extern void mz80SetContext(void *); 337 | extern void mz80reset(void); 338 | extern void mz80ClearPendingInterrupt(void); 339 | extern UINT32 mz80int(UINT32); 340 | extern UINT32 mz80nmi(void); 341 | extern void mz80init(void); 342 | extern void mz80shutdown(void); 343 | extern UINT32 z80intAddr; 344 | extern UINT32 z80nmiAddr; 345 | 346 | // Debugger useful routines 347 | 348 | extern UINT8 mz80SetRegisterValue(void *, UINT32, UINT32); 349 | extern UINT32 mz80GetRegisterValue(void *, UINT32); 350 | extern UINT32 mz80GetRegisterTextValue(void *, UINT32, UINT8 *); 351 | extern UINT8 *mz80GetRegisterName(UINT32); 352 | 353 | // Memory/IO read/write commands 354 | 355 | #ifndef VALUE_BYTE 356 | #define VALUE_BYTE 0 357 | #endif 358 | 359 | #ifndef VALUE_WORD 360 | #define VALUE_WORD 1 361 | #endif 362 | 363 | #ifndef VALUE_DWORD 364 | #define VALUE_DWORD 2 365 | #endif 366 | 367 | #ifndef VALUE_IO 368 | #define VALUE_IO 3 369 | #endif 370 | 371 | extern void mz80WriteValue(UINT8 bWhat, UINT32 dwAddr, UINT32 dwData); 372 | extern UINT32 mz80ReadValue(UINT8 bWhat, UINT32 dwAddr); 373 | 374 | // Flag definitions 375 | 376 | #define Z80_FLAG_CARRY 0x01 377 | #define Z80_FLAG_NEGATIVE 0x02 378 | #define Z80_FLAG_OVERFLOW_PARITY 0x04 379 | #define Z80_FLAG_UNDEFINED1 0x08 380 | #define Z80_FLAG_HALF_CARRY 0x10 381 | #define Z80_FLAG_UNDEFINED2 0x20 382 | #define Z80_FLAG_ZERO 0x40 383 | #define Z80_FLAG_SIGN 0x80 384 | 385 | #define IFF1 0x01 386 | #define IFF2 0x02 387 | 388 | typedef struct mz80context CONTEXTMZ80; 389 | 390 | #ifdef __cplusplus 391 | }; 392 | #endif 393 | 394 | #endif // _MZ80_H_ 395 | -------------------------------------------------------------------------------- /arm9/source/pico/sound/sn76496.c: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | 3 | sn76496.c 4 | 5 | Routines to emulate the Texas Instruments SN76489 / SN76496 programmable 6 | tone /noise generator. Also known as (or at least compatible with) TMS9919. 7 | 8 | Noise emulation is not accurate due to lack of documentation. The noise 9 | generator uses a shift register with a XOR-feedback network, but the exact 10 | layout is unknown. It can be set for either period or white noise; again, 11 | the details are unknown. 12 | 13 | 28/03/2005 : Sebastien Chevalier 14 | Update th SN76496Write func, according to SN76489 doc found on SMSPower. 15 | - On write with 0x80 set to 0, when LastRegister is other then TONE, 16 | the function is similar than update with 0x80 set to 1 17 | ***************************************************************************/ 18 | 19 | #include "driver.h" 20 | #include "sn76496.h" 21 | 22 | #ifdef ARM9_SOUND 23 | 24 | #define MAX_OUTPUT 0x47ff // was 0x7fff 25 | 26 | #define STEP 0x10000 27 | 28 | 29 | /* Formulas for noise generator */ 30 | /* bit0 = output */ 31 | 32 | /* noise feedback for white noise mode (verified on real SN76489 by John Kortink) */ 33 | #define FB_WNOISE 0x14002 /* (16bits) bit16 = bit0(out) ^ bit2 ^ bit15 */ 34 | 35 | /* noise feedback for periodic noise mode */ 36 | //#define FB_PNOISE 0x10000 /* 16bit rorate */ 37 | #define FB_PNOISE 0x08000 /* JH 981127 - fixes Do Run Run */ 38 | 39 | /* 40 | 0x08000 is definitely wrong. The Master System conversion of Marble Madness 41 | uses periodic noise as a baseline. With a 15-bit rotate, the bassline is 42 | out of tune. 43 | The 16-bit rotate has been confirmed against a real PAL Sega Master System 2. 44 | Hope that helps the System E stuff, more news on the PSG as and when! 45 | */ 46 | 47 | /* noise generator start preset (for periodic noise) */ 48 | #define NG_PRESET 0x0f35 49 | 50 | 51 | struct SN76496 52 | { 53 | //sound_stream * Channel; 54 | int SampleRate; 55 | int VolTable[16]; /* volume table */ 56 | int Register[8]; /* registers */ 57 | unsigned int UpdateStep; 58 | int LastRegister; /* last register written */ 59 | int Volume[4]; /* volume of voice 0-2 and noise */ 60 | unsigned int RNG; /* noise generator */ 61 | int NoiseFB; /* noise feedback mask */ 62 | int Period[4]; 63 | int Count[4]; 64 | int Output[4]; 65 | }; 66 | 67 | static struct SN76496 ono_sn; // one and only SN76496 68 | int *sn76496_regs; 69 | 70 | //static 71 | void SN76496Write(int data) 72 | { 73 | struct SN76496 *R = &ono_sn; 74 | int n; 75 | 76 | 77 | /* update the output buffer before changing the registers */ 78 | //stream_update(R->Channel,0); 79 | 80 | if (data & 0x80) 81 | { 82 | int r = (data & 0x70) >> 4; 83 | int c = r/2; 84 | 85 | R->LastRegister = r; 86 | R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f); 87 | switch (r) 88 | { 89 | case 0: /* tone 0 : frequency */ 90 | case 2: /* tone 1 : frequency */ 91 | case 4: /* tone 2 : frequency */ 92 | R->Period[c] = R->UpdateStep * R->Register[r]; 93 | if (R->Period[c] == 0) R->Period[c] = R->UpdateStep; 94 | if (r == 4) 95 | { 96 | /* update noise shift frequency */ 97 | if ((R->Register[6] & 0x03) == 0x03) 98 | R->Period[3] = 2 * R->Period[2]; 99 | } 100 | break; 101 | case 1: /* tone 0 : volume */ 102 | case 3: /* tone 1 : volume */ 103 | case 5: /* tone 2 : volume */ 104 | case 7: /* noise : volume */ 105 | R->Volume[c] = R->VolTable[data & 0x0f]; 106 | break; 107 | case 6: /* noise : frequency, mode */ 108 | { 109 | int n = R->Register[6]; 110 | R->NoiseFB = (n & 4) ? FB_WNOISE : FB_PNOISE; 111 | n &= 3; 112 | /* N/512,N/1024,N/2048,Tone #3 output */ 113 | R->Period[3] = ((n&3) == 3) ? 2 * R->Period[2] : (R->UpdateStep << (5+(n&3))); 114 | 115 | /* reset noise shifter */ 116 | R->RNG = NG_PRESET; 117 | R->Output[3] = R->RNG & 1; 118 | } 119 | break; 120 | } 121 | } 122 | else 123 | { 124 | int r = R->LastRegister; 125 | int c = r/2; 126 | 127 | switch (r) 128 | { 129 | case 0: /* tone 0 : frequency */ 130 | case 2: /* tone 1 : frequency */ 131 | case 4: /* tone 2 : frequency */ 132 | R->Register[r] = (R->Register[r] & 0x0f) | ((data & 0x3f) << 4); 133 | R->Period[c] = R->UpdateStep * R->Register[r]; 134 | if (R->Period[c] == 0) R->Period[c] = R->UpdateStep; 135 | if (r == 4) 136 | { 137 | /* update noise shift frequency */ 138 | if ((R->Register[6] & 0x03) == 0x03) 139 | R->Period[3] = 2 * R->Period[2]; 140 | } 141 | break; 142 | case 1: /* tone 0 : volume */ 143 | case 3: /* tone 1 : volume */ 144 | case 5: /* tone 2 : volume */ 145 | case 7: /* noise : volume */ 146 | R->Volume[c] = R->VolTable[data & 0x0f]; 147 | R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f); 148 | break; 149 | case 6: /* noise : frequency, mode */ 150 | { 151 | R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f); 152 | n = R->Register[6]; 153 | R->NoiseFB = (n & 4) ? FB_WNOISE : FB_PNOISE; 154 | n &= 3; 155 | /* N/512,N/1024,N/2048,Tone #3 output */ 156 | R->Period[3] = ((n&3) == 3) ? 2 * R->Period[2] : (R->UpdateStep << (5+(n&3))); 157 | 158 | /* reset noise shifter */ 159 | R->RNG = NG_PRESET; 160 | R->Output[3] = R->RNG & 1; 161 | } 162 | break; 163 | } 164 | } 165 | } 166 | 167 | /* 168 | WRITE8_HANDLER( SN76496_0_w ) { SN76496Write(0,data); } 169 | WRITE8_HANDLER( SN76496_1_w ) { SN76496Write(1,data); } 170 | WRITE8_HANDLER( SN76496_2_w ) { SN76496Write(2,data); } 171 | WRITE8_HANDLER( SN76496_3_w ) { SN76496Write(3,data); } 172 | WRITE8_HANDLER( SN76496_4_w ) { SN76496Write(4,data); } 173 | */ 174 | 175 | //static 176 | void SN76496Update(short *buffer,int length,int stereo) 177 | { 178 | int i; 179 | struct SN76496 *R = &ono_sn; 180 | 181 | /* If the volume is 0, increase the counter */ 182 | for (i = 0;i < 4;i++) 183 | { 184 | if (R->Volume[i] == 0) 185 | { 186 | /* note that I do count += length, NOT count = length + 1. You might think */ 187 | /* it's the same since the volume is 0, but doing the latter could cause */ 188 | /* interferencies when the program is rapidly modulating the volume. */ 189 | if (R->Count[i] <= length*STEP) R->Count[i] += length*STEP; 190 | } 191 | } 192 | 193 | while (length > 0) 194 | { 195 | int vol[4]; 196 | unsigned int out; 197 | int left; 198 | 199 | 200 | /* vol[] keeps track of how long each square wave stays */ 201 | /* in the 1 position during the sample period. */ 202 | vol[0] = vol[1] = vol[2] = vol[3] = 0; 203 | 204 | for (i = 0;i < 3;i++) 205 | { 206 | if (R->Output[i]) vol[i] += R->Count[i]; 207 | R->Count[i] -= STEP; 208 | /* Period[i] is the half period of the square wave. Here, in each */ 209 | /* loop I add Period[i] twice, so that at the end of the loop the */ 210 | /* square wave is in the same status (0 or 1) it was at the start. */ 211 | /* vol[i] is also incremented by Period[i], since the wave has been 1 */ 212 | /* exactly half of the time, regardless of the initial position. */ 213 | /* If we exit the loop in the middle, Output[i] has to be inverted */ 214 | /* and vol[i] incremented only if the exit status of the square */ 215 | /* wave is 1. */ 216 | while (R->Count[i] <= 0) 217 | { 218 | R->Count[i] += R->Period[i]; 219 | if (R->Count[i] > 0) 220 | { 221 | R->Output[i] ^= 1; 222 | if (R->Output[i]) vol[i] += R->Period[i]; 223 | break; 224 | } 225 | R->Count[i] += R->Period[i]; 226 | vol[i] += R->Period[i]; 227 | } 228 | if (R->Output[i]) vol[i] -= R->Count[i]; 229 | } 230 | 231 | left = STEP; 232 | do 233 | { 234 | int nextevent; 235 | 236 | 237 | if (R->Count[3] < left) nextevent = R->Count[3]; 238 | else nextevent = left; 239 | 240 | if (R->Output[3]) vol[3] += R->Count[3]; 241 | R->Count[3] -= nextevent; 242 | if (R->Count[3] <= 0) 243 | { 244 | if (R->RNG & 1) R->RNG ^= R->NoiseFB; 245 | R->RNG >>= 1; 246 | R->Output[3] = R->RNG & 1; 247 | R->Count[3] += R->Period[3]; 248 | if (R->Output[3]) vol[3] += R->Period[3]; 249 | } 250 | if (R->Output[3]) vol[3] -= R->Count[3]; 251 | 252 | left -= nextevent; 253 | } while (left > 0); 254 | 255 | out = vol[0] * R->Volume[0] + vol[1] * R->Volume[1] + 256 | vol[2] * R->Volume[2] + vol[3] * R->Volume[3]; 257 | 258 | if (out > MAX_OUTPUT * STEP) out = MAX_OUTPUT * STEP; 259 | 260 | out /= STEP; // will be optimized to shift 261 | if(stereo) { 262 | *buffer++ += out; 263 | *buffer++ += out; 264 | } else 265 | *buffer++ += out; 266 | 267 | length--; 268 | } 269 | } 270 | 271 | 272 | static void SN76496_set_clock(struct SN76496 *R,int clock) 273 | { 274 | 275 | /* the base clock for the tone generators is the chip clock divided by 16; */ 276 | /* for the noise generator, it is clock / 256. */ 277 | /* Here we calculate the number of steps which happen during one sample */ 278 | /* at the given sample rate. No. of events = sample rate / (clock/16). */ 279 | /* STEP is a multiplier used to turn the fraction into a fixed point */ 280 | /* number. */ 281 | R->UpdateStep = ((double)STEP * R->SampleRate * 16) / clock; 282 | } 283 | 284 | 285 | static void SN76496_set_gain(struct SN76496 *R,int gain) 286 | { 287 | int i; 288 | double out; 289 | 290 | 291 | gain &= 0xff; 292 | 293 | /* increase max output basing on gain (0.2 dB per step) */ 294 | out = MAX_OUTPUT / 3; 295 | while (gain-- > 0) 296 | out *= 1.023292992; /* = (10 ^ (0.2/20)) */ 297 | 298 | /* build volume table (2dB per step) */ 299 | for (i = 0;i < 15;i++) 300 | { 301 | /* limit volume to avoid clipping */ 302 | if (out > MAX_OUTPUT / 3) R->VolTable[i] = MAX_OUTPUT / 3; 303 | else R->VolTable[i] = out; 304 | 305 | out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */ 306 | } 307 | R->VolTable[15] = 0; 308 | } 309 | 310 | 311 | //static 312 | int SN76496_init(int clock,int sample_rate) 313 | { 314 | struct SN76496 *R = &ono_sn; 315 | int i; 316 | 317 | //R->Channel = stream_create(0,1, sample_rate,R,SN76496Update); 318 | sn76496_regs = R->Register; 319 | 320 | R->SampleRate = sample_rate; 321 | SN76496_set_clock(R,clock); 322 | 323 | for (i = 0;i < 4;i++) R->Volume[i] = 0; 324 | 325 | R->LastRegister = 0; 326 | for (i = 0;i < 8;i+=2) 327 | { 328 | R->Register[i] = 0; 329 | R->Register[i + 1] = 0x0f; /* volume = 0 */ 330 | } 331 | 332 | for (i = 0;i < 4;i++) 333 | { 334 | R->Output[i] = 0; 335 | R->Period[i] = R->Count[i] = R->UpdateStep; 336 | } 337 | R->RNG = NG_PRESET; 338 | R->Output[3] = R->RNG & 1; 339 | 340 | // added 341 | SN76496_set_gain(R, 0); 342 | 343 | return 0; 344 | } 345 | 346 | #endif // ARM9_SOUND 347 | -------------------------------------------------------------------------------- /arm9/source/pico/sound/sn76496.h: -------------------------------------------------------------------------------- 1 | #ifndef SN76496_H 2 | #define SN76496_H 3 | 4 | #include "../../config.h" 5 | 6 | void SN76496Write(int data); 7 | void SN76496Update(short *buffer,int length,int stereo); 8 | int SN76496_init(int clock,int sample_rate); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /arm9/source/pico/sound/sound.c: -------------------------------------------------------------------------------- 1 | // This is part of Pico Library 2 | 3 | // Most code (c) Copyright 2004 Dave, All rights reserved. 4 | // Some code (c) Copyright 2006 notaz, All rights reserved. 5 | // Free for non-commercial use. 6 | 7 | // For commercial use, separate licencing terms must be obtained. 8 | 9 | 10 | #include 11 | #include "sound.h" 12 | #include "ym2612.h" 13 | #include "sn76496.h" 14 | 15 | #include "driver.h" 16 | #if defined(_USE_MZ80) 17 | #include "mz80.h" 18 | #elif defined(_USE_DRZ80) 19 | #include "DrZ80.h" 20 | #endif 21 | 22 | #include "../PicoInt.h" 23 | 24 | #ifdef ARM9_SOUND 25 | 26 | int rasters_total; // total num of rasters per frame 27 | 28 | // dac 29 | short *dac_out; 30 | void (*dac_next)(int raster) = 0; 31 | 32 | // for Pico 33 | int PsndRate=0; 34 | int PsndLen=0; // number of mono samples, multiply by 2 for stereo 35 | short *PsndOut=NULL; // PCM data buffer 36 | 37 | // from ym2612.c 38 | extern int *ym2612_dacen; 39 | extern INT32 *ym2612_dacout; 40 | void YM2612TimerHandler(int c,int cnt); 41 | 42 | 43 | // dac handlers, somewhat nasty 44 | void dac_next_shrink(int raster) 45 | { 46 | static int dac_cnt; 47 | 48 | if(!raster) { 49 | dac_cnt = rasters_total - PsndLen; 50 | dac_out = PsndOut; 51 | if(*ym2612_dacen) 52 | *dac_out = (short) *ym2612_dacout; // take a sample left from prev frame 53 | dac_out++; 54 | } else { 55 | // shrinking algo 56 | if(dac_cnt < 0) { 57 | // advance pointer 58 | if(*ym2612_dacen) 59 | *dac_out = (short) *ym2612_dacout; 60 | dac_out++; 61 | dac_cnt += rasters_total; 62 | } 63 | dac_cnt -= PsndLen; 64 | } 65 | } 66 | 67 | // fixme: stereo versions should also regard panning.. 68 | void dac_next_shrink_stereo(int raster) 69 | { 70 | static int dac_cnt; 71 | 72 | if(!raster) { 73 | dac_cnt = rasters_total - PsndLen; 74 | dac_out = PsndOut; 75 | if(*ym2612_dacen) 76 | *dac_out = *(dac_out+1) = (short) *ym2612_dacout; 77 | dac_out+=2; 78 | } else { 79 | if(dac_cnt < 0) { 80 | if(*ym2612_dacen) 81 | *dac_out = *(dac_out+1) = (short) *ym2612_dacout; 82 | dac_out+=2; 83 | dac_cnt += rasters_total; 84 | } 85 | dac_cnt -= PsndLen; 86 | } 87 | } 88 | 89 | void dac_next_stretch(int raster) 90 | { 91 | static int dac_cnt; 92 | 93 | if(!raster) { 94 | dac_cnt = PsndLen; 95 | dac_out = PsndOut; 96 | } 97 | 98 | if(raster == rasters_total-1) { 99 | if(*ym2612_dacen) 100 | for(; dac_out < PsndOut+PsndLen; dac_out++) 101 | *dac_out = (short) *ym2612_dacout; 102 | } else { 103 | while(dac_cnt >= 0) { 104 | dac_cnt -= rasters_total; 105 | if(*ym2612_dacen) 106 | *dac_out = (short) *ym2612_dacout; 107 | dac_out++; 108 | } 109 | } 110 | dac_cnt += PsndLen; 111 | } 112 | 113 | void dac_next_stretch_stereo(int raster) 114 | { 115 | static int dac_cnt; 116 | 117 | if(!raster) { 118 | dac_cnt = PsndLen; 119 | dac_out = PsndOut; 120 | } 121 | 122 | if(raster == rasters_total-1) { 123 | if(*ym2612_dacen) 124 | for(; dac_out < PsndOut+(PsndLen<<1); dac_out+=2) 125 | *dac_out = *(dac_out+1) = (short) *ym2612_dacout; 126 | } else { 127 | while(dac_cnt >= 0) { 128 | dac_cnt -= rasters_total; 129 | if(*ym2612_dacen) 130 | *dac_out = *(dac_out+1) = (short) *ym2612_dacout; 131 | dac_out+=2; 132 | } 133 | } 134 | dac_cnt += PsndLen; 135 | } 136 | 137 | 138 | // also re-inits masked sound chips, because PicoOpt or Pico.m.pal might have changed 139 | void sound_reset(int mask) 140 | { 141 | if(mask & 1) { 142 | YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PsndRate); 143 | YM2612ResetChip(); 144 | } 145 | 146 | if(mask & 2) 147 | SN76496_init(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PsndRate); 148 | 149 | // calculate PsndLen 150 | PsndLen=PsndRate/(Pico.m.pal ? 50 : 60); 151 | 152 | // for dac data outputing 153 | rasters_total = Pico.m.pal ? 312 : 262; 154 | if(PsndLen <= rasters_total) 155 | dac_next = (PicoOpt & 8) ? dac_next_shrink_stereo : dac_next_shrink; 156 | else dac_next = (PicoOpt & 8) ? dac_next_stretch_stereo : dac_next_stretch; 157 | } 158 | 159 | 160 | // This is called once per raster (aka line) 161 | int sound_timers_and_dac(int raster) 162 | { 163 | if(PsndOut) 164 | dac_next(raster); 165 | 166 | // Our raster lasts 63.61323/64.102564 microseconds (NTSC/PAL) 167 | YM2612PicoTick(); 168 | 169 | return 0; 170 | } 171 | 172 | 173 | int sound_render() 174 | { 175 | // PSG 176 | if(PicoOpt & 2) 177 | SN76496Update(PsndOut, PsndLen, PicoOpt & 8); 178 | 179 | // Add in the stereo FM buffer 180 | if(PicoOpt & 1) 181 | YM2612UpdateOne(PsndOut, PsndLen, PicoOpt & 8); 182 | 183 | 184 | // dave's FM 185 | //PsndRender(); 186 | 187 | return 0; 188 | } 189 | 190 | 191 | 192 | #if defined(_USE_MZ80) 193 | 194 | // memhandlers for mz80 core 195 | unsigned char mz80_read(UINT32 a, struct MemoryReadByte *w) { return z80_read(a); } 196 | void mz80_write(UINT32 a, UINT8 d, struct MemoryWriteByte *w) { z80_write(d, a); } 197 | 198 | // structures for mz80 core 199 | static struct MemoryReadByte mz80_mem_read[]= 200 | { 201 | {0x2000,0xffff,mz80_read}, 202 | {(UINT32) -1,(UINT32) -1,NULL} 203 | }; 204 | static struct MemoryWriteByte mz80_mem_write[]= 205 | { 206 | {0x2000,0xffff,mz80_write}, 207 | {(UINT32) -1,(UINT32) -1,NULL} 208 | }; 209 | static struct z80PortRead mz80_io_read[] ={ 210 | {(UINT16) -1,(UINT16) -1,NULL} 211 | }; 212 | static struct z80PortWrite mz80_io_write[]={ 213 | {(UINT16) -1,(UINT16) -1,NULL} 214 | }; 215 | 216 | #elif defined(_USE_DRZ80) 217 | 218 | static struct DrZ80 drZ80; 219 | 220 | unsigned int DrZ80_rebasePC(unsigned short a) 221 | { 222 | // don't know if this makes sense 223 | drZ80.Z80PC_BASE = (unsigned int) Pico.zram;// - (a&0xe000); 224 | return drZ80.Z80PC_BASE + a; 225 | } 226 | 227 | unsigned int DrZ80_rebaseSP(unsigned short a) 228 | { 229 | drZ80.Z80SP_BASE = (unsigned int) Pico.zram;// - (a&0xe000); 230 | return drZ80.Z80SP_BASE + a; 231 | //return a; 232 | } 233 | 234 | unsigned char DrZ80_in(unsigned char p) 235 | { 236 | return 0xff; 237 | } 238 | 239 | void DrZ80_out(unsigned char p,unsigned char d) 240 | { 241 | } 242 | 243 | void DrZ80_irq_callback() 244 | { 245 | drZ80.Z80_IRQ = 0; // lower irq when accepted 246 | } 247 | 248 | #endif 249 | 250 | // z80 functionality wrappers 251 | void z80_init() 252 | { 253 | #if defined(_USE_MZ80) 254 | struct mz80context z80; 255 | 256 | // z80 257 | mz80init(); 258 | // Modify the default context 259 | mz80GetContext(&z80); 260 | 261 | // point mz80 stuff 262 | z80.z80Base=Pico.zram; 263 | z80.z80MemRead=mz80_mem_read; 264 | z80.z80MemWrite=mz80_mem_write; 265 | z80.z80IoRead=mz80_io_read; 266 | z80.z80IoWrite=mz80_io_write; 267 | 268 | mz80SetContext(&z80); 269 | 270 | #elif defined(_USE_DRZ80) 271 | 272 | memset(&drZ80, 0, sizeof(struct DrZ80)); 273 | drZ80.z80_rebasePC=DrZ80_rebasePC; 274 | drZ80.z80_rebaseSP=DrZ80_rebaseSP; 275 | drZ80.z80_read8 =z80_read; 276 | drZ80.z80_read16 =z80_read16; 277 | drZ80.z80_write8 =z80_write; 278 | drZ80.z80_write16 =z80_write16; 279 | drZ80.z80_in =DrZ80_in; 280 | drZ80.z80_out =DrZ80_out; 281 | drZ80.z80_irq_callback=DrZ80_irq_callback; 282 | #endif 283 | } 284 | 285 | void z80_reset() 286 | { 287 | #if defined(_USE_MZ80) 288 | mz80reset(); 289 | #elif defined(_USE_DRZ80) 290 | memset(&drZ80, 0, 0x4c); 291 | drZ80.Z80F = (1<<2); // set ZFlag 292 | drZ80.Z80F2 = (1<<2); // set ZFlag 293 | drZ80.Z80IX = 0xFFFF << 16; 294 | drZ80.Z80IY = 0xFFFF << 16; 295 | drZ80.Z80IM = 0; // 1? 296 | drZ80.Z80PC = drZ80.z80_rebasePC(0); 297 | drZ80.Z80SP = drZ80.z80_rebaseSP(0x2000); // 0xf000 ? 298 | #endif 299 | Pico.m.z80_fakeval = 0; // for faking when Z80 is disabled 300 | } 301 | 302 | void z80_resetCycles() 303 | { 304 | #if defined(_USE_MZ80) 305 | mz80GetElapsedTicks(1); 306 | #endif 307 | } 308 | 309 | void z80_int() 310 | { 311 | #if defined(_USE_MZ80) 312 | mz80int(0); 313 | #elif defined(_USE_DRZ80) 314 | drZ80.z80irqvector = 0xFF; // default IRQ vector RST opcode 315 | drZ80.Z80_IRQ = 1; 316 | #endif 317 | } 318 | 319 | // returns number of cycles actually executed 320 | int z80_run(int cycles) 321 | { 322 | #if defined(_USE_MZ80) 323 | int ticks_pre = mz80GetElapsedTicks(0); 324 | mz80exec(cycles); 325 | return mz80GetElapsedTicks(0) - ticks_pre; 326 | #elif defined(_USE_DRZ80) 327 | return cycles - DrZ80Run(&drZ80, cycles); 328 | #else 329 | return cycles; 330 | #endif 331 | } 332 | 333 | void z80_pack(unsigned char *data) 334 | { 335 | #if defined(_USE_MZ80) 336 | struct mz80context mz80; 337 | *(int *)data = 0x00005A6D; // "mZ" 338 | mz80GetContext(&mz80); 339 | memcpy(data+4, &mz80.z80clockticks, sizeof(mz80)-5*4); // don't save base&memhandlers 340 | #elif defined(_USE_DRZ80) 341 | *(int *)data = 0x005A7244; // "DrZ" 342 | drZ80.Z80PC = drZ80.z80_rebasePC(drZ80.Z80PC-drZ80.Z80PC_BASE); 343 | drZ80.Z80SP = drZ80.z80_rebaseSP(drZ80.Z80SP-drZ80.Z80SP_BASE); 344 | memcpy(data+4, &drZ80, 0x4c); 345 | #endif 346 | } 347 | 348 | void z80_unpack(unsigned char *data) 349 | { 350 | #if defined(_USE_MZ80) 351 | if(*(int *)data == 0x00005A6D) { // "mZ" save? 352 | struct mz80context mz80; 353 | mz80GetContext(&mz80); 354 | memcpy(&mz80.z80clockticks, data+4, sizeof(mz80)-5*4); 355 | mz80SetContext(&mz80); 356 | } else { 357 | z80_reset(); 358 | z80_int(); 359 | } 360 | #elif defined(_USE_DRZ80) 361 | if(*(int *)data == 0x005A7244) { // "DrZ" save? 362 | memcpy(&drZ80, data+4, 0x4c); 363 | // update bases 364 | drZ80.Z80PC = drZ80.z80_rebasePC(drZ80.Z80PC-drZ80.Z80PC_BASE); 365 | drZ80.Z80SP = drZ80.z80_rebaseSP(drZ80.Z80SP-drZ80.Z80SP_BASE); 366 | } else { 367 | z80_reset(); 368 | drZ80.Z80IM = 1; 369 | z80_int(); // try to goto int handler, maybe we won't execute trash there? 370 | } 371 | #endif 372 | } 373 | 374 | void z80_exit() 375 | { 376 | #if defined(_USE_MZ80) 377 | mz80shutdown(); 378 | #endif 379 | } 380 | 381 | #endif // ARM9_SOUND 382 | -------------------------------------------------------------------------------- /arm9/source/pico/sound/sound.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | extern "C" { 3 | #endif 4 | 5 | int sound_timers_and_dac(int raster); 6 | int sound_render(); 7 | 8 | // z80 functionality wrappers 9 | void z80_init(); 10 | void z80_resetCycles(); 11 | void z80_int(); 12 | int z80_run(int cycles); 13 | void z80_exit(); 14 | 15 | #ifdef __cplusplus 16 | } // End of extern "C" 17 | #endif 18 | -------------------------------------------------------------------------------- /arm9/source/pico/sound/ym2612.h: -------------------------------------------------------------------------------- 1 | /* 2 | header file for software emulation for FM sound generator 3 | 4 | */ 5 | #ifndef _H_FM_FM_ 6 | #define _H_FM_FM_ 7 | 8 | #include "../../config.h" 9 | 10 | void YM2612Init(int baseclock, int rate); 11 | void YM2612ResetChip(); 12 | void YM2612UpdateOne(short *buffer, int length, int stereo); 13 | 14 | void YM2612Write(unsigned int a, unsigned int v); 15 | unsigned char YM2612Read(); 16 | 17 | void YM2612PicoTick(); 18 | void YM2612PicoStateLoad(); 19 | 20 | #endif /* _H_FM_FM_ */ 21 | -------------------------------------------------------------------------------- /arm9/source/pico/sound/ym2612_helper.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | void sum_outputs_mono(INT32 *out_fm, UINT32 pan); 4 | void sum_outputs_mono_mix(INT32 *out_fm, UINT32 pan); 5 | void sum_outputs_stereo(INT32 *out_fm, UINT32 pan); 6 | void sum_outputs_stereo_mix(INT32 *out_fm, UINT32 pan); 7 | -------------------------------------------------------------------------------- /arm9/source/pico/sound/ym2612_helper.s: -------------------------------------------------------------------------------- 1 | @ some attempts to improve speed of YM2612UpdateOne() function from ym2612.c 2 | @ by using multiple register loads 3 | 4 | @ (c) Copyright 2006, notaz 5 | @ All Rights Reserved 6 | 7 | 8 | @ sum_outputs* assume r11 points to output buffer 9 | 10 | .global sum_outputs_mono @ UINT32 *out_fm 11 | sum_outputs_mono: 12 | stmfd sp!, {lr} 13 | ldmia r0, {r0-r3,r12,lr} @ checked the ARM manual, this should be safe 14 | add r0, r0, r1 15 | b .sum_outputs_mono_common 16 | 17 | 18 | 19 | .global sum_outputs_mono_mix @ UINT32 *out_fm 20 | sum_outputs_mono_mix: 21 | stmfd sp!, {lr} 22 | ldmia r0, {r0-r3,r12,lr} 23 | add r0, r0, r1 24 | 25 | ldrsh r1, [r11] 26 | add r0, r0, r1 27 | 28 | .sum_outputs_mono_common: 29 | add r0, r0, r2 30 | add r0, r0, r3 31 | add r0, r0, r12 32 | add r0, r0, lr 33 | 34 | @ Limit 35 | mov r1, #0x8000 36 | sub r1, r1, #1 37 | cmp r0, r1 38 | movgt r0, r1 39 | bgt .done1 40 | 41 | cmn r0, #0x8000 42 | movlt r0, #0x8000 43 | 44 | .done1: 45 | strh r0, [r11], #2 46 | 47 | ldmfd sp!, {lr} 48 | bx lr 49 | 50 | 51 | 52 | .global sum_outputs_stereo @ UINT32 *out_fm, UINT32 pan 53 | sum_outputs_stereo: 54 | stmfd sp!, {r4-r6,lr} 55 | mov r5, #0 @ lt 56 | mov r6, #0 @ rt 57 | b .sum_outputs_stereo_common 58 | 59 | 60 | 61 | .global sum_outputs_stereo_mix @ UINT32 *out_fm, UINT32 pan 62 | sum_outputs_stereo_mix: 63 | stmfd sp!, {r4-r6,lr} 64 | ldrsh r5, [r11] @ lt 65 | mov r6, r5 @ rt 66 | 67 | .sum_outputs_stereo_common: 68 | ldmia r0, {r0,r2-r4,r12,lr} 69 | movs r1, r1, lsr #1 70 | addcs r6, r6, r0 71 | movs r1, r1, lsr #1 72 | addcs r5, r5, r0 73 | movs r1, r1, lsr #1 74 | addcs r6, r6, r2 75 | movs r1, r1, lsr #1 76 | addcs r5, r5, r2 77 | movs r1, r1, lsr #1 78 | addcs r6, r6, r3 79 | movs r1, r1, lsr #1 80 | addcs r5, r5, r3 81 | movs r1, r1, lsr #1 82 | addcs r6, r6, r4 83 | movs r1, r1, lsr #1 84 | addcs r5, r5, r4 85 | movs r1, r1, lsr #1 86 | addcs r6, r6, r12 87 | movs r1, r1, lsr #1 88 | addcs r5, r5, r12 89 | movs r1, r1, lsr #1 90 | addcs r6, r6, lr 91 | movs r1, r1, lsr #1 92 | addcs r5, r5, lr 93 | 94 | @ Limit 95 | mov r1, #0x8000 96 | sub r1, r1, #1 97 | cmp r5, r1 98 | movgt r5, r1 99 | cmp r6, r1 100 | movgt r6, r1 101 | 102 | cmn r5, #0x8000 103 | movlt r5, #0x8000 104 | cmn r6, #0x8000 105 | movlt r6, #0x8000 106 | 107 | mov r5, r5, lsl #16 108 | mov r5, r5, lsr #16 109 | orr r5, r5, r6, lsl #16 110 | stmia r11!, {r5} 111 | 112 | ldmfd sp!, {r4-r6,lr} 113 | bx lr 114 | 115 | 116 | -------------------------------------------------------------------------------- /arm9/source/singleton.h: -------------------------------------------------------------------------------- 1 | /* 2 | common/singleton.h 3 | Copyright (c) 2018 chyyran 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | */ 23 | 24 | #pragma once 25 | #ifndef _SINGLETON_H_ 26 | #define _SINGLETON_H_ 27 | #include 28 | #include 29 | #include // std::nothrow 30 | 31 | template 32 | class singleton 33 | { 34 | 35 | public: 36 | static inline T &instance(Args &&... args) 37 | { 38 | if (!_instance) 39 | make(std::forward(args)...); 40 | return *_instance; 41 | } 42 | 43 | private: 44 | static inline void make(Args... args) 45 | { 46 | if (!_instance) 47 | _instance = new (std::nothrow) T(std::forward(args)...); 48 | } 49 | 50 | static inline void reset() 51 | { 52 | if (_instance) 53 | { 54 | delete _instance; 55 | _instance = NULL; 56 | } 57 | } 58 | 59 | private: 60 | static T *_instance; 61 | }; 62 | 63 | template 64 | T * singleton::_instance = NULL; 65 | 66 | #endif //_SINGLETON_H_ 67 | -------------------------------------------------------------------------------- /arm9/source/streamingaudio.c: -------------------------------------------------------------------------------- 1 | #include "streamingaudio.h" 2 | #include "tonccpy.h" 3 | 4 | // Private members 5 | 6 | /* Not a actual pointer, but the index to the current location of the 7 | * buffers that are being streamed. 8 | */ 9 | volatile s32 streaming_buf_ptr = 0; 10 | 11 | /** 12 | * Number of samples filled so far 13 | */ 14 | volatile s32 filled_samples = 0; 15 | 16 | // Pointers to the stream buffers. 17 | volatile s16* play_stream_buf = (s16*)0x02E00000; 18 | volatile s16* fill_stream_buf = (s16*)0x02F00000; 19 | 20 | 21 | // Toggle this to true to trigger a fill as soon as possible. 22 | volatile bool fill_requested = false; 23 | 24 | volatile u16 fade_counter = FADE_STEPS; 25 | volatile bool fade_out = false; 26 | 27 | volatile u32 sample_delay_count = 0; 28 | 29 | #ifdef SOUND_DEBUG 30 | char debug_buf[256] = {0}; 31 | #endif 32 | 33 | void resetStreamSettings() { 34 | streaming_buf_ptr = 0; 35 | filled_samples = 0; 36 | 37 | play_stream_buf = (s16*)0x02E00000; 38 | fill_stream_buf = (s16*)0x02F00000; 39 | 40 | 41 | fill_requested = false; 42 | 43 | fade_counter = FADE_STEPS; 44 | fade_out = false; 45 | 46 | sample_delay_count = 0; 47 | } 48 | 49 | /* 50 | * The maxmod stream request handler. 51 | * 52 | * This method is called automatically by maxmod at random times. 53 | * 54 | * While streaming from RAM is a trivial matter, streaming from 55 | * a file is a bit more involved because SD access is much slower. 56 | * 57 | * We can't fread in the stream request handler, or it will take 58 | * too long and the DS will crash. We can load it after as soon as 59 | * possible, but there is no guarantee that it will be finished 60 | * before we need to stream again. Waiting until we've run out 61 | * of things to stream and then loading new samples is too slow. 62 | * We need it to be "just-in-time". 63 | * 64 | * The buffers that play_stream_buf and fill_stream_buf must be 65 | * completely filled with audio data before the first call to 66 | * this method. This is set up by sound.cpp. 67 | * 68 | * Samples are then streamed from play_stream_buf. Once a 69 | * sample has been compied to dest (and thus streamed), it 70 | * is replaced by a sample from the same location at 71 | * fill_stream_buf. Hence there is always new data at 72 | * play_stream_buf. 73 | * 74 | * Every SAMPLES_PER_FILL, a fill request is made. This is 75 | * handled by sound.cpp, and copies enough data to replace 76 | * the number of samples that were streamed since the last 77 | * fill request into the fill_stream_buf. This ensures that 78 | * there is always new data in the fill_stream_buf, and that 79 | * data is only overwritten once it has been copied into 80 | * play_stream_buf. 81 | * 82 | * streaming_buf_ptr keeps track of where in the buffers 83 | * data will be read from. This is reset after STREAMING_BUF_LENGTH 84 | * samples have been streamed. 85 | * 86 | * In combination, this creates two looping buffers that ensure 87 | * that new data is ready just in time. 88 | */ 89 | mm_word on_stream_request(mm_word length, mm_addr dest, mm_stream_formats format) { 90 | 91 | // Debug stuff 92 | // if (fill_requested) { 93 | // nocashMessage("missed fill"); 94 | // } 95 | 96 | int len = length; 97 | s16 *target = dest; 98 | 99 | // fill delay with silence 100 | for (; sample_delay_count && len; len--, sample_delay_count--) { 101 | *target++ = 0; 102 | } 103 | 104 | for (; len; len--) 105 | { 106 | // Loop the streaming_buf_ptr 107 | if (streaming_buf_ptr >= STREAMING_BUF_LENGTH) { 108 | streaming_buf_ptr = 0; 109 | } 110 | 111 | // Stream the next sample 112 | *target++ = (*(play_stream_buf + streaming_buf_ptr) >> (FADE_STEPS - fade_counter)); 113 | 114 | // Copy the next sample that will be played the next time 115 | // the streaming_buf_ptr is at this location from 116 | // fill_stream_buf 117 | *(play_stream_buf + streaming_buf_ptr) = *(fill_stream_buf + streaming_buf_ptr); 118 | 119 | // Zero out fill_stream_buf at this location, preventing 120 | // glitched sound if fills don't keep up. 121 | *(fill_stream_buf + streaming_buf_ptr) = 0; 122 | 123 | // Increment the streaming buf pointer. 124 | streaming_buf_ptr++; 125 | 126 | } 127 | 128 | if (!sample_delay_count && fade_out && (fade_counter > 0)) { 129 | // sprintf(debug_buf, "Fade i: %i", fade_counter); 130 | // nocashMessage(debug_buf); 131 | fade_counter--; 132 | } 133 | 134 | 135 | #ifdef SOUND_DEBUG 136 | sprintf(debug_buf, "Stream filled, pointer at %li, samples filed %li", streaming_buf_ptr, filled_samples); 137 | nocashMessage(debug_buf); 138 | #endif 139 | // Request a new fill from sound.cpp, refreshing the fill buffer. 140 | // Ensure that fills are requested only if the streaming buf ptr is more than 141 | // the filled samples. 142 | if (!fill_requested && abs(streaming_buf_ptr - filled_samples) >= SAMPLES_PER_FILL) { 143 | #ifdef SOUND_DEBUG 144 | nocashMessage("Fill requested!"); 145 | #endif 146 | fill_requested = true; 147 | } 148 | return length; 149 | } -------------------------------------------------------------------------------- /arm9/source/streamingaudio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef __PICOTWL_SOUND_STREAM__ 3 | #define __PICOTWL_SOUND_STREAM__ 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // #define SOUND_DEBUG 10 | 11 | #define FADE_STEPS 7 // Number of fill requests to fade out across when fade out is requested. 12 | #define STREAMING_BUF_LENGTH 96000 // Size in samples (16 bits) => 96000 samples = 192KB * 2 buffers = 384KB RAM total. 13 | #define FILL_FACTOR 4 // The higher the fill factor the more frequent the fills will be requested. 14 | #define SAMPLES_PER_FILL (STREAMING_BUF_LENGTH >> FILL_FACTOR) // Samples to load into the fill buffer per fill. 15 | #define TOTAL_FILLS (1 << FILL_FACTOR) // Fills before we stop accepting new data into the fill buffer 16 | 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | void resetStreamSettings(); 23 | mm_word on_stream_request(mm_word length, mm_addr dest, mm_stream_formats format); 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif 28 | #endif -------------------------------------------------------------------------------- /arm9/source/stringtool.cpp: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------- 2 | 3 | 4 | Copyright (C) 2007 Acekard, www.acekard.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | 24 | 25 | ---------------------------------------------------------------------------------*/ 26 | 27 | #include "stringtool.h" 28 | #include 29 | #include 30 | #include 31 | 32 | std::string formatString( const char* fmt, ... ) 33 | { 34 | const char * f = fmt; 35 | va_list argList; 36 | va_start(argList, fmt); 37 | char * ptempStr = NULL; 38 | size_t max_len = vasiprintf( &ptempStr, f, argList); 39 | std::string str( ptempStr ); 40 | str.resize( max_len ); 41 | free( ptempStr ); 42 | va_end(argList); 43 | return str; 44 | } 45 | 46 | std::string replaceAll(std::string str, const std::string &from, const std::string &to) 47 | { 48 | size_t start_pos = 0; 49 | while ((start_pos = str.find(from, start_pos)) != std::string::npos) 50 | { 51 | str.replace(start_pos, from.length(), to); 52 | start_pos += to.length(); // Handles case where 'to' is a substring of 'from' 53 | } 54 | return str; 55 | } -------------------------------------------------------------------------------- /arm9/source/stringtool.h: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------- 2 | 3 | 4 | Copyright (C) 2007 Acekard, www.acekard.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | 24 | 25 | ---------------------------------------------------------------------------------*/ 26 | 27 | #ifndef _STRINGTOOL_H_ 28 | #define _STRINGTOOL_H_ 29 | 30 | #include 31 | 32 | std::string formatString(const char *fmt, ...); 33 | 34 | std::string replaceAll(std::string str, const std::string &from, const std::string &to); 35 | 36 | #endif //_STRINGTOOL_H_ 37 | -------------------------------------------------------------------------------- /arm9/source/tonccpy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tonccpy.h" 3 | //# tonccpy.c 4 | 5 | //! VRAM-safe cpy. 6 | /*! This version mimics memcpy in functionality, with 7 | the benefit of working for VRAM as well. It is also 8 | slightly faster than the original memcpy, but faster 9 | implementations can be made. 10 | \param dst Destination pointer. 11 | \param src Source pointer. 12 | \param size Fill-length in bytes. 13 | \note The pointers and size need not be word-aligned. 14 | */ 15 | void tonccpy(void *dst, const void *src, uint size) 16 | { 17 | if(size==0 || dst==NULL || src==NULL) 18 | return; 19 | 20 | uint count; 21 | u16 *dst16; // hword destination 22 | u8 *src8; // byte source 23 | 24 | // Ideal case: copy by 4x words. Leaves tail for later. 25 | if( ((u32)src|(u32)dst)%4==0 && size>=4) 26 | { 27 | u32 *src32= (u32*)src, *dst32= (u32*)dst; 28 | 29 | count= size/4; 30 | uint tmp= count&3; 31 | count /= 4; 32 | 33 | // Duff's Device, good friend! 34 | switch(tmp) { 35 | do { *dst32++ = *src32++; 36 | case 3: *dst32++ = *src32++; 37 | case 2: *dst32++ = *src32++; 38 | case 1: *dst32++ = *src32++; 39 | case 0: ; } while(count--); 40 | } 41 | 42 | // Check for tail 43 | size &= 3; 44 | if(size == 0) 45 | return; 46 | 47 | src8= (u8*)src32; 48 | dst16= (u16*)dst32; 49 | } 50 | else // Unaligned. 51 | { 52 | uint dstOfs= (u32)dst&1; 53 | src8= (u8*)src; 54 | dst16= (u16*)(dst-dstOfs); 55 | 56 | // Head: 1 byte. 57 | if(dstOfs != 0) 58 | { 59 | *dst16= (*dst16 & 0xFF) | *src8++<<8; 60 | dst16++; 61 | if(--size==0) 62 | return; 63 | } 64 | } 65 | 66 | // Unaligned main: copy by 2x byte. 67 | count= size/2; 68 | while(count--) 69 | { 70 | *dst16++ = src8[0] | src8[1]<<8; 71 | src8 += 2; 72 | } 73 | 74 | // Tail: 1 byte. 75 | if(size&1) 76 | *dst16= (*dst16 &~ 0xFF) | *src8; 77 | } 78 | //# toncset.c 79 | 80 | //! VRAM-safe memset, internal routine. 81 | /*! This version mimics memset in functionality, with 82 | the benefit of working for VRAM as well. It is also 83 | slightly faster than the original memset. 84 | \param dst Destination pointer. 85 | \param fill Word to fill with. 86 | \param size Fill-length in bytes. 87 | \note The \a dst pointer and \a size need not be 88 | word-aligned. In the case of unaligned fills, \a fill 89 | will be masked off to match the situation. 90 | */ 91 | void __toncset(void *dst, u32 fill, uint size) 92 | { 93 | if(size==0 || dst==NULL) 94 | return; 95 | 96 | uint left= (u32)dst&3; 97 | u32 *dst32= (u32*)(dst-left); 98 | u32 count, mask; 99 | 100 | // Unaligned head. 101 | if(left != 0) 102 | { 103 | // Adjust for very small stint. 104 | if(left+size<4) 105 | { 106 | mask= BIT_MASK(size*8)<<(left*8); 107 | *dst32= (*dst32 &~ mask) | (fill & mask); 108 | return; 109 | } 110 | 111 | mask= BIT_MASK(left*8); 112 | *dst32= (*dst32 & mask) | (fill&~mask); 113 | dst32++; 114 | size -= 4-left; 115 | } 116 | 117 | // Main stint. 118 | count= size/4; 119 | uint tmp= count&3; 120 | count /= 4; 121 | 122 | switch(tmp) { 123 | do { *dst32++ = fill; 124 | case 3: *dst32++ = fill; 125 | case 2: *dst32++ = fill; 126 | case 1: *dst32++ = fill; 127 | case 0: ; } while(count--); 128 | } 129 | 130 | // Tail 131 | size &= 3; 132 | if(size) 133 | { 134 | mask= BIT_MASK(size*8); 135 | *dst32= (*dst32 &~ mask) | (fill & mask); 136 | } 137 | } -------------------------------------------------------------------------------- /arm9/source/tonccpy.h: -------------------------------------------------------------------------------- 1 | //# Stuff you may not have yet. 2 | 3 | #ifndef TONCCPY_H 4 | #define TONCCPY_H 5 | 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #include 12 | 13 | typedef unsigned int uint; 14 | #define BIT_MASK(len) ( (1<<(len))-1 ) 15 | static inline u32 quad8(u8 x) { x |= x<<8; return x | x<<16; } 16 | 17 | 18 | //# Declarations and inlines. 19 | 20 | void tonccpy(void *dst, const void *src, uint size); 21 | 22 | void __toncset(void *dst, u32 fill, uint size); 23 | static inline void toncset(void *dst, u8 src, uint size); 24 | static inline void toncset16(void *dst, u16 src, uint size); 25 | static inline void toncset32(void *dst, u32 src, uint size); 26 | 27 | 28 | //! VRAM-safe memset, byte version. Size in bytes. 29 | static inline void toncset(void *dst, u8 src, uint size) 30 | { __toncset(dst, quad8(src), size); } 31 | 32 | //! VRAM-safe memset, halfword version. Size in hwords. 33 | static inline void toncset16(void *dst, u16 src, uint size) 34 | { __toncset(dst, src|src<<16, size*2); } 35 | 36 | //! VRAM-safe memset, word version. Size in words. 37 | static inline void toncset32(void *dst, u32 src, uint size) 38 | { __toncset(dst, src, size*4); } 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | #endif 44 | -------------------------------------------------------------------------------- /fix_ndsheader.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf8 -*- 2 | # Patch an .nds (works with homebrew and ds demo only) to make it ready for make_cia 3 | # 4 | # 2016-02-28, Ahezard 5 | # 6 | # inspired by 7 | # Apache Thunder .nds edited files and comments 8 | # https://github.com/Relys/Project_CTR/blob/master/makerom/srl.h 9 | # https://dsibrew.org/wiki/DSi_Cartridge_Header 10 | # if the header size of the input nds file is 0x200 (homebrew) 11 | # the header size of the output nds file will be patched to 0x4000 (normal ds/dsi header), 0x3E00 offset 12 | 13 | from struct import * 14 | from collections import namedtuple 15 | from collections import OrderedDict 16 | from pprint import pprint 17 | import os, sys 18 | import binascii 19 | import argparse 20 | 21 | 22 | parser = argparse.ArgumentParser(description='Patch an nds in order to be ready cia conversion via make_cia --srl=.') 23 | parser.add_argument('file', metavar='file.nds', type=file, help='nds file to patch') 24 | parser.add_argument('--verbose', help='verbose mode', action="store_true") 25 | parser.add_argument('--out', help='output file [optionnal]') 26 | parser.add_argument('--read', help='print only the header content, do not patch', action="store_true") 27 | parser.add_argument('--extract', help='extract the content of the rom : header.bin,arm9.bin,arm7.bin,icon.bin,arm9i.bin,arm7i.bin, do not patch', action="store_true") #Not yet implemented 28 | parser.add_argument('--title', help='Game title') 29 | parser.add_argument('--code', help='Game code') 30 | parser.add_argument('--maker', help='Maker code') 31 | parser.add_argument('--mode', help='target mode, default mode is ds [ds|dsi|dsinogba|nitrohax]') 32 | parser.add_argument('--arm9', type=file, help='swap the ds arm9 binary by the one provided') 33 | parser.add_argument('--arm7', type=file, help='swap the ds arm7 binary by the one provided') 34 | parser.add_argument('--arm9EntryAddress', help='arm9 ram address of the binary provided') 35 | parser.add_argument('--arm7EntryAddress', help='arm7 ram address of the binary provided') 36 | parser.add_argument('--arm9i', type=file, help='add a dsi arm9i binary to the file, not needed for homebrew so far') 37 | parser.add_argument('--arm7i', type=file, help='add a dsi arm7i binary to the file, not needed for homebrew so far') 38 | parser.add_argument('--accessControl', help='access control field') 39 | args = parser.parse_args() 40 | 41 | if args.mode is None: 42 | args.mode = "dsi" 43 | 44 | # 45 | # CRC16 MODULE 46 | # 47 | # includes CRC16 and CRC16 MODBUS 48 | # 49 | 50 | from ctypes import c_ushort 51 | 52 | # from https://github.com/cristianav/PyCRC/blob/master/demo.py 53 | class CRC16(object): 54 | crc16_tab = [] 55 | 56 | # The CRC's are computed using polynomials. Here is the most used 57 | # coefficient for CRC16 58 | crc16_constant = 0xA001 # 40961 59 | 60 | def __init__(self, modbus_flag=False): 61 | # initialize the precalculated tables 62 | if not len(self.crc16_tab): 63 | self.init_crc16() 64 | self.mdflag = bool(modbus_flag) 65 | 66 | def calculate(self, input_data=None): 67 | try: 68 | is_string = isinstance(input_data, str) 69 | is_bytes = isinstance(input_data, (bytes, bytearray)) 70 | 71 | if not is_string and not is_bytes: 72 | raise Exception("Please provide a string or a byte sequence " 73 | "as argument for calculation.") 74 | 75 | crc_value = 0x0000 if not self.mdflag else 0xffff 76 | 77 | for c in input_data: 78 | d = ord(c) if is_string else c 79 | tmp = crc_value ^ d 80 | rotated = crc_value >> 8 81 | crc_value = rotated ^ self.crc16_tab[(tmp & 0x00ff)] 82 | 83 | return crc_value 84 | except Exception as e: 85 | print("EXCEPTION(calculate): {}".format(e)) 86 | 87 | def init_crc16(self): 88 | """The algorithm uses tables with precalculated values""" 89 | for i in range(0, 256): 90 | crc = c_ushort(i).value 91 | for j in range(0, 8): 92 | if crc & 0x0001: 93 | crc = c_ushort(crc >> 1).value ^ self.crc16_constant 94 | else: 95 | crc = c_ushort(crc >> 1).value 96 | self.crc16_tab.append(crc) 97 | 98 | def getSize(fileobject): 99 | current = fileobject.tell() 100 | fileobject.seek(0,2) # move the cursor to the end of the file 101 | size = fileobject.tell() 102 | fileobject.seek(current,0) 103 | return size 104 | 105 | def skipUntilAddress(f_in,f_out, caddr, taddr): 106 | chunk = f_in.read(taddr-caddr) 107 | f_out.write(chunk) 108 | 109 | def writeBlankuntilAddress(f_out, caddr, taddr): 110 | f_out.write("\x00"*(taddr-caddr)) 111 | 112 | fname=args.file.name 113 | args.file.close() 114 | 115 | if not args.read: 116 | print "Patching file : "+fname 117 | else: 118 | print "Reading header of file : "+fname 119 | 120 | #offset of 0x4600 created 121 | 122 | # File size compute 123 | file = open(fname, 'rb') 124 | fsize=getSize(file) 125 | file.close() 126 | 127 | #CRC header compute "CRC-16 (Modbus)" 128 | file = open(fname, 'rb') 129 | #0x15E from https://github.com/devkitPro/ndstool/ ... source/header.cpp 130 | hdr = file.read(0x15E) 131 | hdrCrc=CRC16(modbus_flag=True).calculate(hdr) 132 | if args.verbose: 133 | print("{:10s} {:20X}".format('HDR CRC-16 ModBus', hdrCrc)) 134 | #print "origin header cr c"+hdr[0x15E:0x15F] 135 | #filew = open(fname+".hdr", "wb") 136 | #filew.write(hdr); 137 | #filew.close() 138 | file.close() 139 | 140 | if args.arm9 is not None: 141 | arm9Fname=args.arm9.name 142 | args.arm9.close() 143 | arm9File = open(arm9Fname, 'rb') 144 | arm9FileSize=getSize(arm9File) 145 | dataArm9=arm9File.read(arm9FileSize) 146 | arm9File.close() 147 | 148 | if args.arm7 is not None: 149 | arm7Fname=args.arm7.name 150 | args.arm7.close() 151 | arm7File = open(arm7Fname, 'rb') 152 | arm7FileSize=getSize(arm7File) 153 | dataArm7=arm7File.read(arm7FileSize) 154 | arm7File.close() 155 | 156 | filer = open(fname, 'rb') 157 | data = filer.read(0x180) 158 | caddr=0x180 159 | 160 | #DS Data 180 bytes 161 | SrlHeader = namedtuple('SrlHeader', 162 | "gameTitle " 163 | "gameCode " 164 | "makerCode " 165 | "unitCode " 166 | "encryptionSeedSelect " 167 | "deviceCapacity " 168 | "reserved0 " 169 | "dsiflags " 170 | "romVersion " 171 | "internalFlag " 172 | "arm9RomOffset " 173 | "arm9EntryAddress " 174 | "arm9RamAddress " 175 | "arm9Size " 176 | "arm7RomOffset " 177 | "arm7EntryAddress " 178 | "arm7RamAddress " 179 | "arm7Size " 180 | "fntOffset " 181 | "fntSize " 182 | "fatOffset " 183 | "fatSize " 184 | "arm9OverlayOffset " 185 | "arm9OverlaySize " 186 | "arm7OverlayOffset " 187 | "arm7OverlaySize " 188 | "normalCardControlRegSettings " 189 | "secureCardControlRegSettings " 190 | "icon_bannerOffset " 191 | "secureAreaCrc " 192 | "secure_transfer_timeout " 193 | "arm9Autoload " 194 | "arm7Autoload " 195 | "secureDisable " 196 | "ntrRomSize " 197 | "headerSize " 198 | "reserved1 " 199 | "nintendoLogo " 200 | "nintendoLogoCrc " 201 | "headerCrc " 202 | "debugReserved ") 203 | srlHeaderFormat='<12s4s2scbb7s2sbcIIIIIIIIIIIIIIIIIIIHHII8sII56s156s2sH32s' 204 | srlHeader=SrlHeader._make(unpack_from(srlHeaderFormat, data)) 205 | if args.verbose: 206 | print "origin header crc "+hex(srlHeader.headerCrc) 207 | print "origin secure crc "+hex(srlHeader.secureAreaCrc) 208 | 209 | #SecureArea CRC compute "CRC-16 (Modbus)" 210 | file = open(fname, 'rb') 211 | #0x15E from https://github.com/devkitPro/ndstool/ ... source/header.cpp 212 | file.read(0x200) 213 | sec = file.read(0x4000) 214 | secCrc=CRC16(modbus_flag=True).calculate(sec) 215 | if args.verbose: 216 | print("{:10s} {:20X}".format('SEC CRC-16 ModBus', secCrc)) 217 | file.close() 218 | 219 | if srlHeader.arm7EntryAddress>0x2400000 and not args.read and args.arm7 is None: 220 | print "WARNING: .nds arm7EntryAddress greater than 0x2400000 will not boot as cia" 221 | print "you need to recompile or swap the arm7 binary with a precompiled one with --arm7 and --arm7EntryAddress" 222 | 223 | if "dsi" in args.mode : 224 | srlHeaderPatched=srlHeader._replace( 225 | dsiflags= '\x01\x00', #disable modcrypt but enable twl 226 | unitCode= '\x02', 227 | ) 228 | 229 | data1=pack(*[srlHeaderFormat]+srlHeaderPatched._asdict().values()) 230 | newHdrCrc=CRC16(modbus_flag=True).calculate(data1[0:0x15E]) 231 | srlHeaderPatched=srlHeaderPatched._replace(headerCrc=newHdrCrc) 232 | 233 | if args.verbose: 234 | print "new header crc "+hex(newHdrCrc) 235 | if not args.read : 236 | if args.verbose: 237 | pprint(dict(srlHeaderPatched._asdict())) 238 | else: 239 | pprint(dict(srlHeader._asdict())) 240 | 241 | data1=pack(*[srlHeaderFormat]+srlHeaderPatched._asdict().values()) 242 | 243 | arm9isize=0 244 | arm7isize=0 245 | 246 | #TWL Only Data 384 bytes 247 | SrlTwlExtHeader = namedtuple('SrlTwlExtHeader', 248 | "MBK_1_5_Settings " 249 | "MBK_6_8_Settings_ARM9 " 250 | "MBK_6_8_Settings_ARM7 " 251 | "global_MBK_9_Setting " 252 | "regionFlags " 253 | "accessControl " 254 | "arm7ScfgExtMask " 255 | "reserved_flags " 256 | "arm9iRomOffset " 257 | "reserved2 " 258 | "arm9iLoadAddress " 259 | "arm9iSize " 260 | "arm7iRomOffset " 261 | "struct_param_baseAddress " 262 | "arm7iLoadAddress " 263 | "arm7iSize " 264 | "digest_ntrRegionOffset " 265 | "digest_ntrRegionSize " 266 | "digest_twlRegionOffset " 267 | "digest_twlRegionSize " 268 | "digestSectorHashtableOffset " 269 | "digestSectorHashtableSize " 270 | "digest_blockHashtableOffset " 271 | "digest_blockHashtableSize " 272 | "digestSectorSize " 273 | "digest_blockSectorcount " 274 | "iconSize " #usually 0x23C0 or 2112 in homebrew 275 | "unknown1 " 276 | "twlRomSize " 277 | "unknown2 " 278 | "modcryptArea1Offset " 279 | "modcryptArea1Size " 280 | "modcryptArea2Offset " 281 | "modcryptArea2Size " 282 | "title_id " 283 | "pubSaveDataSize " 284 | "privSaveDataSize " 285 | "reserved4 " 286 | "parentalControl ") 287 | srlTwlExtHeaderFormat="<20s12s12s4s4sIIII4sIIIIIIIIIIIIIIIII4sI12sIIII8sII176s16s" 288 | if srlHeader.headerSize<0x300: 289 | #homebrew 290 | srlTwlExtHeader=SrlTwlExtHeader._make(unpack_from(srlTwlExtHeaderFormat, "\x00" * (0x300-0x180))) 291 | else: 292 | data = filer.read(0x300-0x180) 293 | srlTwlExtHeader=SrlTwlExtHeader._make(unpack_from(srlTwlExtHeaderFormat, data)) 294 | caddr=0x300 295 | 296 | #pprint(dict(srlTwlExtHeader._asdict())) 297 | 298 | if not args.read: 299 | # Fix srlTwlExtHeader 300 | if "dsi" in args.mode: 301 | arm7iRomOffset=srlHeaderPatched.arm7RomOffset 302 | arm9iRomOffset=srlHeaderPatched.arm9RomOffset 303 | arm7isize=srlHeaderPatched.arm7Size 304 | arm9isize=srlHeaderPatched.arm9Size 305 | totaldsisize=0 306 | arm7iname = None 307 | arm9iname = None 308 | 309 | if args.arm9i is not None: 310 | arm9iname = args.arm9i.name 311 | arm9isize = getSize(args.arm9i) 312 | arm9iRomOffset=srlHeaderPatched.ntrRomSize 313 | if args.verbose: 314 | print "arm9isize : "+hex(arm9isize) 315 | print "arm9ioffset : "+hex(srlHeaderPatched.ntrRomSize) 316 | args.arm9i.close() 317 | totaldsisize=arm9isize 318 | 319 | if args.arm7i is not None: 320 | arm7iname = args.arm7i.name 321 | arm7isize = getSize(args.arm7i) 322 | arm7iRomOffset=srlHeaderPatched.ntrRomSize+arm9isize 323 | if args.verbose: 324 | print "arm7isize : "+hex(arm7isize) 325 | print "arm9ioffset : "+hex(srlHeaderPatched.ntrRomSize+arm9isize) 326 | args.arm7i.close() 327 | totaldsisize=arm9isize+arm7isize 328 | 329 | 330 | 331 | srlTwlExtHeader=srlTwlExtHeader._replace( 332 | accessControl= 0x00000138, 333 | arm7ScfgExtMask= 0x80040000, 334 | reserved_flags= 0x01000000 335 | ) 336 | 337 | if args.accessControl is not None: 338 | srlTwlExtHeader=srlTwlExtHeader._replace( 339 | accessControl= int(args.accessControl,0), 340 | ) 341 | 342 | if args.verbose or args.read: 343 | pprint(dict(srlTwlExtHeader._asdict())) 344 | 345 | data2=pack(*[srlTwlExtHeaderFormat]+srlTwlExtHeader._asdict().values()) 346 | 347 | #TWL and Signed NTR 3328 bytes 348 | SrlSignedHeader = namedtuple('SrlSignedHeader', 349 | "arm9WithSecAreaSha1Hmac " 350 | "arm7Sha1Hmac " 351 | "digestMasterSha1Hmac " 352 | "bannerSha1Hmac " 353 | "arm9iSha1Hmac " 354 | "arm7iSha1Hmac " 355 | "reserved5 " 356 | "arm9Sha1Hmac " 357 | "reserved6 " 358 | "reserved7 " 359 | "signature " 360 | ) 361 | srlSignedHeaderFormat="<20s20s20s20s20s20s40s20s2636s384s128s" 362 | if srlHeader.headerSize<0x1100: 363 | #homebrew 364 | srlSignedHeader=SrlSignedHeader._make(unpack_from(srlSignedHeaderFormat, "\x00" * (3328))) 365 | else: 366 | data = filer.read(3328) 367 | srlSignedHeader=SrlSignedHeader._make(unpack_from(srlSignedHeaderFormat, data)) 368 | caddr=0x300+3328 369 | filer.read(0x4000-caddr) 370 | caddr=0x4000 371 | 372 | #pprint(dict(srlSignedHeader._asdict())) 373 | 374 | # Fix srlSignedHeader 375 | if not args.read: 376 | srlSignedHeader=srlSignedHeader._replace( 377 | arm7Sha1Hmac= '\xff'*20, 378 | arm9WithSecAreaSha1Hmac= '\xff'*20, 379 | bannerSha1Hmac= '\xff'*20, 380 | signature= '\xff'*128 381 | ) 382 | if "dsi" in args.mode : 383 | srlSignedHeader=srlSignedHeader._replace( 384 | arm7Sha1Hmac= '\xff'*20, 385 | arm7iSha1Hmac= '\xff'*20, 386 | arm9Sha1Hmac= '\xff'*20, 387 | arm9WithSecAreaSha1Hmac= '\xff'*20, 388 | arm9iSha1Hmac= '\xff'*20, 389 | bannerSha1Hmac= '\xff'*20, 390 | digestMasterSha1Hmac= '\xff'*20, 391 | signature= '\xff'*128 392 | ) 393 | if args.verbose or args.read: 394 | pprint(dict(srlSignedHeader._asdict())) 395 | 396 | data3=pack(*[srlSignedHeaderFormat]+srlSignedHeader._asdict().values()) 397 | 398 | # ARM9 footer 399 | # from https://github.com/devkitPro/ndstool/ ... source/header.cpp 400 | # ARM9 footer size = 3*4 401 | ARM9Footer = namedtuple('ARM9Footer', 402 | "nitrocode " #0xDEC00621 403 | "versionInfo " 404 | "reserved " 405 | ) 406 | ARM9FooterFormat="