├── .gitignore ├── Makefile ├── README.md ├── TODO.md ├── banner ├── app.rsf └── banner.bnr ├── icon.png └── source ├── audio.c ├── audio.h ├── blargGL.c ├── blargGL.h ├── config.c ├── config.h ├── cpu.h ├── cpu.inc ├── cpu.s ├── defaultborder.h ├── dma.c ├── dsp.c ├── dsp.h ├── dspMixer.s ├── final.g.pica ├── final.v.pica ├── font.h ├── helper.h ├── helper.s ├── main.c ├── main.h ├── mem.c ├── mem.h ├── mem_io.s ├── mixrate.h ├── plain_quad.g.pica ├── plain_quad.v.pica ├── ppu.c ├── ppu.h ├── ppu_hard.c ├── ppu_soft.c ├── render_hard.g.pica ├── render_hard.v.pica ├── render_hard7.g.pica ├── render_hard7.v.pica ├── render_hard_obj.g.pica ├── render_hard_obj.v.pica ├── render_soft.g.pica ├── render_soft.v.pica ├── rom.c ├── screenfill.h ├── snes.c ├── snes.h ├── spc700.h ├── spc700.inc ├── spc700.s ├── spc700io.c ├── superfasthash.c ├── ui.c ├── ui.h ├── ui_config.c ├── ui_console.c ├── ui_rommenu.c ├── window_mask.g.pica └── window_mask.v.pica /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | blargSnes.elf 3 | blargSnes.3dsx 4 | blargSnes.smdh 5 | blargSnes.cia 6 | 7 | # old? 8 | blargSnes.bin 9 | blargSnes_s.elf -------------------------------------------------------------------------------- /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 | TOPDIR ?= $(CURDIR) 10 | include $(DEVKITARM)/3ds_rules 11 | 12 | export BLARG_VERSION := 1.4 13 | 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 | # DATA is a list of directories containing data files 19 | # INCLUDES is a list of directories containing header files 20 | # GRAPHICS is a list of directories containing graphics files 21 | # GFXBUILD is the directory where converted graphics files will be placed 22 | # If set to $(BUILD), it will statically link in the converted 23 | # files as if they were data files. 24 | # 25 | # NO_SMDH: if set to anything, no SMDH file is generated. 26 | # ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional) 27 | # APP_TITLE is the name of the app stored in the SMDH file (Optional) 28 | # APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) 29 | # APP_AUTHOR is the author of the app stored in the SMDH file (Optional) 30 | # ICON is the filename of the icon (.png), relative to the project folder. 31 | # If not set, it attempts to use one of the following (in this order): 32 | # - .png 33 | # - icon.png 34 | # - /default_icon.png 35 | #--------------------------------------------------------------------------------- 36 | TARGET := $(notdir $(CURDIR)) 37 | BUILD := build 38 | SOURCES := source 39 | DATA := data 40 | INCLUDES := include 41 | GRAPHICS := gfx 42 | GFXBUILD := $(BUILD) 43 | #ROMFS := romfs 44 | #GFXBUILD := $(ROMFS)/gfx 45 | 46 | #--------------------------------------------------------------------------------- 47 | # options for code generation 48 | #--------------------------------------------------------------------------------- 49 | ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft 50 | 51 | CFLAGS := -g -Wall -O2 -mword-relocations \ 52 | -fomit-frame-pointer -ffunction-sections \ 53 | $(ARCH) 54 | 55 | CFLAGS += $(INCLUDE) -D__3DS__ -DBLARGSNES_VERSION="\"$(BLARG_VERSION)\"" 56 | 57 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 58 | 59 | ASFLAGS := -g $(ARCH) 60 | LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 61 | 62 | LIBS := -lctru -lm 63 | 64 | #--------------------------------------------------------------------------------- 65 | # list of directories containing libraries, this must be the top level containing 66 | # include and lib 67 | #--------------------------------------------------------------------------------- 68 | LIBDIRS := $(CTRULIB) 69 | 70 | 71 | #--------------------------------------------------------------------------------- 72 | # no real need to edit anything past this point unless you need to add additional 73 | # rules for different file extensions 74 | #--------------------------------------------------------------------------------- 75 | ifneq ($(BUILD),$(notdir $(CURDIR))) 76 | #--------------------------------------------------------------------------------- 77 | 78 | ifneq ($(NO_SMDH),) 79 | NO_CIA = 1 80 | endif 81 | 82 | export OUTPUT := $(CURDIR)/$(TARGET) 83 | export TOPDIR := $(CURDIR) 84 | 85 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 86 | $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) \ 87 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 88 | 89 | export DEPSDIR := $(CURDIR)/$(BUILD) 90 | 91 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 92 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 93 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 94 | PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) 95 | SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist))) 96 | GFXFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.t3s))) 97 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 98 | 99 | #--------------------------------------------------------------------------------- 100 | # use CXX for linking C++ projects, CC for standard C 101 | #--------------------------------------------------------------------------------- 102 | ifeq ($(strip $(CPPFILES)),) 103 | #--------------------------------------------------------------------------------- 104 | export LD := $(CC) 105 | #--------------------------------------------------------------------------------- 106 | else 107 | #--------------------------------------------------------------------------------- 108 | export LD := $(CXX) 109 | #--------------------------------------------------------------------------------- 110 | endif 111 | #--------------------------------------------------------------------------------- 112 | 113 | #--------------------------------------------------------------------------------- 114 | ifeq ($(GFXBUILD),$(BUILD)) 115 | #--------------------------------------------------------------------------------- 116 | export T3XFILES := $(GFXFILES:.t3s=.t3x) 117 | #--------------------------------------------------------------------------------- 118 | else 119 | #--------------------------------------------------------------------------------- 120 | export ROMFS_T3XFILES := $(patsubst %.t3s, $(GFXBUILD)/%.t3x, $(GFXFILES)) 121 | export T3XHFILES := $(patsubst %.t3s, $(BUILD)/%.h, $(GFXFILES)) 122 | #--------------------------------------------------------------------------------- 123 | endif 124 | #--------------------------------------------------------------------------------- 125 | 126 | export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 127 | 128 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) \ 129 | $(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \ 130 | $(addsuffix .o,$(T3XFILES)) 131 | 132 | export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) 133 | 134 | export HFILES := $(PICAFILES:.v.pica=_shbin.h) $(SHLISTFILES:.shlist=_shbin.h) \ 135 | $(addsuffix .h,$(subst .,_,$(BINFILES))) \ 136 | $(GFXFILES:.t3s=.h) 137 | 138 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 139 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 140 | -I$(CURDIR)/$(BUILD) 141 | 142 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 143 | 144 | export _3DSXDEPS := $(if $(NO_SMDH),,$(OUTPUT).smdh) 145 | 146 | ifeq ($(strip $(ICON)),) 147 | icons := $(wildcard *.png) 148 | ifneq (,$(findstring $(TARGET).png,$(icons))) 149 | export APP_ICON := $(TOPDIR)/$(TARGET).png 150 | else 151 | ifneq (,$(findstring icon.png,$(icons))) 152 | export APP_ICON := $(TOPDIR)/icon.png 153 | endif 154 | endif 155 | else 156 | export APP_ICON := $(TOPDIR)/$(ICON) 157 | endif 158 | 159 | export APP_TITLE := blargSNES $(BLARG_VERSION) 160 | export APP_DESCRIPTION := SNES emulator 161 | export APP_AUTHOR := Arisotura & DiscostewSM 162 | 163 | ifeq ($(strip $(NO_SMDH)),) 164 | export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh 165 | endif 166 | 167 | ifneq ($(ROMFS),) 168 | export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) 169 | endif 170 | 171 | .PHONY: all clean 172 | 173 | #--------------------------------------------------------------------------------- 174 | all: $(BUILD) $(GFXBUILD) $(DEPSDIR) $(ROMFS_T3XFILES) $(T3XHFILES) 175 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 176 | 177 | $(BUILD): 178 | @mkdir -p $@ 179 | 180 | ifneq ($(GFXBUILD),$(BUILD)) 181 | $(GFXBUILD): 182 | @mkdir -p $@ 183 | endif 184 | 185 | ifneq ($(DEPSDIR),$(BUILD)) 186 | $(DEPSDIR): 187 | @mkdir -p $@ 188 | endif 189 | 190 | #--------------------------------------------------------------------------------- 191 | clean: 192 | @echo clean ... 193 | @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf $(GFXBUILD) 194 | 195 | #--------------------------------------------------------------------------------- 196 | $(GFXBUILD)/%.t3x $(BUILD)/%.h : %.t3s 197 | #--------------------------------------------------------------------------------- 198 | @echo $(notdir $<) 199 | @tex3ds -i $< -H $(BUILD)/$*.h -d $(DEPSDIR)/$*.d -o $(GFXBUILD)/$*.t3x 200 | 201 | #--------------------------------------------------------------------------------- 202 | else 203 | 204 | #--------------------------------------------------------------------------------- 205 | # main targets 206 | #--------------------------------------------------------------------------------- 207 | 208 | ifeq ($(NO_CIA),) 209 | .PHONY: all 210 | 211 | all: $(OUTPUT).3dsx $(OUTPUT).cia 212 | endif 213 | 214 | $(OUTPUT).3dsx : $(OUTPUT).elf $(_3DSXDEPS) 215 | 216 | $(OFILES_SOURCES) : $(HFILES) 217 | 218 | $(OUTPUT).elf : $(OFILES) 219 | 220 | $(OUTPUT).cia : $(OUTPUT).elf $(_3DSXDEPS) 221 | @makerom -f cia -o $(OUTPUT).cia -exefslogo -elf $(OUTPUT).elf -rsf $(TOPDIR)/banner/app.rsf \ 222 | -ver 0 -icon $(OUTPUT).smdh -banner $(TOPDIR)/banner/banner.bnr 223 | @echo "built ... $(notdir $(OUTPUT)).cia" 224 | 225 | #--------------------------------------------------------------------------------- 226 | # you need a rule like this for each extension you use as binary data 227 | #--------------------------------------------------------------------------------- 228 | %.bin.o %_bin.h : %.bin 229 | #--------------------------------------------------------------------------------- 230 | @echo $(notdir $<) 231 | @$(bin2o) 232 | 233 | #--------------------------------------------------------------------------------- 234 | .PRECIOUS : %.t3x 235 | #--------------------------------------------------------------------------------- 236 | %.t3x.o %_t3x.h : %.t3x 237 | #--------------------------------------------------------------------------------- 238 | @echo $(notdir $<) 239 | @$(bin2o) 240 | 241 | #--------------------------------------------------------------------------------- 242 | # rules for assembling GPU shaders 243 | #--------------------------------------------------------------------------------- 244 | define shader-as 245 | $(eval CURBIN := $*.shbin) 246 | $(eval DEPSFILE := $(DEPSDIR)/$*.shbin.d) 247 | echo "$(CURBIN).o: $< $1" > $(DEPSFILE) 248 | echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h 249 | echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h 250 | echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h 251 | picasso -o $(CURBIN) $1 252 | bin2s $(CURBIN) | $(AS) -o $*.shbin.o 253 | endef 254 | 255 | %.shbin.o %_shbin.h : %.v.pica %.g.pica 256 | @echo $(notdir $^) 257 | @$(call shader-as,$^) 258 | 259 | %.shbin.o %_shbin.h : %.v.pica 260 | @echo $(notdir $<) 261 | @$(call shader-as,$<) 262 | 263 | %.shbin.o %_shbin.h : %.shlist 264 | @echo $(notdir $<) 265 | @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)$(file))) 266 | 267 | #--------------------------------------------------------------------------------- 268 | %.t3x %.h : %.t3s 269 | #--------------------------------------------------------------------------------- 270 | @echo $(notdir $<) 271 | @tex3ds -i $< -H $*.h -d $*.d -o $*.t3x 272 | 273 | -include $(DEPSDIR)/*.d 274 | 275 | #--------------------------------------------------------------------------------------- 276 | endif 277 | #--------------------------------------------------------------------------------------- 278 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | blargSnes 2 | ========= 3 | 4 | http://blargsnes.kuribo64.net/ 5 | 6 | A SNES emulator for the 3DS. If you have seen lolSnes, you know what to expect. 7 | 8 | 9 | Features 10 | * CPU: 100% (only lacking a few unimportant tidbits) 11 | * PPU: ~80% (modes 0-4 and 7, 8x8 and 16x16 tiles, sprites, color math, brightness, windows) 12 | * SPC700: 99% (mostly everything is in) 13 | * DSP: lacking noise and echo; interpolation is linear only 14 | * DMA and HDMA 15 | * SRAM with auto-saving 16 | 17 | Missing, oncoming features 18 | * expansion chips 19 | * hires (modes 5/6), mosaic, other fancy shiz 20 | 21 | 22 | Credits 23 | 24 | * 3dbrew and all the people who made 3DS homebrew possible 25 | * anyone who helped make lolSnes work 26 | * Bond697 and Normmatt for how to use the 3DS syscore 27 | * smealum for ctrulib and for paving the way to the PICA200 28 | * Martin Korth for Fullsnes and no$sns's debugger 29 | * the SNemulDS authors for their DSP emulation code 30 | * DiscostewSM for his fixes and additions 31 | * if you feel I forgot your name here, let me know 32 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | TODO 2 | 3 | short term 4 | 5 | * atleast one expansion chip 6 | * bugfixes (see the GBAtemp thread) 7 | 8 | short-mid term 9 | 10 | * sound via 3DS DSP 11 | * mosaic? this effect is only used for screen transitions (also easier to implement in the hardware renderer) 12 | 13 | long term 14 | 15 | * expansion chips and other fancy shiz 16 | * 3D 17 | 18 | 19 | -------------------------------------------------------------------------------- /banner/app.rsf: -------------------------------------------------------------------------------- 1 | BasicInfo: 2 | Title : blarg 3 | ProductCode : CTR-BL-ARGZ 4 | Logo : Homebrew 5 | 6 | TitleInfo: 7 | Category : Application 8 | UniqueId : 0xB1A96 9 | 10 | Option: 11 | UseOnSD : true # true if App is to be installed to SD 12 | FreeProductCode : true # Removes limitations on ProductCode 13 | MediaFootPadding : false # If true CCI files are created with padding 14 | EnableCrypt : false # Enables encryption for NCCH and CIA 15 | EnableCompress : true # Compresses where applicable (currently only exefs:/.code) 16 | 17 | AccessControlInfo: 18 | CoreVersion : 2 19 | 20 | # Exheader Format Version 21 | DescVersion : 2 22 | 23 | # Minimum Required Kernel Version (below is for 4.5.0) 24 | ReleaseKernelMajor : "02" 25 | ReleaseKernelMinor : "46" 26 | 27 | # ExtData 28 | UseExtSaveData : false # enables ExtData 29 | #ExtSaveDataId : 0x300 # only set this when the ID is different to the UniqueId 30 | 31 | # FS:USER Archive Access Permissions 32 | # Uncomment as required 33 | FileSystemAccess: 34 | - CategorySystemApplication 35 | - CategoryHardwareCheck 36 | - CategoryFileSystemTool 37 | - Debug 38 | - TwlCardBackup 39 | - TwlNandData 40 | - Boss 41 | - DirectSdmc 42 | - Core 43 | - CtrNandRo 44 | - CtrNandRw 45 | - CtrNandRoWrite 46 | - CategorySystemSettings 47 | - CardBoard 48 | - ExportImportIvs 49 | - DirectSdmcWrite 50 | - SwitchCleanup 51 | - SaveDataMove 52 | - Shop 53 | - Shell 54 | - CategoryHomeMenu 55 | - SeedDB 56 | IoAccessControl: 57 | - FsMountNand 58 | - FsMountNandRoWrite 59 | - FsMountTwln 60 | - FsMountWnand 61 | - FsMountCardSpi 62 | - UseSdif3 63 | - CreateSeed 64 | - UseCardSpi 65 | 66 | # Process Settings 67 | MemoryType : Application # Application/System/Base 68 | SystemMode : 64MB # 64MB(Default)/96MB/80MB/72MB/32MB 69 | IdealProcessor : 0 70 | AffinityMask : 1 71 | Priority : 16 72 | MaxCpu : 0x9E # Default 73 | HandleTableSize : 0x200 74 | DisableDebug : false 75 | EnableForceDebug : false 76 | CanWriteSharedPage : true 77 | CanUsePrivilegedPriority : false 78 | CanUseNonAlphabetAndNumber : true 79 | PermitMainFunctionArgument : true 80 | CanShareDeviceMemory : true 81 | RunnableOnSleep : false 82 | SpecialMemoryArrange : true 83 | 84 | # New3DS Exclusive Process Settings 85 | SystemModeExt : Legacy # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode 86 | CpuSpeed : 268MHz # 268MHz(Default)/804MHz 87 | EnableL2Cache : false # false(default)/true 88 | CanAccessCore2 : false 89 | 90 | # Virtual Address Mappings 91 | IORegisterMapping: 92 | - 1ff00000-1ff7ffff # DSP memory 93 | MemoryMapping: 94 | - 1f000000-1f5fffff:r # VRAM 95 | 96 | # Accessible SVCs, : 97 | SystemCallAccess: 98 | ControlMemory: 1 99 | QueryMemory: 2 100 | ExitProcess: 3 101 | GetProcessAffinityMask: 4 102 | SetProcessAffinityMask: 5 103 | GetProcessIdealProcessor: 6 104 | SetProcessIdealProcessor: 7 105 | CreateThread: 8 106 | ExitThread: 9 107 | SleepThread: 10 108 | GetThreadPriority: 11 109 | SetThreadPriority: 12 110 | GetThreadAffinityMask: 13 111 | SetThreadAffinityMask: 14 112 | GetThreadIdealProcessor: 15 113 | SetThreadIdealProcessor: 16 114 | GetCurrentProcessorNumber: 17 115 | Run: 18 116 | CreateMutex: 19 117 | ReleaseMutex: 20 118 | CreateSemaphore: 21 119 | ReleaseSemaphore: 22 120 | CreateEvent: 23 121 | SignalEvent: 24 122 | ClearEvent: 25 123 | CreateTimer: 26 124 | SetTimer: 27 125 | CancelTimer: 28 126 | ClearTimer: 29 127 | CreateMemoryBlock: 30 128 | MapMemoryBlock: 31 129 | UnmapMemoryBlock: 32 130 | CreateAddressArbiter: 33 131 | ArbitrateAddress: 34 132 | CloseHandle: 35 133 | WaitSynchronization1: 36 134 | WaitSynchronizationN: 37 135 | SignalAndWait: 38 136 | DuplicateHandle: 39 137 | GetSystemTick: 40 138 | GetHandleInfo: 41 139 | GetSystemInfo: 42 140 | GetProcessInfo: 43 141 | GetThreadInfo: 44 142 | ConnectToPort: 45 143 | SendSyncRequest1: 46 144 | SendSyncRequest2: 47 145 | SendSyncRequest3: 48 146 | SendSyncRequest4: 49 147 | SendSyncRequest: 50 148 | OpenProcess: 51 149 | OpenThread: 52 150 | GetProcessId: 53 151 | GetProcessIdOfThread: 54 152 | GetThreadId: 55 153 | GetResourceLimit: 56 154 | GetResourceLimitLimitValues: 57 155 | GetResourceLimitCurrentValues: 58 156 | GetThreadContext: 59 157 | Break: 60 158 | OutputDebugString: 61 159 | ControlPerformanceCounter: 62 160 | CreatePort: 71 161 | CreateSessionToPort: 72 162 | CreateSession: 73 163 | AcceptSession: 74 164 | ReplyAndReceive1: 75 165 | ReplyAndReceive2: 76 166 | ReplyAndReceive3: 77 167 | ReplyAndReceive4: 78 168 | ReplyAndReceive: 79 169 | BindInterrupt: 80 170 | UnbindInterrupt: 81 171 | InvalidateProcessDataCache: 82 172 | StoreProcessDataCache: 83 173 | FlushProcessDataCache: 84 174 | StartInterProcessDma: 85 175 | StopDma: 86 176 | GetDmaState: 87 177 | RestartDma: 88 178 | DebugActiveProcess: 96 179 | BreakDebugProcess: 97 180 | TerminateDebugProcess: 98 181 | GetProcessDebugEvent: 99 182 | ContinueDebugEvent: 100 183 | GetProcessList: 101 184 | GetThreadList: 102 185 | GetDebugThreadContext: 103 186 | SetDebugThreadContext: 104 187 | QueryDebugProcessMemory: 105 188 | ReadProcessMemory: 106 189 | WriteProcessMemory: 107 190 | SetHardwareBreakPoint: 108 191 | GetDebugThreadParam: 109 192 | ControlProcessMemory: 112 193 | MapProcessMemory: 113 194 | UnmapProcessMemory: 114 195 | CreateCodeSet: 115 196 | CreateProcess: 117 197 | TerminateProcess: 118 198 | SetProcessResourceLimits: 119 199 | CreateResourceLimit: 120 200 | SetResourceLimitValues: 121 201 | AddCodeSegment: 122 202 | Backdoor: 123 203 | KernelSetState: 124 204 | QueryProcessMemory: 125 205 | 206 | # Service List 207 | # Maximum 34 services (32 if firmware is prior to 9.6.0) 208 | ServiceAccessControl: 209 | - APT:U 210 | - ac:u 211 | - am:net 212 | - boss:U 213 | - cam:u 214 | - cecd:u 215 | - cfg:nor 216 | - cfg:u 217 | - csnd:SND 218 | - dsp::DSP 219 | - frd:u 220 | - fs:USER 221 | - gsp::Gpu 222 | - gsp::Lcd 223 | - hid:USER 224 | - http:C 225 | - ir:rst 226 | - ir:u 227 | - ir:USER 228 | - mic:u 229 | - ndm:u 230 | - news:s 231 | - nwm::EXT 232 | - nwm::UDS 233 | - ptm:sysm 234 | - ptm:u 235 | - pxi:dev 236 | - soc:U 237 | - ssl:C 238 | - y2r:u 239 | 240 | 241 | SystemControlInfo: 242 | SaveDataSize: 0KB # Change if the app uses savedata 243 | RemasterVersion: 2 244 | StackSize: 0x40000 245 | 246 | # Modules that run services listed above should be included below 247 | # Maximum 48 dependencies 248 | # : 249 | Dependency: 250 | ac: 0x0004013000002402 251 | #act: 0x0004013000003802 252 | am: 0x0004013000001502 253 | boss: 0x0004013000003402 254 | camera: 0x0004013000001602 255 | cecd: 0x0004013000002602 256 | cfg: 0x0004013000001702 257 | codec: 0x0004013000001802 258 | csnd: 0x0004013000002702 259 | dlp: 0x0004013000002802 260 | dsp: 0x0004013000001a02 261 | friends: 0x0004013000003202 262 | gpio: 0x0004013000001b02 263 | gsp: 0x0004013000001c02 264 | hid: 0x0004013000001d02 265 | http: 0x0004013000002902 266 | i2c: 0x0004013000001e02 267 | ir: 0x0004013000003302 268 | mcu: 0x0004013000001f02 269 | mic: 0x0004013000002002 270 | ndm: 0x0004013000002b02 271 | news: 0x0004013000003502 272 | #nfc: 0x0004013000004002 273 | nim: 0x0004013000002c02 274 | nwm: 0x0004013000002d02 275 | pdn: 0x0004013000002102 276 | ps: 0x0004013000003102 277 | ptm: 0x0004013000002202 278 | #qtm: 0x0004013020004202 279 | ro: 0x0004013000003702 280 | socket: 0x0004013000002e02 281 | spi: 0x0004013000002302 282 | ssl: 0x0004013000002f02 -------------------------------------------------------------------------------- /banner/banner.bnr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arisotura/blargSnes/f3ab5a96ad694e30c43a8701a066126a33f43840/banner/banner.bnr -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arisotura/blargSnes/f3ab5a96ad694e30c43a8701a066126a33f43840/icon.png -------------------------------------------------------------------------------- /source/audio.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #include <3ds.h> 20 | #include 21 | 22 | #include "dsp.h" 23 | #include "mixrate.h" 24 | 25 | 26 | // 0 = none, 2 = DSP 27 | int Audio_Type = 0; 28 | 29 | ndspWaveBuf waveBuf; 30 | 31 | s16* Audio_Buffer; 32 | 33 | s32 cursample = 0; 34 | 35 | bool isPlaying = false; 36 | 37 | 38 | void Audio_Init() 39 | { 40 | if (Audio_Type) 41 | return; 42 | 43 | if (R_FAILED(ndspInit())) 44 | return; 45 | 46 | ndspSetOutputMode(NDSP_OUTPUT_STEREO); 47 | ndspSetOutputCount(1); 48 | ndspSetMasterVol(1.0f); 49 | 50 | 51 | Audio_Buffer = (s16*)linearAlloc(MIXBUFSIZE*2*2); 52 | memset(Audio_Buffer, 0, MIXBUFSIZE*2*2); 53 | 54 | Audio_Type = 2; 55 | cursample = 0; 56 | 57 | isPlaying = false; 58 | 59 | } 60 | 61 | void Audio_DeInit() 62 | { 63 | if (!Audio_Type) 64 | return; 65 | 66 | linearFree(Audio_Buffer); 67 | 68 | ndspChnWaveBufClear(0); 69 | ndspExit(); 70 | 71 | Audio_Type = 0; 72 | isPlaying = false; 73 | } 74 | 75 | void Audio_Pause() 76 | { 77 | if (!Audio_Type) 78 | return; 79 | 80 | if(isPlaying) 81 | { 82 | // stop 83 | 84 | ndspChnWaveBufClear(0); 85 | 86 | memset(Audio_Buffer, 0, MIXBUFSIZE*2*2); 87 | DSP_FlushDataCache(Audio_Buffer, MIXBUFSIZE*2*2); 88 | 89 | isPlaying = false; 90 | } 91 | } 92 | 93 | void Audio_Mix(u32 samples, bool restart) 94 | { 95 | if (!Audio_Type) 96 | return; 97 | 98 | cursample = DspMixSamplesStereo(samples, Audio_Buffer, MIXBUFSIZE, cursample, restart); 99 | } 100 | 101 | bool Audio_Begin() 102 | { 103 | if (!Audio_Type) 104 | return 0; 105 | 106 | if (isPlaying) 107 | return 0; 108 | 109 | float mix[12]; 110 | memset(mix, 0, sizeof(mix)); 111 | 112 | mix[0] = mix[1] = 1.0f; 113 | ndspChnReset(0); 114 | ndspChnSetInterp(0, NDSP_INTERP_LINEAR); 115 | ndspChnSetRate(0, 32000.0f); 116 | ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16); 117 | ndspChnSetMix(0, mix); 118 | 119 | memset(&waveBuf, 0, sizeof(ndspWaveBuf)); 120 | waveBuf.data_vaddr = Audio_Buffer; 121 | waveBuf.nsamples = MIXBUFSIZE; 122 | waveBuf.looping = true; 123 | waveBuf.status = NDSP_WBUF_FREE; 124 | 125 | memset(Audio_Buffer, 0, MIXBUFSIZE*2*2); 126 | DSP_FlushDataCache(Audio_Buffer, MIXBUFSIZE*2*2); 127 | 128 | ndspChnWaveBufAdd(0, &waveBuf); 129 | 130 | cursample = MIXBUFSIZE >> 1; 131 | isPlaying = true; 132 | return 1; 133 | } 134 | -------------------------------------------------------------------------------- /source/audio.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #ifndef AUDIO_H 20 | #define AUDIO_H 21 | 22 | #include "mixrate.h" 23 | 24 | void Audio_Init(); 25 | void Audio_DeInit(); 26 | 27 | void Audio_Pause(); 28 | 29 | bool Audio_Begin(); 30 | 31 | void Audio_Mix(u32 samples, bool restart); 32 | 33 | void Audio_Inc(); 34 | void Audio_Dec(); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /source/blargGL.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #ifndef BGL_H 20 | #define BGL_H 21 | 22 | #include 23 | #include 24 | #include <3ds/types.h> 25 | #include <3ds/gpu/registers.h> 26 | #include <3ds/gpu/gpu.h> 27 | #include <3ds/gpu/shbin.h> 28 | #include <3ds/gpu/shaderProgram.h> 29 | 30 | // blargGL -- thin wrapper around the ctrulib GPU API 31 | // Not meant to be on par with OpenGL, just meant to be somewhat sane~ 32 | 33 | void bglInit(); 34 | void bglDeInit(); 35 | 36 | 37 | void bglUseShader(shaderProgram_s* shader); 38 | 39 | u32 bglUniformLoc(GPU_SHADER_TYPE type, const char* name); 40 | void bglUniform(GPU_SHADER_TYPE type, u32 id, float* val); 41 | void bglUniformMatrix(GPU_SHADER_TYPE type, u32 id, float* val); 42 | 43 | void bglOutputBuffers(void* color, void* depth, GPU_TEXCOLOR colortype, u32 w, u32 h); 44 | void bglViewport(u32 x, u32 y, u32 w, u32 h); 45 | 46 | void bglScissorMode(GPU_SCISSORMODE mode); 47 | void bglScissor(u32 x, u32 y, u32 w, u32 h); 48 | 49 | void bglDepthRange(float min, float max); 50 | void bglEnableDepthTest(bool enable); 51 | void bglDepthFunc(GPU_TESTFUNC func); 52 | 53 | void bglFaceCulling(GPU_CULLMODE mode); 54 | 55 | void bglEnableStencilTest(bool enable); 56 | void bglStencilFunc(GPU_TESTFUNC func, u32 ref, u32 mask, u32 replace); 57 | void bglStencilOp(GPU_STENCILOP sfail, GPU_STENCILOP dfail, GPU_STENCILOP pass); 58 | 59 | void bglColorDepthMask(GPU_WRITEMASK mask); 60 | 61 | void bglEnableAlphaTest(bool enable); 62 | void bglAlphaFunc(GPU_TESTFUNC func, u32 ref); 63 | 64 | void bglBlendColor(u32 r, u32 g, u32 b, u32 a); 65 | void bglBlendEquation(GPU_BLENDEQUATION coloreq, GPU_BLENDEQUATION alphaeq); 66 | void bglBlendFunc(GPU_BLENDFACTOR colorsrc, GPU_BLENDFACTOR colordst, GPU_BLENDFACTOR alphasrc, GPU_BLENDFACTOR alphadst); 67 | 68 | void bglEnableTextures(GPU_TEXUNIT units); 69 | void bglTexEnv(u32 id, u32 colorsrc, u32 alphasrc, u32 colorop, u32 alphaop, GPU_COMBINEFUNC colorcomb, GPU_COMBINEFUNC alphacomb, u32 constcol); 70 | void bglDummyTexEnv(u32 id); 71 | void bglTexImage(GPU_TEXUNIT unit, void* data, u32 width, u32 height, u32 param, GPU_TEXCOLOR colortype); 72 | 73 | void bglNumAttribs(u32 num); 74 | void bglAttribBuffer(void* data); 75 | void bglAttribType(u32 id, GPU_FORMATS datatype, u32 numcomps); 76 | 77 | void bglDrawArrays(GPU_Primitive_t type, u32 numvertices); 78 | 79 | void bglFlush(); 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /source/config.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include <3ds.h> 24 | 25 | #include "main.h" 26 | #include "config.h" 27 | 28 | Config_t Config; 29 | 30 | const char* configFilePath = "/blargSnes.ini"; 31 | 32 | const char* configFileL = 33 | "HardwareRenderer=%d\n" 34 | "ScaleMode=%d\n" 35 | "DirPath=%[^\t\n]\n" 36 | "VSync=%d\n" 37 | "FrameSkip=%d\n"; 38 | 39 | const char* configFileS = 40 | "HardwareRenderer=%d\n" 41 | "ScaleMode=%d\n" 42 | "DirPath=%s\n" 43 | "VSync=%d\n" 44 | "FrameSkip=%d\n"; 45 | 46 | char lastDir[0x106]; 47 | 48 | 49 | void LoadConfig(u8 init) 50 | { 51 | char tempDir[0x106]; 52 | Config.HardwareRenderer = 1; 53 | Config.ScaleMode = 0; 54 | Config.VSync = 0; 55 | Config.FrameSkip = 0; 56 | if(init) 57 | { 58 | strncpy(Config.DirPath,"/\0",2); 59 | strncpy(lastDir,"/\0",2); 60 | } 61 | 62 | FILE *pFile = fopen(configFilePath, "rb"); 63 | if (pFile == NULL) 64 | return; 65 | 66 | fseek(pFile, 0, SEEK_END); 67 | u32 size = ftell(pFile); 68 | if (!size) 69 | { 70 | fclose(pFile); 71 | return; 72 | } 73 | 74 | char* tempbuf = (char*)malloc(size + 1); 75 | fseek(pFile, 0, SEEK_SET); 76 | fread(tempbuf, sizeof(char), size, pFile); 77 | tempbuf[size] = '\0'; 78 | 79 | sscanf(tempbuf, configFileL, 80 | &Config.HardwareRenderer, 81 | &Config.ScaleMode, 82 | tempDir, 83 | &Config.VSync, 84 | &Config.FrameSkip); 85 | 86 | if (Config.HardwareMode7Filter == -1) 87 | Config.HardwareMode7Filter = 0; 88 | 89 | if (init && strlen(tempDir) > 0 && tempDir[0] == '/') 90 | { 91 | DIR *pDir = opendir(tempDir); 92 | if (pDir != NULL) 93 | { 94 | strncpy(Config.DirPath, tempDir, 0x106); 95 | strncpy(lastDir, tempDir, 0x106); 96 | closedir(pDir); 97 | } 98 | 99 | } 100 | 101 | fclose(pFile); 102 | free(tempbuf); 103 | } 104 | 105 | void SaveConfig(u8 saveCurDir) 106 | { 107 | char tempDir[0x106]; 108 | if (!saveCurDir) 109 | strncpy(tempDir,lastDir,0x106); 110 | else 111 | strncpy(tempDir,Config.DirPath,0x106); 112 | 113 | FILE *pFile = fopen(configFilePath, "wb"); 114 | if (pFile == NULL) 115 | { 116 | bprintf("Error while saving config\n"); 117 | return; 118 | } 119 | 120 | 121 | char* tempbuf = (char*)malloc(1024); 122 | u32 size = snprintf(tempbuf, 1024, configFileS, 123 | Config.HardwareRenderer, 124 | Config.ScaleMode, 125 | tempDir, 126 | Config.VSync, 127 | Config.FrameSkip); 128 | 129 | fwrite(tempbuf, sizeof(char), size, pFile); 130 | fclose(pFile); 131 | 132 | 133 | free(tempbuf); 134 | 135 | if (saveCurDir) 136 | strncpy(lastDir,Config.DirPath,0x106); 137 | } 138 | -------------------------------------------------------------------------------- /source/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #ifndef CONFIG_H 20 | #define CONFIG_H 21 | 22 | typedef struct 23 | { 24 | int HardwareRenderer; 25 | int ScaleMode; 26 | char DirPath[0x106]; 27 | int HardwareMode7Filter; 28 | int VSync; 29 | int FrameSkip; 30 | 31 | } Config_t; 32 | 33 | extern Config_t Config; 34 | 35 | void LoadConfig(u8 init); 36 | void SaveConfig(u8 saveCurDir); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /source/cpu.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #ifndef _CPU_H_ 20 | #define _CPU_H_ 21 | 22 | typedef union 23 | { 24 | u16 val; 25 | struct 26 | { 27 | u16 C :1, 28 | Z :1, 29 | I :1, 30 | D :1, 31 | X :1, // B in emulation mode 32 | M :1, 33 | V :1, 34 | N :1, 35 | E :1, // actually not in P, but hey, we have to keep it somewhere 36 | W :1, // not even an actual flag, set by WAI 37 | I2 :1, // not an actual flag either, set when NMI's are disabled 38 | :5; 39 | }; 40 | } CPU_P; 41 | 42 | typedef struct 43 | { 44 | u32 _memoryMap; 45 | u32 _opTable; 46 | u16 nLines; 47 | s16 nCycles; 48 | CPU_P P; 49 | u16 PC; 50 | u16 DBR; 51 | u16 D; 52 | u16 PBR; 53 | u16 S; 54 | u32 Y; 55 | u32 X; 56 | u32 A; 57 | } CPU_Regs_t; 58 | 59 | extern CPU_Regs_t CPU_Regs; 60 | 61 | 62 | void CPU_Reset(); 63 | void CPU_MainLoop(); 64 | void CPU_Run(); 65 | 66 | void CPU_TriggerIRQ(); 67 | void CPU_TriggerNMI(); 68 | 69 | // debugging crap 70 | u32 CPU_GetPC(); 71 | u32 CPU_GetReg(u32 reg); 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /source/cpu.inc: -------------------------------------------------------------------------------- 1 | @ ----------------------------------------------------------------------------- 2 | @ Copyright 2014-2022 Arisotura 3 | @ 4 | @ This file is part of blargSnes. 5 | @ 6 | @ blargSnes is free software: you can redistribute it and/or modify it under 7 | @ the terms of the GNU General Public License as published by the Free 8 | @ Software Foundation, either version 3 of the License, or (at your option) 9 | @ any later version. 10 | @ 11 | @ blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | @ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | @ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | @ 15 | @ You should have received a copy of the GNU General Public License along 16 | @ with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | @ ----------------------------------------------------------------------------- 18 | 19 | @ --- Register aliases -------------------------------------------------------- 20 | 21 | snesA .req r12 22 | snesX .req r11 23 | snesY .req r10 24 | snesS .req r9 @ high hword: S 25 | snesPBR .req r9 @ low hword: PBR 26 | snesD .req r8 @ high hword: D 27 | snesDBR .req r8 @ low hword: DBR 28 | snesPC .req r7 @ high hword: PC 29 | snesP .req r7 @ low hword: P 30 | snesCycles .req r6 @ high hword: master cycle count 31 | opTable .req r5 @ pointer to the opcode table in use 32 | memoryMap .req r4 @ pointer to the memory map table 33 | snesStatus .req r4 @ status data is before this pointer 34 | 35 | @ --- Variables and whatever -------------------------------------------------- 36 | 37 | .equ SRAMDirty, -4 38 | .equ HVBFlags, -5 39 | .equ IRQCond, -6 40 | .equ ScreenHeight, -7 41 | .equ TotalLines, -8 42 | .equ SRAMMask, -12 43 | .equ HCount, -14 44 | .equ HCountFull, -16 45 | .equ VCount, -18 46 | .equ IRQ_CurHMatch, -20 47 | .equ IRQ_HMatch, -22 48 | .equ IRQ_VMatch, -24 49 | .equ SPC_LastCycle, -28 50 | .equ SPC_CycleRatio, -32 51 | .equ SPC_CyclesPerLine, -36 52 | .equ LastBusVal, -38 53 | 54 | .equ flagC, 0x01 55 | .equ flagZ, 0x02 56 | .equ flagI, 0x04 57 | .equ flagD, 0x08 58 | .equ flagX, 0x10 59 | .equ flagB, 0x10 60 | .equ flagM, 0x20 61 | .equ flagV, 0x40 62 | .equ flagN, 0x80 63 | .equ flagE, 0x100 @ not actually in P, but hey, we have to keep it somewhere 64 | .equ flagW, 0x200 @ not even an actual flag... set by WAI 65 | .equ flagNMI, 0x400 @ NMI requested 66 | .equ flagDMA, 0x800 @ DMA transfer in progress 67 | 68 | .equ flagNVZC, 0xC3 69 | .equ flagNZ, 0x82 70 | .equ flagNZC, 0x83 71 | .equ flagNVZ, 0xC2 72 | 73 | @ --- Handy macros ------------------------------------------------------------ 74 | 75 | .macro SafeCall func 76 | stmdb sp!, {r12} 77 | bl \func 78 | ldmia sp!, {r12} 79 | .endm 80 | 81 | .macro SafeCall_3 func 82 | stmdb sp!, {r3, r12} 83 | bl \func 84 | ldmia sp!, {r3, r12} 85 | .endm 86 | 87 | .macro SafeCall_03 func 88 | stmdb sp!, {r0, r3, r12} 89 | bl \func 90 | ldmia sp!, {r0, r3, r12} 91 | .endm 92 | 93 | @ --- Debugging --------------------------------------------------------------- 94 | 95 | .macro DbgPrint 96 | stmdb sp!, {r12, lr} 97 | bl bprintf 98 | ldmia sp!, {r12, lr} 99 | .endm 100 | 101 | .macro DbgPrintAndHalt stuff 102 | add r0, pc, #8 103 | bl bprintf 104 | 1: 105 | swi #0x50000 106 | b 1b 107 | .ascii \stuff 108 | .byte 0 109 | .align 4 110 | .endm 111 | -------------------------------------------------------------------------------- /source/dma.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #include "snes.h" 20 | #include "ppu.h" 21 | 22 | 23 | u8 DMA_Chans[8*16]; 24 | u8 DMA_HDMAFlag; 25 | u8 DMA_HDMACurFlag; 26 | u8 DMA_HDMAEnded; 27 | 28 | u8 HDMA_Pause[8]; 29 | 30 | 31 | u8 DMA_Read8(u32 addr) 32 | { 33 | u8 ret = (addr > 0x7F) ? SNES_Status->LastBusVal : DMA_Chans[addr]; 34 | return ret; 35 | } 36 | 37 | u16 DMA_Read16(u32 addr) 38 | { 39 | u16 ret = (addr > 0x7F) ? (SNES_Status->LastBusVal|(SNES_Status->LastBusVal<<8)) : *(u16*)&DMA_Chans[addr]; 40 | return ret; 41 | } 42 | 43 | void DMA_Write8(u32 addr, u8 val) 44 | { 45 | if (addr < 0x80) 46 | DMA_Chans[addr] = val; 47 | } 48 | 49 | void DMA_Write16(u32 addr, u16 val) 50 | { 51 | if (addr < 0x80) 52 | { 53 | *(u16*)&DMA_Chans[addr] = val; 54 | } 55 | } 56 | 57 | void DMA_Enable(u8 flag) 58 | { 59 | int c; 60 | for (c = 0; c < 8; c++) 61 | { 62 | if (!(flag & (1 << c))) 63 | continue; 64 | 65 | u8* chan = &DMA_Chans[c << 4]; 66 | u8 params = chan[0]; 67 | 68 | u16 maddrinc; 69 | switch (params & 0x18) 70 | { 71 | case 0x00: maddrinc = 1; break; 72 | case 0x10: maddrinc = -1; break; 73 | default: maddrinc = 0; break; 74 | } 75 | 76 | u8 paddrinc = params & 0x07; 77 | 78 | u8 ppuaddr = chan[1]; 79 | u16 memaddr = *(u16*)&chan[2]; 80 | u32 membank = chan[4] << 16; 81 | u32 bytecount = *(u16*)&chan[5]; 82 | if (!bytecount) bytecount = 0x10000; 83 | 84 | //bprintf("DMA%d %d %06X %s 21%02X m:%d p:%d\n", c, bytecount, memaddr|membank, (params&0x80)?"<-":"->", ppuaddr, maddrinc, paddrinc); 85 | 86 | u8 scheck = params & 0x9F; 87 | if (scheck == 0x00 || scheck == 0x02) 88 | { 89 | if (ppuaddr == 0x04) 90 | { 91 | // coolspot: transfer at scanline 205 92 | //bprintf("OAM DMA %04X %02X AT %d\n", PPU.OAMAddr, bytecount, SNES_Status->VCount); 93 | while (bytecount > 1) 94 | { 95 | if (PPU.OAMAddr >= 0x200) 96 | { 97 | *(u16*)&PPU.OAM[PPU.OAMAddr & 0x21F] = SNES_Read16(membank|memaddr); 98 | } 99 | else 100 | { 101 | *(u16*)&PPU.OAM[PPU.OAMAddr] = SNES_Read16(membank|memaddr); 102 | } 103 | memaddr += maddrinc<<1; 104 | bytecount -= 2; 105 | PPU.OAMAddr += 2; 106 | PPU.OAMAddr &= ~0x400; 107 | } 108 | PPU.OBJDirty |= 0x02; 109 | } 110 | else if (ppuaddr == 0x22) 111 | { 112 | while (bytecount > 1) 113 | { 114 | PPU_SetColor(PPU.CGRAMAddr >> 1, SNES_Read16(membank|memaddr)); 115 | memaddr += maddrinc<<1; 116 | bytecount -= 2; 117 | PPU.CGRAMAddr += 2; 118 | PPU.CGRAMAddr &= ~0x200; 119 | } 120 | } 121 | } 122 | else if (scheck == 0x01) 123 | { 124 | if (ppuaddr == 0x18) 125 | { 126 | while (bytecount > 1) 127 | { 128 | u16 newval = SNES_Read16(membank|memaddr); 129 | u32 newaddr = PPU_TranslateVRAMAddress(PPU.VRAMAddr); 130 | if (newval != *(u16*)&PPU.VRAM[newaddr]) 131 | { 132 | if (PPU.HardwareRenderer) 133 | PPU_ConvertVRAM16(newaddr, newval); 134 | *(u16*)&PPU.VRAM[newaddr] = newval; 135 | } 136 | memaddr += maddrinc<<1; 137 | bytecount -= 2; 138 | PPU.VRAMAddr += PPU.VRAMStep; 139 | 140 | } 141 | } 142 | } 143 | 144 | if (bytecount > 0) 145 | { 146 | if (params & 0x80) 147 | { 148 | for (;;) 149 | { 150 | switch (paddrinc) 151 | { 152 | case 0: 153 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 154 | memaddr += maddrinc; bytecount--; 155 | break; 156 | case 1: 157 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 158 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 159 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr+1)); 160 | memaddr += maddrinc; bytecount--; 161 | break; 162 | case 2: 163 | case 6: 164 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 165 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 166 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 167 | memaddr += maddrinc; bytecount--; 168 | break; 169 | case 3: 170 | case 7: 171 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 172 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 173 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 174 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 175 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr+1)); 176 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 177 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr+1)); 178 | memaddr += maddrinc; bytecount--; 179 | break; 180 | case 4: 181 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 182 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 183 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr+1)); 184 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 185 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr+2)); 186 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 187 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr+3)); 188 | memaddr += maddrinc; bytecount--; 189 | break; 190 | case 5: 191 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 192 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 193 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr+1)); 194 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 195 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 196 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 197 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr+1)); 198 | memaddr += maddrinc; bytecount--; 199 | break; 200 | } 201 | 202 | if (!bytecount) break; 203 | } 204 | } 205 | else 206 | { 207 | for (;;) 208 | { 209 | switch (paddrinc) 210 | { 211 | case 0: 212 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 213 | memaddr += maddrinc; bytecount--; 214 | break; 215 | case 1: 216 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 217 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 218 | PPU_Write8(ppuaddr+1, SNES_Read8(membank|memaddr)); 219 | memaddr += maddrinc; bytecount--; 220 | break; 221 | case 2: 222 | case 6: 223 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 224 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 225 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 226 | memaddr += maddrinc; bytecount--; 227 | break; 228 | case 3: 229 | case 7: 230 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 231 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 232 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 233 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 234 | PPU_Write8(ppuaddr+1, SNES_Read8(membank|memaddr)); 235 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 236 | PPU_Write8(ppuaddr+1, SNES_Read8(membank|memaddr)); 237 | memaddr += maddrinc; bytecount--; 238 | break; 239 | case 4: 240 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 241 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 242 | PPU_Write8(ppuaddr+1, SNES_Read8(membank|memaddr)); 243 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 244 | PPU_Write8(ppuaddr+2, SNES_Read8(membank|memaddr)); 245 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 246 | PPU_Write8(ppuaddr+3, SNES_Read8(membank|memaddr)); 247 | memaddr += maddrinc; bytecount--; 248 | break; 249 | case 5: 250 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 251 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 252 | PPU_Write8(ppuaddr+1, SNES_Read8(membank|memaddr)); 253 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 254 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 255 | memaddr += maddrinc; bytecount--; if (!bytecount) break; 256 | PPU_Write8(ppuaddr+1, SNES_Read8(membank|memaddr)); 257 | memaddr += maddrinc; bytecount--; 258 | break; 259 | } 260 | 261 | if (!bytecount) break; 262 | } 263 | } 264 | } 265 | 266 | *(u16*)&chan[2] = memaddr; 267 | *(u16*)&chan[5] = 0; 268 | } 269 | } 270 | 271 | void DMA_ReloadHDMA() 272 | { 273 | register u8 flag = DMA_HDMACurFlag; 274 | register u8 ended = DMA_HDMAEnded; 275 | flag = DMA_HDMAFlag; 276 | if (flag) 277 | { 278 | int c; 279 | for (c = 0; c < 8; c++) 280 | { 281 | if (!(flag & (1 << c))) 282 | continue; 283 | 284 | u8* chan = &DMA_Chans[c << 4]; 285 | 286 | // reload table address 287 | u16 tableaddr = *(u16*)&chan[2]; 288 | u32 tablebank = chan[4] << 16; 289 | 290 | // load first repeatflag 291 | chan[10] = SNES_Read8(tablebank|tableaddr); 292 | if(!chan[10]) 293 | { 294 | if(chan[0] & 0x40) 295 | { 296 | u16 memaddr = SNES_Read16(tableaddr|tablebank); 297 | *(u16*)&chan[5] = memaddr; 298 | tableaddr++; 299 | } 300 | *(u16*)&chan[8] = tableaddr + 1; 301 | flag &= ~(1 << c); 302 | ended |= (1 << c); 303 | HDMA_Pause[c] = 1; 304 | continue; 305 | } 306 | 307 | tableaddr++; 308 | HDMA_Pause[c] = 0; 309 | 310 | if (chan[0] & 0x40) 311 | { 312 | u16 memaddr = SNES_Read16(tableaddr|tablebank); 313 | *(u16*)&chan[5] = memaddr; 314 | 315 | tableaddr += 2; 316 | } 317 | 318 | *(u16*)&chan[8] = tableaddr; 319 | } 320 | } 321 | } 322 | 323 | const u8 hdma_sizes[8] = {1, 2, 2, 4, 4, 4, 2, 4}; 324 | 325 | void DMA_DoHDMA() 326 | { 327 | register u8 flag = DMA_HDMACurFlag; 328 | register u8 ended = DMA_HDMAEnded; 329 | flag &= ~ended; 330 | if (flag) 331 | { 332 | int c; 333 | for (c = 0; c < 8; c++) 334 | { 335 | if (!(flag & (1 << c))) 336 | continue; 337 | 338 | u8* chan = &DMA_Chans[c << 4]; 339 | 340 | u16 tableaddr = *(u16*)&chan[8]; 341 | u32 tablebank = chan[4] << 16; 342 | 343 | u8 repeatflag = chan[10]; 344 | if (repeatflag == 0) 345 | continue; 346 | 347 | if (!HDMA_Pause[c]) 348 | { 349 | u8 params = chan[0]; 350 | 351 | u8 paddrinc = params & 0x07; 352 | 353 | u8 ppuaddr = chan[1]; 354 | 355 | u16 memaddr; 356 | u32 membank; 357 | 358 | if (params & 0x40) 359 | { 360 | memaddr = *(u16*)&chan[5]; 361 | membank = chan[7] << 16; 362 | } 363 | else 364 | { 365 | memaddr = tableaddr; 366 | membank = tablebank; 367 | } 368 | 369 | if (params & 0x80) 370 | { 371 | switch (paddrinc) 372 | { 373 | case 0: 374 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 375 | break; 376 | case 1: 377 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 378 | memaddr++; 379 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr+1)); 380 | break; 381 | case 2: 382 | case 6: 383 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 384 | memaddr++; 385 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 386 | break; 387 | case 3: 388 | case 7: 389 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 390 | memaddr++; 391 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 392 | memaddr++; 393 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr+1)); 394 | memaddr++; 395 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr+1)); 396 | break; 397 | case 4: 398 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 399 | memaddr++; 400 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr+1)); 401 | memaddr++; 402 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr+2)); 403 | memaddr++; 404 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr+3)); 405 | break; 406 | case 5: 407 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 408 | memaddr++; 409 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr+1)); 410 | memaddr++; 411 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr)); 412 | memaddr++; 413 | SNES_Write8(membank|memaddr, PPU_Read8(ppuaddr+1)); 414 | break; 415 | } 416 | } 417 | else 418 | { 419 | switch (paddrinc) 420 | { 421 | case 0: 422 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 423 | break; 424 | case 1: 425 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 426 | memaddr++; 427 | PPU_Write8(ppuaddr+1, SNES_Read8(membank|memaddr)); 428 | break; 429 | case 2: 430 | case 6: 431 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 432 | memaddr++; 433 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 434 | break; 435 | case 3: 436 | case 7: 437 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 438 | memaddr++; 439 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 440 | memaddr++; 441 | PPU_Write8(ppuaddr+1, SNES_Read8(membank|memaddr)); 442 | memaddr++; 443 | PPU_Write8(ppuaddr+1, SNES_Read8(membank|memaddr)); 444 | break; 445 | case 4: 446 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 447 | memaddr++; 448 | PPU_Write8(ppuaddr+1, SNES_Read8(membank|memaddr)); 449 | memaddr++; 450 | PPU_Write8(ppuaddr+2, SNES_Read8(membank|memaddr)); 451 | memaddr++; 452 | PPU_Write8(ppuaddr+3, SNES_Read8(membank|memaddr)); 453 | break; 454 | case 5: 455 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 456 | memaddr++; 457 | PPU_Write8(ppuaddr+1, SNES_Read8(membank|memaddr)); 458 | memaddr++; 459 | PPU_Write8(ppuaddr, SNES_Read8(membank|memaddr)); 460 | memaddr++; 461 | PPU_Write8(ppuaddr+1, SNES_Read8(membank|memaddr)); 462 | break; 463 | } 464 | } 465 | 466 | if (!(params & 0x40)) 467 | tableaddr += hdma_sizes[paddrinc]; 468 | else 469 | { 470 | memaddr++; 471 | *(u16*)&chan[5] = memaddr; 472 | } 473 | } 474 | 475 | repeatflag--; 476 | if (!(repeatflag & 0x80)) 477 | { 478 | if (!repeatflag) 479 | { 480 | chan[10] = SNES_Read8(tablebank|tableaddr); 481 | if(!chan[10]) 482 | { 483 | if(chan[0] & 0x40) 484 | { 485 | u16 memaddr = SNES_Read16(tableaddr|tablebank); 486 | *(u16*)&chan[5] = memaddr; 487 | tableaddr++; 488 | } 489 | *(u16*)&chan[8] = tableaddr + 1; 490 | 491 | flag &= ~(1 << c); 492 | ended |= (1 << c); 493 | HDMA_Pause[c] = 1; 494 | continue; 495 | } 496 | 497 | tableaddr++; 498 | HDMA_Pause[c] = 0; 499 | 500 | if (chan[0] & 0x40) 501 | { 502 | u16 maddr = SNES_Read16(tableaddr|tablebank); 503 | *(u16*)&chan[5] = maddr; 504 | tableaddr += 2; 505 | } 506 | } 507 | else 508 | { 509 | chan[10] = repeatflag; 510 | HDMA_Pause[c] = 1; 511 | } 512 | } 513 | else 514 | { 515 | if (repeatflag == 0x80) 516 | { 517 | chan[10] = SNES_Read8(tablebank|tableaddr); 518 | if(!chan[10]) 519 | { 520 | if(chan[0] & 0x40) 521 | { 522 | u16 memaddr = SNES_Read16(tableaddr|tablebank); 523 | *(u16*)&chan[5] = memaddr; 524 | tableaddr++; 525 | } 526 | *(u16*)&chan[8] = tableaddr + 1; 527 | 528 | flag &= ~(1 << c); 529 | ended |= (1 << c); 530 | HDMA_Pause[c] = 1; 531 | continue; 532 | } 533 | 534 | tableaddr++; 535 | HDMA_Pause[c] = 0; 536 | 537 | if (chan[0] & 0x40) 538 | { 539 | u16 maddr = SNES_Read16(tableaddr|tablebank); 540 | *(u16*)&chan[5] = maddr; 541 | tableaddr += 2; 542 | } 543 | 544 | } 545 | else 546 | chan[10] = repeatflag; 547 | } 548 | 549 | *(u16*)&chan[8] = tableaddr; 550 | } 551 | } 552 | } 553 | -------------------------------------------------------------------------------- /source/dsp.c: -------------------------------------------------------------------------------- 1 | // This code has been taken from SNemulDS which is licensed under GPLv2. 2 | // Credits go to Archeide and whoever else participated in this. 3 | 4 | #include 5 | 6 | #include "main.h" 7 | #include "dsp.h" 8 | 9 | 10 | // DSP write buffers 11 | // * 32 16-sample periods 12 | // * over a 16-sample period -> 512 cycles 13 | // * 4 cycles atleast per write -> 128 writes at most 14 | // * double-buffered 15 | // -> 8192 entries (16K) 16 | 17 | 18 | u16 DSP_WriteDSPBuffer[2][8192]; 19 | u16* DSP_WritingWriteDSPBuffer; 20 | u16* DSP_PlayingWriteDSPBuffer; 21 | 22 | u16 DSP_WriteTimeBuffer[2][8192]; 23 | u16* DSP_WritingWriteTimeBuffer; 24 | u16* DSP_PlayingWriteTimeBuffer; 25 | 26 | // Envelope timing table. Number of counts that should be subtracted from the counter 27 | // The counter starts at 30720 (0x7800). 28 | static const s16 ENVCNT_START = 0x7800; 29 | static const s16 ENVCNT[0x20] = { 30 | 0x0000,0x000F,0x0014,0x0018,0x001E,0x0028,0x0030,0x003C, 31 | 0x0050,0x0060,0x0078,0x00A0,0x00C0,0x00F0,0x0140,0x0180, 32 | 0x01E0,0x0280,0x0300,0x03C0,0x0500,0x0600,0x0780,0x0A00, 33 | 0x0C00,0x0F00,0x1400,0x1800,0x1E00,0x2800,0x3C00,0x7800 34 | }; 35 | 36 | void DspSetEndOfSample(u32 channel); 37 | 38 | #define APU_MEM SPC_RAM 39 | 40 | DspChannel channels[8]; 41 | u8 DSP_MEM[0x100]; 42 | 43 | s8 firFilter[16]; 44 | s16 brrTab[16 * 16]; 45 | u32 echoBase; 46 | u32 echoDelay ALIGNED; 47 | u16 dspPreamp ALIGNED = 0x100; 48 | u16 echoRemain ALIGNED; 49 | 50 | 51 | // externs from dspmixer.S 52 | u32 DecodeSampleBlockAsm(u8 *blockPos, s16 *samplePos, DspChannel *channel); 53 | void DspMixSamplesStereoAsm(u32 samples, s16 *mixBuf); 54 | extern u16 firOffset; 55 | extern s16 noiseSample; 56 | extern u16 noiseStep; 57 | 58 | u32 playSamples; 59 | u32 writeSamples; 60 | 61 | 62 | u32 DecodeSampleBlock(DspChannel *channel, u32 channelNum) { 63 | u8 *cur = (u8*)&(APU_MEM[channel->blockPos]); 64 | s16 *sample = channel->decoded; 65 | 66 | if (channel->blockPos > 0x10000 - 9) { 67 | // Set end of block, with no loop 68 | DspSetEndOfSample(channelNum); 69 | return 1; 70 | } 71 | 72 | // Is this the last block? 73 | if (channel->brrHeader & 1) { 74 | // channelNum will be set from asm 75 | DSP_MEM[DSP_ENDX] |= (1 << channelNum); 76 | 77 | if (channel->brrHeader & 2) { 78 | // Looping 79 | u8 *sampleBase = APU_MEM + (DSP_MEM[DSP_DIR] << 8); 80 | u16 *lookupPointer = (u16*)(sampleBase + (DSP_MEM[(channelNum << 4) + DSP_SRC] << 2)); 81 | channel->blockPos = lookupPointer[1]; 82 | cur = (u8*)&(APU_MEM[channel->blockPos]); 83 | } else { 84 | DspSetEndOfSample(channelNum); 85 | return 1; 86 | } 87 | } 88 | 89 | channel->brrHeader = *cur; 90 | 91 | DecodeSampleBlockAsm(cur, sample, channel); 92 | 93 | channel->blockPos += 9; 94 | 95 | return 0; 96 | } 97 | 98 | 99 | void DspReset() { 100 | // Delay for 1 sample 101 | echoDelay = 4; 102 | echoRemain = 1; 103 | echoBase = 0; 104 | int i=0,c=0; 105 | 106 | memset(DSP_WriteDSPBuffer, 0, sizeof(DSP_WriteDSPBuffer)); 107 | memset(DSP_WriteTimeBuffer, 0, sizeof(DSP_WriteTimeBuffer)); 108 | 109 | DSP_WritingWriteDSPBuffer = DSP_WriteDSPBuffer[0]; 110 | DSP_PlayingWriteDSPBuffer = DSP_WriteDSPBuffer[1]; 111 | 112 | DSP_WritingWriteTimeBuffer = DSP_WriteTimeBuffer[0]; 113 | DSP_PlayingWriteTimeBuffer = DSP_WriteTimeBuffer[1]; 114 | 115 | firOffset = 0; 116 | for(i = 0; i > 8; i++) 117 | firFilter[i] = 0; 118 | 119 | 120 | memset(DSP_MEM, 0, 0x100); 121 | // Disable echo emulation 122 | DSP_MEM[DSP_FLAG] = 0x60; 123 | 124 | noiseSample = 0x8000; 125 | noiseStep = 1; 126 | 127 | for (i = 0; i < 8; i++) 128 | memset(&channels[i], 0, sizeof(DspChannel)); 129 | 130 | 131 | // Build a lookup table for the range values (thanks to trac) 132 | for (i = 0; i < 13; i++) { 133 | for (c = 0; c < 16; c++) 134 | brrTab[(i << 4) + c] = (s16)((((c ^ 8) - 8) << i) >> 1); 135 | } 136 | // range 13-15 137 | for (i = 13; i < 16; i++) { 138 | for (c = 0; c < 8; c++) 139 | brrTab[(i << 4) + c] = 0; 140 | for(c = 8; c < 16; c++) 141 | brrTab[(i << 4) + c] = 0xF800; 142 | } 143 | 144 | } 145 | 146 | void DspSetFIRCoefficient(u32 index) { 147 | firFilter[index] = DSP_MEM[(index << 4) + DSP_FIR]; 148 | } 149 | 150 | void DspSetChannelVolume(u32 channel) { 151 | channels[channel].leftVolume = DSP_MEM[(channel << 4) + DSP_VOL_L]; 152 | channels[channel].rightVolume = DSP_MEM[(channel << 4) + DSP_VOL_R]; 153 | 154 | channels[channel].leftCalcVolume = (channels[channel].leftVolume * channels[channel].envx) >> 7; 155 | channels[channel].rightCalcVolume = (channels[channel].rightVolume * channels[channel].envx) >> 7; 156 | } 157 | 158 | void DspSetChannelPitch(u32 channel) { 159 | u16 rawFreq = ((DSP_MEM[(channel << 4) + DSP_PITCH_H] << 8) + DSP_MEM[(channel << 4) + DSP_PITCH_L]) & 0x3fff; 160 | 161 | // Clear low bit of sample speed so we can do a little optimization in dsp mixing 162 | // channels[channel].sampleSpeed = (((rawFreq << 3) << 12) / MIXRATE) & (~1); 163 | //channels[channel].sampleSpeed = (((rawFreq << 3) << 12) / MIXRATE); 164 | channels[channel].sampleSpeed = rawFreq; 165 | } 166 | 167 | void DspSetChannelSource(u32 channel) { 168 | u8 *sampleBase = APU_MEM + (DSP_MEM[DSP_DIR] << 8); 169 | u16 *lookupPointer = (u16*)(sampleBase + (DSP_MEM[(channel << 4) + DSP_SRC] << 2)); 170 | 171 | channels[channel].blockPos = lookupPointer[0]; 172 | } 173 | 174 | void DspSetEndOfSample(u32 channel) { 175 | channels[channel].active = false; 176 | channels[channel].envState = ENVSTATE_NONE; 177 | 178 | channels[channel].envx = 0; 179 | DSP_MEM[(channel << 4) | DSP_ENVX] = 0; 180 | DSP_MEM[(channel << 4) | DSP_OUTX] = 0; 181 | } 182 | 183 | void DspSetChannelEnvelopeHeight(u32 channel, u8 height) { 184 | channels[channel].envx = height << 8; 185 | 186 | channels[channel].leftCalcVolume = (channels[channel].leftVolume * channels[channel].envx) >> 7; 187 | channels[channel].rightCalcVolume = (channels[channel].rightVolume * channels[channel].envx) >> 7; 188 | } 189 | 190 | void DspStartChannelEnvelope(u32 channel) { 191 | u8 adsr1 = DSP_MEM[(channel << 4) + DSP_ADSR1]; 192 | 193 | // ADSR mode, set envelope up 194 | // Attack rate goes into envelopeSpeed initially 195 | u8 adsr2 = DSP_MEM[(channel << 4) + DSP_ADSR2]; 196 | u8 decay = (adsr1 >> 4) & 0x7; 197 | u8 sustainLevel = adsr2 >> 5; 198 | u8 sustainRate = adsr2 & 0x1f; 199 | 200 | channels[channel].decaySpeed = ENVCNT[(decay << 1) + 0x10]; 201 | channels[channel].sustainLevel = 0x10 * (sustainLevel + 1); 202 | channels[channel].sustainSpeed = ENVCNT[sustainRate]; 203 | 204 | // Don't set envelope parameters when we are releasing the note 205 | if (adsr1 & 0x80) { 206 | u8 attack = adsr1 & 0xf; 207 | 208 | if (attack == 0xf) { 209 | // 0ms attack, go straight to full volume, and set decay 210 | DspSetChannelEnvelopeHeight(channel, 0x7f); 211 | channels[channel].envSpeed = channels[channel].decaySpeed; 212 | channels[channel].envState = ENVSTATE_DECAY; 213 | } else { 214 | DspSetChannelEnvelopeHeight(channel, 0); 215 | channels[channel].envSpeed = ENVCNT[(attack << 1) + 1]; 216 | channels[channel].envState = ENVSTATE_ATTACK; 217 | } 218 | } else { 219 | // Gain mode 220 | u8 gain = DSP_MEM[(channel << 4) + DSP_GAIN]; 221 | 222 | if ((gain & 0x80) == 0) { 223 | // Direct designation 224 | DspSetChannelEnvelopeHeight(channel, gain & 0x7f); 225 | channels[channel].envState = ENVSTATE_DIRECT; 226 | } else { 227 | DspSetChannelEnvelopeHeight(channel, 0); 228 | channels[channel].envSpeed = ENVCNT[gain & 0x1f]; 229 | 230 | switch ((gain >> 5) & 0x3) { 231 | case 0: 232 | // Linear decrease 233 | channels[channel].envState = ENVSTATE_DECREASE; 234 | break; 235 | case 1: 236 | // Exponential decrease 237 | channels[channel].envState = ENVSTATE_DECEXP; 238 | break; 239 | case 2: 240 | // Linear increase 241 | channels[channel].envState = ENVSTATE_INCREASE; 242 | break; 243 | case 3: 244 | // Bent line increase 245 | channels[channel].envState = ENVSTATE_BENTLINE; 246 | break; 247 | } 248 | } 249 | } 250 | } 251 | 252 | void DspChangeChannelEnvelopeGain(u32 channel) { 253 | // Don't set envelope parameters when we are releasing the note 254 | if (!channels[channel].active) return; 255 | if (channels[channel].envState == ENVSTATE_RELEASE) return; 256 | 257 | // If in ADSR mode, write to GAIN register has no effect 258 | if (DSP_MEM[(channel << 4) + DSP_ADSR1] & 0x80) return; 259 | 260 | // Otherwise treat it as GAIN change 261 | u8 gain = DSP_MEM[(channel << 4) + DSP_GAIN]; 262 | 263 | if ((gain & 0x80) == 0) { 264 | // Direct designation 265 | DspSetChannelEnvelopeHeight(channel, gain & 0x7f); 266 | channels[channel].envState = ENVSTATE_DIRECT; 267 | channels[channel].envSpeed = 0; 268 | } else { 269 | channels[channel].envSpeed = ENVCNT[gain & 0x1f]; 270 | 271 | switch ((gain >> 5) & 0x3) { 272 | case 0: 273 | // Linear decrease 274 | channels[channel].envState = ENVSTATE_DECREASE; 275 | break; 276 | case 1: 277 | // Exponential decrease 278 | channels[channel].envState = ENVSTATE_DECEXP; 279 | break; 280 | case 2: 281 | // Linear increase 282 | channels[channel].envState = ENVSTATE_INCREASE; 283 | break; 284 | case 3: 285 | // Bent line increase 286 | channels[channel].envState = ENVSTATE_BENTLINE; 287 | break; 288 | } 289 | } 290 | } 291 | 292 | void DspChangeChannelEnvelopeAdsr1(u32 channel, u8 orig) { 293 | // Don't set envelope parameters when we are releasing the note 294 | if (!channels[channel].active) return; 295 | if (channels[channel].envState == ENVSTATE_RELEASE) return; 296 | 297 | u8 adsr1 = DSP_MEM[(channel << 4) + DSP_ADSR1]; 298 | 299 | u8 decay = (adsr1 >> 4) & 0x7; 300 | channels[channel].decaySpeed = ENVCNT[(decay << 1) + 0x10]; 301 | 302 | if (channels[channel].envState == ENVSTATE_ATTACK) { 303 | u8 attack = adsr1 & 0xf; 304 | channels[channel].envSpeed = ENVCNT[(attack << 1) + 1]; 305 | } else if (channels[channel].envState == ENVSTATE_DECAY) { 306 | channels[channel].envSpeed = channels[channel].decaySpeed; 307 | } 308 | 309 | if (adsr1 & 0x80) { 310 | if (!(orig & 0x80)) { 311 | // Switch to ADSR 312 | u8 attack = adsr1 & 0xf; 313 | channels[channel].envState = ENVSTATE_ATTACK; 314 | channels[channel].envSpeed = ENVCNT[(attack << 1) + 1]; 315 | } 316 | } else { 317 | // Switch to gain mode 318 | DspChangeChannelEnvelopeGain(channel); 319 | } 320 | } 321 | 322 | void DspChangeChannelEnvelopeAdsr2(u32 channel) { 323 | // Don't set envelope parameters when we are releasing the note 324 | if (!channels[channel].active) return; 325 | if (channels[channel].envState == ENVSTATE_RELEASE) return; 326 | 327 | u8 adsr2 = DSP_MEM[(channel << 4) + DSP_ADSR2]; 328 | u8 sustainRate = adsr2 & 0x1f; 329 | channels[channel].sustainSpeed = ENVCNT[sustainRate]; 330 | 331 | if (channels[channel].envState == ENVSTATE_SUSTAIN) { 332 | channels[channel].envSpeed = channels[channel].sustainSpeed; 333 | } 334 | } 335 | 336 | void DspKeyOnChannel(u32 i) { 337 | channels[i].envState = ENVSTATE_NONE; 338 | 339 | DspSetChannelEnvelopeHeight(i, 0); 340 | DSP_MEM[(i << 4) | DSP_ENVX] = 0; 341 | DSP_MEM[(i << 4) | DSP_OUTX] = 0; 342 | 343 | DspSetChannelVolume(i); 344 | DspSetChannelPitch(i); 345 | DspSetChannelSource(i); 346 | DspStartChannelEnvelope(i); 347 | 348 | channels[i].samplePos = 16 << 12; 349 | 350 | channels[i].brrHeader = 0; 351 | channels[i].prevSamp1 = 0; 352 | channels[i].prevSamp2 = 0; 353 | 354 | channels[i].decoded[13] = 0; 355 | channels[i].decoded[14] = 0; 356 | channels[i].decoded[15] = 0; 357 | 358 | channels[i].envCount = ENVCNT_START; 359 | channels[i].active = true; 360 | 361 | /*bprintf("%d -> %04X %04X\n", 362 | i, 363 | channels[i].blockPos, channels[i].sampleSpeed);*/ 364 | 365 | DSP_MEM[DSP_ENDX] &= ~(1 << i); 366 | } 367 | 368 | void DspPrepareStateAfterReload() { 369 | // Set up echo delay 370 | DspWriteByte(DSP_MEM[DSP_EDL], DSP_EDL); 371 | 372 | echoBase = ((u32)DSP_MEM[DSP_ESA] << 8); 373 | //memset(&APU_MEM[echoBase], 0, echoDelay); 374 | 375 | u32 i=0; 376 | for (i = 0; i < 8; i++) { 377 | channels[i].echoEnabled = (DSP_MEM[DSP_EON] >> i) & 1; 378 | 379 | if (DSP_MEM[DSP_KON] & (1 << i)) { 380 | DspKeyOnChannel(i); 381 | } 382 | } 383 | } 384 | 385 | extern Handle SPCSync; 386 | void DspReplayWriteByte(u8 val, u8 address); 387 | 388 | void DSP_BufferSwap() 389 | { 390 | 391 | u16* tmp = DSP_WritingWriteDSPBuffer; 392 | DSP_WritingWriteDSPBuffer = DSP_PlayingWriteDSPBuffer; 393 | DSP_PlayingWriteDSPBuffer = tmp; 394 | 395 | tmp = DSP_WritingWriteTimeBuffer; 396 | DSP_WritingWriteTimeBuffer = DSP_PlayingWriteTimeBuffer; 397 | DSP_PlayingWriteTimeBuffer = tmp; 398 | 399 | DSP_WritingWriteDSPBuffer[8191] = 0; 400 | 401 | svcSignalEvent(SPCSync); 402 | } 403 | 404 | void DspWriteByte(u8 val, u8 address) 405 | { 406 | if (address > 0x7f) return; 407 | 408 | u16* dsp = DSP_WritingWriteDSPBuffer; 409 | 410 | if(dsp[8191] >= 8191) 411 | { 412 | bprintf("!! DSP WRITEBUFFER OVERFLOW\n"); 413 | return; 414 | } 415 | 416 | DSP_WritingWriteTimeBuffer[dsp[8191]] = SPC_ElapsedCycles; 417 | dsp[dsp[8191]] = (address << 8) | val; 418 | dsp[8191]++; 419 | } 420 | 421 | u32 DspMixSamplesStereo(u32 samples, s16 *mixBuf, u32 length, u32 curpos, bool restart) 422 | { 423 | if(!samples) 424 | return curpos; 425 | 426 | u32 newlen = length << 1; 427 | u32 newpos = (curpos << 1) % newlen; 428 | 429 | if(restart) 430 | { 431 | playSamples = samples; 432 | writeSamples = 512; 433 | } 434 | else 435 | { 436 | playSamples += samples; 437 | writeSamples += 512; 438 | } 439 | 440 | if(playSamples >= 0x10000 && writeSamples >= 0x10000) 441 | { 442 | playSamples -= 0x10000; 443 | writeSamples -= 0x10000; 444 | } 445 | 446 | u32 procSamples = 512; 447 | if(playSamples > writeSamples) { 448 | procSamples += (playSamples - writeSamples); 449 | writeSamples = playSamples; 450 | } 451 | 452 | s16 *mix = &mixBuf[newpos]; 453 | s16 *mixend = &mixBuf[newlen]; 454 | u16 *dsp = DSP_PlayingWriteDSPBuffer; 455 | u16 *dspend = &dsp[dsp[8191]]; 456 | u16 *time = DSP_PlayingWriteTimeBuffer; 457 | 458 | u32 totCycle = procSamples * 32; 459 | u32 curCycle = 0; 460 | bool loop; 461 | 462 | do 463 | { 464 | loop = false; 465 | 466 | // We work with one sample each time. We could try and group up a bunch of samples together, but then we'd need to deal with ring buffer wrap-around 467 | if(curCycle < totCycle) 468 | { 469 | loop = true; 470 | DspMixSamplesStereoAsm(1, mix); 471 | mix += 2; 472 | if(mix == mixend) 473 | mix = mixBuf; 474 | curCycle += 32; 475 | } 476 | 477 | // Only process DSP writes if we have any left and either if they are within the correct cycle period or all samples are accounted for 478 | while((dsp < dspend) && ((*time < curCycle) || (curCycle == totCycle))) 479 | { 480 | loop = true; 481 | u16 val = *dsp; 482 | DspReplayWriteByte(val & 0xFF, val >> 8); 483 | dsp++; 484 | time++; 485 | } 486 | 487 | } while(loop); 488 | 489 | return ((curpos + procSamples) % length); 490 | } 491 | 492 | void DspReplayWriteByte(u8 val, u8 address) 493 | { 494 | u8 orig = DSP_MEM[address]; 495 | DSP_MEM[address] = val; 496 | 497 | //if (address > 0x7f) return; 498 | 499 | switch (address & 0xf) { 500 | case DSP_VOL_L: 501 | DspSetChannelVolume(address >> 4); 502 | break; 503 | case DSP_VOL_R: 504 | DspSetChannelVolume(address >> 4); 505 | break; 506 | case DSP_PITCH_L: 507 | DspSetChannelPitch(address >> 4); 508 | break; 509 | case DSP_PITCH_H: 510 | DspSetChannelPitch(address >> 4); 511 | break; 512 | case DSP_ADSR1: 513 | DspChangeChannelEnvelopeAdsr1(address >> 4, orig); 514 | break; 515 | case DSP_ADSR2: 516 | DspChangeChannelEnvelopeAdsr2(address >> 4); 517 | break; 518 | case DSP_GAIN: 519 | DspChangeChannelEnvelopeGain(address >> 4); 520 | break; 521 | 522 | case DSP_FIR: 523 | DspSetFIRCoefficient(address >> 4); 524 | break; 525 | 526 | case 0xC: 527 | switch (address >> 4) { 528 | case (DSP_KON >> 4): 529 | // val &= ~DSP_MEM[DSP_KOF]; 530 | // DSP_MEM[DSP_KON] = val & DSP_MEM[DSP_KOF]; 531 | 532 | if (val) { 533 | DSP_MEM[DSP_KON] = val & DSP_MEM[DSP_KOF]; 534 | val &= ~DSP_MEM[DSP_KOF]; 535 | u32 i=0; 536 | for (; i<8; i++) 537 | if ((val>>i)&1) { 538 | DspKeyOnChannel(i); 539 | } 540 | } 541 | break; 542 | 543 | case (DSP_KOF >> 4):{ 544 | int i=0; 545 | for (; i<8; i++) 546 | if (((val>>i)&1) && channels[i].active && channels[i].envState != ENVSTATE_RELEASE) { 547 | // Set current state to release (ENDX will be set when release hits 0) 548 | channels[i].envState = ENVSTATE_RELEASE; 549 | channels[i].envCount = ENVCNT_START; 550 | channels[i].envSpeed = ENVCNT[0x1C]; 551 | }} 552 | break; 553 | case (DSP_FLAG >> 4):{ 554 | if(val & 0x80) 555 | { 556 | DSP_MEM[DSP_FLAG] = 0x60; 557 | noiseSample = 0x8000; 558 | noiseStep = 1; 559 | } 560 | else 561 | DSP_MEM[DSP_FLAG] = val; 562 | } 563 | break; 564 | 565 | case (DSP_ENDX >> 4): 566 | DSP_MEM[DSP_ENDX] = 0; 567 | break; 568 | } 569 | break; 570 | 571 | case 0xD: 572 | switch (address >> 4) { 573 | 574 | case (DSP_EDL >> 4): 575 | val &= 0xf; 576 | if (val == 0) { 577 | echoDelay = 4; 578 | } else { 579 | echoDelay = ((u32)(val << 4) * 32) << 2; 580 | } 581 | break; 582 | 583 | case (DSP_PMOD >> 4): 584 | { 585 | int i=1; 586 | for (; i<8; i++) 587 | channels[i].pmodEnabled = (val >> i) & 0x1; 588 | } 589 | break; 590 | 591 | case (DSP_NON >> 4): 592 | { 593 | int i=0; 594 | for (; i<8; i++) 595 | channels[i].noiseEnabled = (val >> i) & 0x1; 596 | } 597 | break; 598 | 599 | case (DSP_ESA >> 4): 600 | echoBase = (u32)val << 8; 601 | break; 602 | 603 | case (DSP_EON >> 4):{ 604 | int i=0; 605 | for (; i < 8; i++) { 606 | channels[i].echoEnabled = (val >> i) & 1; 607 | }} 608 | break; 609 | } 610 | } 611 | } 612 | -------------------------------------------------------------------------------- /source/dsp.h: -------------------------------------------------------------------------------- 1 | // This code has been taken from SNemulDS which is licensed under GPLv2. 2 | // Credits go to Archeide and whoever else participated in this. 3 | 4 | #include <3ds.h> 5 | 6 | #include "spc700.h" 7 | #include "mixrate.h" 8 | 9 | #define ALIGNED __attribute__ ((aligned(4))) 10 | 11 | void DspReset(); 12 | void DspPrepareStateAfterReload(); 13 | 14 | extern u8 DSP_MEM[0x100]; 15 | extern u16 dspPreamp; 16 | 17 | extern u32 echoBase; 18 | extern u16 echoRemain; 19 | extern s8 firFilter[16]; 20 | 21 | u32 DspMixSamplesStereo(u32 samples, s16 *mixBuf, u32 length, u32 curpos, bool restart); 22 | void DspWriteByte(u8 val, u8 address); 23 | 24 | void DSP_BufferSwap(); 25 | 26 | struct _DspChannel { 27 | int sampleSpeed; 28 | int samplePos; 29 | int envCount; 30 | int envSpeed; 31 | s16 prevDecode[4]; 32 | s16 decoded[16]; 33 | u16 decaySpeed; 34 | u16 sustainSpeed; 35 | s16 envx; 36 | s16 prevSamp1, prevSamp2; 37 | u16 blockPos; 38 | s16 leftCalcVolume; 39 | s16 rightCalcVolume; 40 | u8 envState; 41 | u8 sustainLevel; 42 | s8 leftVolume; 43 | s8 rightVolume; 44 | s8 keyWait; 45 | bool active; 46 | u8 brrHeader; 47 | bool echoEnabled; 48 | bool noiseEnabled; 49 | bool pmodEnabled; 50 | u8 empty1; 51 | u8 empty2; 52 | } ALIGNED; 53 | typedef struct _DspChannel DspChannel; 54 | 55 | extern DspChannel channels[8]; 56 | 57 | // DSP Register defintions 58 | 59 | // Channel specific 60 | #define DSP_VOL_L 0x00 61 | #define DSP_VOL_R 0x01 62 | #define DSP_PITCH_L 0x02 63 | #define DSP_PITCH_H 0x03 64 | #define DSP_SRC 0x04 65 | #define DSP_ADSR1 0x05 66 | #define DSP_ADSR2 0x06 67 | #define DSP_GAIN 0x07 68 | #define DSP_ENVX 0x08 69 | #define DSP_OUTX 0x09 70 | 71 | // Global 72 | #define DSP_MAINVOL_L 0x0C 73 | #define DSP_MAINVOL_R 0x1C 74 | #define DSP_ECHOVOL_L 0x2C 75 | #define DSP_ECHOVOL_R 0x3C 76 | #define DSP_KON 0x4C 77 | #define DSP_KOF 0x5C 78 | #define DSP_FLAG 0x6C 79 | #define DSP_ENDX 0x7C 80 | 81 | #define DSP_EFB 0x0D 82 | #define DSP_PMOD 0x2D 83 | #define DSP_NON 0x3D 84 | #define DSP_EON 0x4D 85 | #define DSP_DIR 0x5D 86 | #define DSP_ESA 0x6D 87 | #define DSP_EDL 0x7D 88 | 89 | #define DSP_FIR 0x0F 90 | 91 | // Envelope state definitions 92 | #define ENVSTATE_NONE 0 93 | #define ENVSTATE_ATTACK 1 94 | #define ENVSTATE_DECAY 2 95 | #define ENVSTATE_SUSTAIN 3 96 | #define ENVSTATE_RELEASE 4 97 | #define ENVSTATE_DIRECT 5 98 | #define ENVSTATE_INCREASE 6 99 | #define ENVSTATE_BENTLINE 7 100 | #define ENVSTATE_DECREASE 8 101 | #define ENVSTATE_DECEXP 9 102 | -------------------------------------------------------------------------------- /source/final.g.pica: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Copyright 2014-2022 Arisotura 3 | ; 4 | ; This file is part of blargSnes. 5 | ; 6 | ; blargSnes is free software: you can redistribute it and/or modify it under 7 | ; the terms of the GNU General Public License as published by the Free 8 | ; Software Foundation, either version 3 of the License, or (at your option) 9 | ; any later version. 10 | ; 11 | ; blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | ; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | ; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | ; 15 | ; You should have received a copy of the GNU General Public License along 16 | ; with blargSnes. If not, see http:;www.gnu.org/licenses/. 17 | ; ----------------------------------------------------------------------------- 18 | 19 | .gsh point c0 20 | 21 | ; setup constants 22 | .constf myconst(-0.345, 0.0, 0.0, 1.0) 23 | 24 | ; setup outmap 25 | .out o0 position 26 | .out o1 color 27 | .out o2 texcoord0 28 | 29 | 30 | .entry gmain 31 | .proc gmain 32 | ; turn two vertices into a rectangle 33 | ; setemit: vtxid, primemit, winding 34 | 35 | ; v0 = vertex 0, position 36 | ; v1 = vertex 0, texcoord 37 | ; v2 = vertex 1, position 38 | ; v3 = vertex 1, texcoord 39 | 40 | ; x1 y1 41 | setemit 0 42 | mov o0, v0 43 | mov o1, myconst.wwww 44 | mov o2, v1 45 | emit 46 | 47 | ; x2 y1 48 | setemit 1 49 | mov r0, v2 50 | mov r0.y, v0.y 51 | mov o0, r0 52 | mov o1, myconst.wwww 53 | mov r2, v1 54 | mov r2.y, v3.y 55 | mov o2, r2 56 | emit 57 | 58 | ; x1 y2 59 | setemit 2, prim 60 | mov r0, v0 61 | mov r0.y, v2.y 62 | mov o0, r0 63 | mov o1, myconst.wwww 64 | mov r2, v3 65 | mov r2.y, v1.y 66 | mov o2, r2 67 | emit 68 | 69 | ; x2 y2 70 | setemit 0, prim inv 71 | mov o0, v2 72 | mov o1, myconst.wwww 73 | mov o2, v3 74 | emit 75 | 76 | end 77 | nop 78 | .end 79 | -------------------------------------------------------------------------------- /source/final.v.pica: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Copyright 2014-2022 Arisotura 3 | ; 4 | ; This file is part of blargSnes. 5 | ; 6 | ; blargSnes is free software: you can redistribute it and/or modify it under 7 | ; the terms of the GNU General Public License as published by the Free 8 | ; Software Foundation, either version 3 of the License, or (at your option) 9 | ; any later version. 10 | ; 11 | ; blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | ; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | ; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | ; 15 | ; You should have received a copy of the GNU General Public License along 16 | ; with blargSnes. If not, see http:;www.gnu.org/licenses/. 17 | ; ----------------------------------------------------------------------------- 18 | 19 | ; setup constants 20 | .constf const1(0.008333333, 0.005, 0.0, 1.0) 21 | ;.constf const1(0.008333333, 0.005, -0.99610138, 1.0) ; FF0080 22 | ;.constf const1(0.008333333, 0.005, -0.00389862, 1.0) ; 00FF7F 23 | ;.constf const1(0.008333333, 0.005, -0.99999237, 1.0) ; FFFF80 24 | .constf const2(-1.0, 0.0, 0.0, 0.0) 25 | 26 | ; setup outmap 27 | .out o0 position 28 | .out o1 texcoord0 29 | 30 | .entry vmain 31 | .proc vmain 32 | ; perform simple projection 33 | mov r0, const1 34 | mul r0.xy, r0.xy, v0.xy 35 | add r0.xy, const2.xx, r0.xy 36 | mov o0, r0 37 | 38 | ; result.texcoord = in.texcoord 39 | mov o1, v1 40 | end 41 | nop 42 | .end 43 | -------------------------------------------------------------------------------- /source/font.h: -------------------------------------------------------------------------------- 1 | #ifndef FONT_H 2 | #define FONT_H 3 | unsigned short font[] = { 4 | 12, 0, 0, 0,0x0C03, 0x0E07, 0x070E, 0x039C, 0x01F8, 0x00F0, 0x00F0, 0x01F8, 0x039C, 0x070E, 0x0E07, 0x0C03, 5 | 12, 0, 0, 0,0x01C0, 0x00E0, 0x0060, 0x0860, 0x0C60, 0x0FE0, 0x07F0, 0x0038, 0x001C, 0x000E, 0x0007, 0x0003, 6 | 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 7 | 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 8 | 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 9 | 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 10 | 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 11 | 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 12 | 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 13 | 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 14 | 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 15 | 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 16 | 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 17 | 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 18 | 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 19 | 12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 20 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21 | 2, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000, 0x0003, 0x0003, 0x0000, 0x0000, 22 | 9, 0, 0, 0,0x01EF, 0x01EF, 0x018C, 0x01CE, 0x00E7, 0x0063, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 23 | 10, 0, 0, 0,0x00CC, 0x00CC, 0x03FF, 0x03FF, 0x00CC, 0x00CC, 0x03FF, 0x03FF, 0x00CC, 0x00CC, 0x0000, 0x0000, 24 | 8, 0, 0, 0,0x0018, 0x00FE, 0x00FF, 0x001B, 0x007F, 0x00FE, 0x00D8, 0x00FF, 0x007F, 0x0018, 0x0000, 0x0000, 25 | 10, 0, 0, 0,0x0306, 0x038F, 0x01CF, 0x00E6, 0x0070, 0x0038, 0x019C, 0x03CE, 0x03C7, 0x0183, 0x0000, 0x0000, 26 | 10, 0, 0, 0,0x007C, 0x00FE, 0x00C6, 0x00EE, 0x007C, 0x037E, 0x03E7, 0x01F3, 0x03BF, 0x031E, 0x0000, 0x0000, 27 | 4, 0, 0, 0,0x000F, 0x000F, 0x000C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 28 | 4, 0, 0, 0,0x000C, 0x000E, 0x0007, 0x0003, 0x0003, 0x0003, 0x0003, 0x0007, 0x000E, 0x000C, 0x0000, 0x0000, 29 | 4, 0, 0, 0,0x0003, 0x0007, 0x000E, 0x000C, 0x000C, 0x000C, 0x000C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000, 30 | 10, 0, 0, 0,0x0030, 0x0333, 0x03B7, 0x01FE, 0x00FC, 0x00FC, 0x01FE, 0x03B7, 0x0333, 0x0030, 0x0000, 0x0000, 31 | 10, 0, 0, 0,0x0030, 0x0030, 0x0030, 0x0030, 0x03FF, 0x03FF, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0000, 32 | 4, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x000F, 0x000F, 0x000C, 0x000E, 0x0007, 0x0003, 33 | 10, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x03FF, 0x03FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 34 | 3, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0007, 0x0007, 0x0007, 0x0000, 0x0000, 35 | 10, 0, 0, 0,0x0300, 0x0380, 0x01C0, 0x00E0, 0x0070, 0x0038, 0x001C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000, 36 | 8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00C3, 0x00C3, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000, 37 | 4, 0, 0, 0,0x0006, 0x0007, 0x0007, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x000F, 0x000F, 0x0000, 0x0000, 38 | 8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C0, 0x00FE, 0x007F, 0x0003, 0x0003, 0x00FF, 0x00FF, 0x0000, 0x0000, 39 | 8, 0, 0, 0,0x007F, 0x00FF, 0x00C0, 0x00C0, 0x007C, 0x00FC, 0x00C0, 0x00C0, 0x00FF, 0x007F, 0x0000, 0x0000, 40 | 8, 0, 0, 0,0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x00FF, 0x00FE, 0x0060, 0x0060, 0x0000, 0x0000, 41 | 8, 0, 0, 0,0x00FF, 0x00FF, 0x0003, 0x0003, 0x007F, 0x00FF, 0x00C0, 0x00C0, 0x00FF, 0x007F, 0x0000, 0x0000, 42 | 8, 0, 0, 0,0x007E, 0x007F, 0x0003, 0x0003, 0x007F, 0x00FF, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000, 43 | 8, 0, 0, 0,0x00FF, 0x00FF, 0x00C0, 0x00E0, 0x0070, 0x0038, 0x001C, 0x000C, 0x000C, 0x000C, 0x0000, 0x0000, 44 | 8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C3, 0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000, 45 | 8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00FF, 0x00FE, 0x00C0, 0x00C0, 0x00FE, 0x007E, 0x0000, 0x0000, 46 | 3, 0, 0, 0,0x0000, 0x0000, 0x0007, 0x0007, 0x0000, 0x0000, 0x0000, 0x0007, 0x0007, 0x0000, 0x0000, 0x0000, 47 | 4, 0, 0, 0,0x0000, 0x0000, 0x000E, 0x000E, 0x0000, 0x0000, 0x000C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000, 48 | 6, 0, 0, 0,0x0030, 0x0038, 0x001C, 0x000E, 0x0007, 0x0007, 0x000E, 0x001C, 0x0038, 0x0030, 0x0000, 0x0000, 49 | 7, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x007F, 0x007F, 0x0000, 0x0000, 0x007F, 0x007F, 0x0000, 0x0000, 0x0000, 50 | 6, 0, 0, 0,0x0003, 0x0007, 0x000E, 0x001C, 0x0038, 0x0038, 0x001C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000, 51 | 8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00F0, 0x0078, 0x0018, 0x0000, 0x0018, 0x0018, 0x0000, 0x0000, 52 | 10, 0, 0, 0,0x00FC, 0x01FE, 0x0387, 0x0333, 0x037B, 0x03FB, 0x01F3, 0x0007, 0x03FE, 0x03FC, 0x0000, 0x0000, 53 | 9, 0, 0, 0,0x00FE, 0x01FF, 0x0183, 0x0183, 0x0183, 0x01FF, 0x01FF, 0x0183, 0x0183, 0x0183, 0x0000, 0x0000, 54 | 9, 0, 0, 0,0x00FF, 0x01FF, 0x0183, 0x0183, 0x00FF, 0x01FF, 0x0183, 0x0183, 0x01FF, 0x00FF, 0x0000, 0x0000, 55 | 8, 0, 0, 0,0x00FE, 0x00FF, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x00FF, 0x00FE, 0x0000, 0x0000, 56 | 9, 0, 0, 0,0x007F, 0x00FF, 0x01C3, 0x0183, 0x0183, 0x0183, 0x0183, 0x01C3, 0x00FF, 0x007F, 0x0000, 0x0000, 57 | 9, 0, 0, 0,0x01FF, 0x01FF, 0x0003, 0x0003, 0x00FF, 0x00FF, 0x0003, 0x0003, 0x01FF, 0x01FF, 0x0000, 0x0000, 58 | 9, 0, 0, 0,0x01FF, 0x01FF, 0x0003, 0x0003, 0x00FF, 0x00FF, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000, 59 | 9, 0, 0, 0,0x01FE, 0x01FF, 0x0003, 0x0003, 0x01F3, 0x01F3, 0x0183, 0x0183, 0x01FF, 0x00FE, 0x0000, 0x0000, 60 | 9, 0, 0, 0,0x0183, 0x0183, 0x0183, 0x0183, 0x01FF, 0x01FF, 0x0183, 0x0183, 0x0183, 0x0183, 0x0000, 0x0000, 61 | 6, 0, 0, 0,0x003F, 0x003F, 0x000C, 0x000C, 0x000C, 0x000C, 0x000C, 0x000C, 0x003F, 0x003F, 0x0000, 0x0000, 62 | 9, 0, 0, 0,0x01F0, 0x01F0, 0x00C0, 0x00C0, 0x00C0, 0x00C0, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000, 63 | 9, 0, 0, 0,0x0183, 0x01C3, 0x00E3, 0x0073, 0x003F, 0x003F, 0x0073, 0x00E3, 0x01C3, 0x0183, 0x0000, 0x0000, 64 | 7, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x007F, 0x007F, 0x0000, 0x0000, 65 | 10, 0, 0, 0,0x0303, 0x0387, 0x03CF, 0x03FF, 0x037B, 0x0333, 0x0303, 0x0303, 0x0303, 0x0303, 0x0000, 0x0000, 66 | 10, 0, 0, 0,0x0303, 0x0307, 0x030F, 0x031F, 0x033B, 0x0373, 0x03E3, 0x03C3, 0x0383, 0x0303, 0x0000, 0x0000, 67 | 10, 0, 0, 0,0x01FE, 0x03FF, 0x0303, 0x0303, 0x0303, 0x0303, 0x0303, 0x0303, 0x03FF, 0x01FE, 0x0000, 0x0000, 68 | 9, 0, 0, 0,0x00FF, 0x01FF, 0x0183, 0x0183, 0x01FF, 0x00FF, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000, 69 | 10, 0, 0, 0,0x01FE, 0x03FF, 0x0303, 0x0303, 0x0333, 0x0373, 0x03E3, 0x01C3, 0x03FF, 0x037E, 0x0000, 0x0000, 70 | 9, 0, 0, 0,0x00FF, 0x01FF, 0x0183, 0x0183, 0x01FF, 0x00FF, 0x0073, 0x00E3, 0x01C3, 0x0183, 0x0000, 0x0000, 71 | 10, 0, 0, 0,0x01FE, 0x01FF, 0x0003, 0x0003, 0x01FF, 0x03FE, 0x0300, 0x0300, 0x03FE, 0x01FE, 0x0000, 0x0000, 72 | 10, 0, 0, 0,0x03FF, 0x03FF, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0000, 73 | 9, 0, 0, 0,0x0183, 0x0183, 0x0183, 0x0183, 0x0183, 0x0183, 0x0183, 0x0183, 0x01FF, 0x00FE, 0x0000, 0x0000, 74 | 10, 0, 0, 0,0x0303, 0x0303, 0x0303, 0x0303, 0x0303, 0x0387, 0x01CE, 0x00FC, 0x0078, 0x0030, 0x0000, 0x0000, 75 | 10, 0, 0, 0,0x0303, 0x0303, 0x0303, 0x0303, 0x0333, 0x037B, 0x03FF, 0x03CF, 0x0387, 0x0303, 0x0000, 0x0000, 76 | 10, 0, 0, 0,0x0303, 0x0387, 0x01CE, 0x00FC, 0x0078, 0x0078, 0x00FC, 0x01CE, 0x0387, 0x0303, 0x0000, 0x0000, 77 | 10, 0, 0, 0,0x0303, 0x0387, 0x01CE, 0x00FC, 0x0078, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0000, 78 | 10, 0, 0, 0,0x03FF, 0x03FF, 0x01C0, 0x00E0, 0x0070, 0x0038, 0x001C, 0x000E, 0x03FF, 0x03FF, 0x0000, 0x0000, 79 | 4, 0, 0, 0,0x000F, 0x000F, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x000F, 0x000F, 0x0000, 0x0000, 80 | 10, 0, 0, 0,0x0003, 0x0007, 0x000E, 0x001C, 0x0038, 0x0070, 0x00E0, 0x01C0, 0x0380, 0x0300, 0x0000, 0x0000, 81 | 4, 0, 0, 0,0x000F, 0x000F, 0x000C, 0x000C, 0x000C, 0x000C, 0x000C, 0x000C, 0x000F, 0x000F, 0x0000, 0x0000, 82 | 8, 0, 0, 0,0x0018, 0x003C, 0x007E, 0x00E7, 0x00C3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 83 | 10, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03FF, 0x03FF, 84 | 4, 0, 0, 0,0x000F, 0x000F, 0x0003, 0x0007, 0x000E, 0x000C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 85 | 7, 0, 0, 0,0x0000, 0x0000, 0x003E, 0x007E, 0x0060, 0x007E, 0x007F, 0x0063, 0x007F, 0x007E, 0x0000, 0x0000, 86 | 7, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x003F, 0x007F, 0x0063, 0x0063, 0x0063, 0x007F, 0x003F, 0x0000, 0x0000, 87 | 7, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x007F, 0x0003, 0x0003, 0x0003, 0x0003, 0x007F, 0x007E, 0x0000, 0x0000, 88 | 7, 0, 0, 0,0x0060, 0x0060, 0x0060, 0x007E, 0x007F, 0x0063, 0x0063, 0x0063, 0x007F, 0x007E, 0x0000, 0x0000, 89 | 7, 0, 0, 0,0x0000, 0x0000, 0x003E, 0x007F, 0x0063, 0x007F, 0x003F, 0x0003, 0x003F, 0x003E, 0x0000, 0x0000, 90 | 6, 0, 0, 0,0x003C, 0x003E, 0x0006, 0x0006, 0x001F, 0x001F, 0x0006, 0x0006, 0x0006, 0x0006, 0x0000, 0x0000, 91 | 7, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x007F, 0x0063, 0x0063, 0x007F, 0x007E, 0x0060, 0x0060, 0x007E, 0x003E, 92 | 7, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x003F, 0x007F, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0000, 0x0000, 93 | 2, 0, 0, 0,0x0003, 0x0003, 0x0000, 0x0000, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000, 94 | 7, 0, 0, 0,0x0060, 0x0060, 0x0000, 0x0000, 0x0060, 0x0060, 0x0060, 0x0060, 0x0060, 0x0063, 0x007F, 0x003E, 95 | 8, 0, 0, 0,0x0003, 0x0003, 0x00E3, 0x0073, 0x003B, 0x001F, 0x001F, 0x003B, 0x0073, 0x00E3, 0x0000, 0x0000, 96 | 4, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x000F, 0x000E, 0x0000, 0x0000, 97 | 10, 0, 0, 0,0x0000, 0x0000, 0x01FF, 0x03FF, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0000, 0x0000, 98 | 7, 0, 0, 0,0x0000, 0x0000, 0x003F, 0x007F, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0000, 0x0000, 99 | 8, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000, 100 | 7, 0, 0, 0,0x0000, 0x0000, 0x003F, 0x007F, 0x0063, 0x0063, 0x007F, 0x003F, 0x0003, 0x0003, 0x0003, 0x0003, 101 | 7, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x007F, 0x0063, 0x0063, 0x007F, 0x007E, 0x0060, 0x0060, 0x0060, 0x0060, 102 | 7, 0, 0, 0,0x0000, 0x0000, 0x003B, 0x007F, 0x0067, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000, 103 | 8, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x007F, 0x0003, 0x007F, 0x00FE, 0x00C0, 0x00FE, 0x007E, 0x0000, 0x0000, 104 | 6, 0, 0, 0,0x0006, 0x0006, 0x003F, 0x003F, 0x0006, 0x0006, 0x0006, 0x0006, 0x003E, 0x003C, 0x0000, 0x0000, 105 | 7, 0, 0, 0,0x0000, 0x0000, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x007F, 0x007E, 0x0000, 0x0000, 106 | 10, 0, 0, 0,0x0000, 0x0000, 0x0303, 0x0303, 0x0303, 0x0387, 0x01CE, 0x00FC, 0x0078, 0x0030, 0x0000, 0x0000, 107 | 10, 0, 0, 0,0x0000, 0x0000, 0x0303, 0x0303, 0x0333, 0x037B, 0x03FF, 0x03CF, 0x0387, 0x0303, 0x0000, 0x0000, 108 | 8, 0, 0, 0,0x0000, 0x0000, 0x00C3, 0x00E7, 0x007E, 0x003C, 0x003C, 0x007E, 0x00E7, 0x00C3, 0x0000, 0x0000, 109 | 10, 0, 0, 0,0x0000, 0x0000, 0x0303, 0x0307, 0x038E, 0x01DC, 0x00F8, 0x0070, 0x0038, 0x001C, 0x000E, 0x0006, 110 | 8, 0, 0, 0,0x0000, 0x0000, 0x00FF, 0x00FF, 0x0070, 0x0038, 0x001C, 0x000E, 0x00FF, 0x00FF, 0x0000, 0x0000, 111 | 6, 0, 0, 0,0x0038, 0x003C, 0x000C, 0x000C, 0x000F, 0x000F, 0x000C, 0x000C, 0x003C, 0x0038, 0x0000, 0x0000, 112 | 2, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000, 113 | 6, 0, 0, 0,0x0007, 0x000F, 0x000C, 0x000C, 0x003C, 0x003C, 0x000C, 0x000C, 0x000F, 0x0007, 0x0000, 0x0000, 114 | 10, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x031C, 0x03BE, 0x01F7, 0x00E3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 115 | 11, 0, 0, 0,0x0555, 0x0000, 0x0401, 0x0000, 0x0401, 0x0000, 0x0401, 0x0000, 0x0401, 0x0000, 0x0555, 0x0000, 116 | }; 117 | #endif 118 | -------------------------------------------------------------------------------- /source/helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #ifndef _HELPER_H_ 20 | #define _HELPER_H_ 21 | 22 | void zerofillBuffers(void* buf1, void* buf2); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /source/helper.s: -------------------------------------------------------------------------------- 1 | @ ----------------------------------------------------------------------------- 2 | @ Copyright 2014-2022 Arisotura 3 | @ 4 | @ This file is part of blargSnes. 5 | @ 6 | @ blargSnes is free software: you can redistribute it and/or modify it under 7 | @ the terms of the GNU General Public License as published by the Free 8 | @ Software Foundation, either version 3 of the License, or (at your option) 9 | @ any later version. 10 | @ 11 | @ blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | @ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | @ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | @ 15 | @ You should have received a copy of the GNU General Public License along 16 | @ with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | @ ----------------------------------------------------------------------------- 18 | 19 | .arm 20 | .text 21 | 22 | 23 | @ clear 32 bytes (16 samples) 24 | @ r0: left buffer 25 | @ r1: right buffer 26 | @ size should be a multiple of 16 27 | 28 | .global zerofillBuffers 29 | zerofillBuffers: 30 | stmdb sp!, {r4-r5, lr} 31 | mov r2, #0 32 | mov r3, #0 33 | mov r4, #0 34 | mov r5, #0 35 | stmia r0!, {r2-r5} 36 | stmia r0!, {r2-r5} 37 | stmia r1!, {r2-r5} 38 | stmia r1!, {r2-r5} 39 | ldmia sp!, {r4-r5, pc} 40 | -------------------------------------------------------------------------------- /source/main.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #ifndef MAIN_H 20 | #define MAIN_H 21 | 22 | void ClearConsole(); 23 | void bprintf(char* fmt, ...); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /source/mem.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include <3ds/types.h> 23 | 24 | void* MemAlloc(u32 size) 25 | { 26 | return memalign(0x10, size); 27 | } 28 | 29 | void MemFree(void* ptr) 30 | { 31 | free(ptr); 32 | } 33 | 34 | 35 | // gross VRAM allocator 36 | 37 | #define VRAM_BASE 0x1F000000 38 | #define VRAM_SIZE 0x600000 39 | #define VRAM_BLOCK_SIZE 0x800 // this size allows allocating framebuffers without wasting VRAM 40 | #define VRAM_NUM_BLOCKS (VRAM_SIZE / VRAM_BLOCK_SIZE) 41 | 42 | // 00 = free, 01 = start of allocated block, 02 = allocated block cont. 43 | u8 VRAM_Status[VRAM_NUM_BLOCKS]; 44 | 45 | 46 | void VRAM_Init() 47 | { 48 | memset(VRAM_Status, 0, sizeof(VRAM_Status)); 49 | } 50 | 51 | void* VRAM_Alloc(u32 size) 52 | { 53 | u32 i; 54 | u32 startid = 0, numfree = 0; 55 | u32 good = 0; 56 | 57 | size = (size + (VRAM_BLOCK_SIZE-1)) / VRAM_BLOCK_SIZE; 58 | 59 | for (i = 0; i < VRAM_NUM_BLOCKS; i++) 60 | { 61 | if (VRAM_Status[i] != 0) 62 | { 63 | numfree = 0; 64 | continue; 65 | } 66 | 67 | if (!numfree) startid = i; 68 | numfree++; 69 | 70 | if (numfree >= size) 71 | { 72 | good = 1; 73 | break; 74 | } 75 | } 76 | 77 | if (!good) 78 | { 79 | // VRAM full 80 | return 0; 81 | } 82 | 83 | VRAM_Status[startid] = 1; 84 | for (i = 1; i < size; i++) 85 | VRAM_Status[startid+i] = 2; 86 | 87 | return (void*)(VRAM_BASE + (startid * VRAM_BLOCK_SIZE)); 88 | } 89 | 90 | void VRAM_Free(void* _ptr) 91 | { 92 | u32 ptr = (u32)_ptr; 93 | if (ptr < VRAM_BASE) return; 94 | 95 | ptr -= VRAM_BASE; 96 | if (ptr >= VRAM_SIZE) return; 97 | 98 | ptr /= VRAM_BLOCK_SIZE; 99 | if (VRAM_Status[ptr] != 1) return; 100 | 101 | VRAM_Status[ptr++] = 0; 102 | while (VRAM_Status[ptr] == 2) 103 | VRAM_Status[ptr++] = 0; 104 | } 105 | -------------------------------------------------------------------------------- /source/mem.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #ifndef MEM_H 20 | #define MEM_H 21 | 22 | void* MemAlloc(u32 size); 23 | void MemFree(void* ptr); 24 | 25 | void VRAM_Init(); 26 | void* VRAM_Alloc(u32 size); 27 | void VRAM_Free(void* ptr); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /source/mem_io.s: -------------------------------------------------------------------------------- 1 | @ ----------------------------------------------------------------------------- 2 | @ Copyright 2014-2022 Arisotura 3 | @ 4 | @ This file is part of blargSnes. 5 | @ 6 | @ blargSnes is free software: you can redistribute it and/or modify it under 7 | @ the terms of the GNU General Public License as published by the Free 8 | @ Software Foundation, either version 3 of the License, or (at your option) 9 | @ any later version. 10 | @ 11 | @ blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | @ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | @ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | @ 15 | @ You should have received a copy of the GNU General Public License along 16 | @ with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | @ ----------------------------------------------------------------------------- 18 | 19 | .arm 20 | .align 4 21 | 22 | #include "cpu.inc" 23 | 24 | .text 25 | 26 | .align 4 27 | .global SNES_IORead8 28 | .global SNES_IORead16 29 | .global SNES_IOWrite8 30 | .global SNES_IOWrite16 31 | 32 | SNES_IORead8: 33 | stmdb sp!, {r12, lr} 34 | ldr lr, =ior8_ret 35 | and r12, r0, #0xFF00 36 | and r0, r0, #0xFF 37 | 38 | cmp r12, #0x2100 39 | beq PPU_Read8 40 | 41 | cmp r12, #0x4200 42 | beq SNES_GIORead8 43 | 44 | cmp r12, #0x4300 45 | beq DMA_Read8 46 | 47 | cmp r12, #0x4000 48 | addeq snesCycles, snesCycles, #0x60000 49 | beq SNES_JoyRead8 50 | 51 | ldrb r0, [snesStatus, #LastBusVal] 52 | ior8_ret: 53 | ldmia sp!, {r12, pc} 54 | 55 | 56 | SNES_IORead16: 57 | stmdb sp!, {r12, lr} 58 | ldr lr, =ior16_ret 59 | and r12, r0, #0xFF00 60 | and r0, r0, #0xFF 61 | 62 | cmp r12, #0x2100 63 | beq PPU_Read16 64 | 65 | cmp r12, #0x4200 66 | beq SNES_GIORead16 67 | 68 | cmp r12, #0x4300 69 | beq DMA_Read16 70 | 71 | cmp r12, #0x4000 72 | addeq snesCycles, snesCycles, #0xC0000 73 | beq SNES_JoyRead16 74 | 75 | ldrb r0, [snesStatus, #LastBusVal] 76 | orr r0, r0, r0, lsl #8 77 | ior16_ret: 78 | ldmia sp!, {r12, pc} 79 | 80 | 81 | SNES_IOWrite8: 82 | stmdb sp!, {r12, lr} 83 | ldr lr, =iow8_ret 84 | and r12, r0, #0xFF00 85 | and r0, r0, #0xFF 86 | 87 | cmp r12, #0x2100 88 | beq PPU_Write8 89 | 90 | cmp r12, #0x4300 91 | beq DMA_Write8 92 | 93 | cmp r12, #0x4000 94 | addeq snesCycles, snesCycles, #0x60000 95 | beq SNES_JoyWrite8 96 | 97 | cmp r12, #0x4200 98 | beq SNES_GIOWrite8 99 | 100 | iow8_ret: 101 | ldmia sp!, {r12, pc} 102 | 103 | 104 | SNES_IOWrite16: 105 | stmdb sp!, {r12, lr} 106 | ldr lr, =iow16_ret 107 | and r12, r0, #0xFF00 108 | and r0, r0, #0xFF 109 | 110 | cmp r12, #0x2100 111 | beq PPU_Write16 112 | 113 | cmp r12, #0x4300 114 | beq DMA_Write16 115 | 116 | cmp r12, #0x4000 117 | addeq snesCycles, snesCycles, #0xC0000 118 | beq SNES_JoyWrite16 119 | 120 | cmp r12, #0x4200 121 | beq SNES_GIOWrite16 122 | 123 | iow16_ret: 124 | ldmia sp!, {r12, pc} 125 | -------------------------------------------------------------------------------- /source/mixrate.h: -------------------------------------------------------------------------------- 1 | #define MIXBUFSIZE 8192 2 | -------------------------------------------------------------------------------- /source/plain_quad.g.pica: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Copyright 2014-2022 Arisotura 3 | ; 4 | ; This file is part of blargSnes. 5 | ; 6 | ; blargSnes is free software: you can redistribute it and/or modify it under 7 | ; the terms of the GNU General Public License as published by the Free 8 | ; Software Foundation, either version 3 of the License, or (at your option) 9 | ; any later version. 10 | ; 11 | ; blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | ; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | ; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | ; 15 | ; You should have received a copy of the GNU General Public License along 16 | ; with blargSnes. If not, see http:;www.gnu.org/licenses/. 17 | ; ----------------------------------------------------------------------------- 18 | 19 | .gsh point c0 20 | 21 | ; setup outmap 22 | .out o0 position 23 | .out o1 color 24 | 25 | ; input 26 | ; v0: XYZ coordinates 27 | ; v1: color 28 | 29 | .entry gmain 30 | .proc gmain 31 | ; turn two vertices into a rectangle 32 | ; setemit: vtxid, primemit, winding 33 | 34 | ; v0 = vertex 0, position 35 | ; v1 = vertex 0, color 36 | ; v2 = vertex 1, position 37 | ; v3 = vertex 1, color 38 | 39 | ; x1 y1 40 | setemit 0 41 | mov o0, v0 42 | mov o1, v1 43 | emit 44 | 45 | ; x2 y1 46 | setemit 1 47 | mov r0, v2 48 | mov r0.y, v0.y 49 | mov o0, r0 50 | mov o1, v3 51 | emit 52 | 53 | ; x1 y2 54 | setemit 2, prim 55 | mov r0, v0 56 | mov r0.y, v2.y 57 | mov o0, r0 58 | mov o1, v1 59 | emit 60 | 61 | ; x2 y2 62 | setemit 0, prim inv 63 | mov o0, v2 64 | mov o1, v3 65 | emit 66 | 67 | end 68 | nop 69 | .end 70 | -------------------------------------------------------------------------------- /source/plain_quad.v.pica: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Copyright 2014-2022 Arisotura 3 | ; 4 | ; This file is part of blargSnes. 5 | ; 6 | ; blargSnes is free software: you can redistribute it and/or modify it under 7 | ; the terms of the GNU General Public License as published by the Free 8 | ; Software Foundation, either version 3 of the License, or (at your option) 9 | ; any later version. 10 | ; 11 | ; blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | ; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | ; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | ; 15 | ; You should have received a copy of the GNU General Public License along 16 | ; with blargSnes. If not, see http:;www.gnu.org/licenses/. 17 | ; ----------------------------------------------------------------------------- 18 | 19 | ; setup constants 20 | .constf const1(0.0078125, 0.00390625, 0.0, 1.0) 21 | .constf const2(-1.0, 0.0, 0.0, 0.0) 22 | 23 | ; setup outmap 24 | .out o0 position 25 | .out o1 color 26 | 27 | ; input 28 | ; v0: XYZ coordinates 29 | ; v1: color 30 | 31 | .entry vmain 32 | .proc vmain 33 | mov r0, const1 34 | mul r0.xyz, r0.xyz, v0.xyz 35 | add r0.xyz, const2.xxx, r0.xyz 36 | mov o0, r0 37 | 38 | ; result.color = in.color 39 | mul o1, const1.yyyy, v1 40 | end 41 | nop 42 | .end 43 | -------------------------------------------------------------------------------- /source/ppu.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #ifndef PPU_H 20 | #define PPU_H 21 | 22 | typedef struct 23 | { 24 | u8 EndOffset; 25 | u8 modeType; 26 | u8 Sel; 27 | 28 | float* vertexStart; 29 | u32 vertexLen; 30 | 31 | union 32 | { 33 | struct 34 | { 35 | s16 A; 36 | s16 B; 37 | }; 38 | u32 AffineParams1; 39 | }; 40 | union 41 | { 42 | struct 43 | { 44 | s16 C; 45 | s16 D; 46 | }; 47 | u32 AffineParams2; 48 | }; 49 | union 50 | { 51 | struct 52 | { 53 | s16 RefX; 54 | s16 RefY; 55 | }; 56 | u32 RefParams; 57 | }; 58 | union 59 | { 60 | struct 61 | { 62 | s16 XScroll; 63 | s16 YScroll; 64 | }; 65 | u32 ScrollParams; 66 | }; 67 | 68 | } PPU_Mode7Section; 69 | 70 | typedef struct 71 | { 72 | u16* Dest; 73 | u16* SrcPixels; 74 | u16 Attrib; 75 | u8 Alpha; 76 | u16 Start, End; 77 | 78 | } PPU_DeferredTile; 79 | 80 | typedef struct 81 | { 82 | u8 EndOffset; 83 | u8 Size; 84 | union 85 | { 86 | struct 87 | { 88 | u16 XScroll; 89 | u16 YScroll; 90 | }; 91 | u32 ScrollParams; 92 | }; 93 | union 94 | { 95 | struct 96 | { 97 | u16 TilesetOffset; 98 | u16 TilemapOffset; 99 | }; 100 | u32 GraphicsParams; 101 | }; 102 | 103 | } PPU_BGSection; 104 | 105 | typedef struct 106 | { 107 | union 108 | { 109 | struct 110 | { 111 | u16 TilesetOffset; 112 | u16 TilemapOffset; 113 | }; 114 | u32 GraphicsParams; 115 | }; 116 | union 117 | { 118 | struct 119 | { 120 | u16 LastTilesetOffset; 121 | u16 LastTilemapOffset; 122 | }; 123 | u32 LastGraphicsParams; 124 | }; 125 | 126 | u16* Tileset; 127 | u16* Tilemap; 128 | u8 Size; 129 | u8 LastSize; 130 | 131 | union 132 | { 133 | struct 134 | { 135 | u16 XScroll; 136 | u16 YScroll; 137 | }; 138 | u32 ScrollParams; 139 | }; 140 | union 141 | { 142 | struct 143 | { 144 | u16 LastXScroll; 145 | u16 LastYScroll; 146 | }; 147 | u32 LastScrollParams; 148 | }; 149 | 150 | u8 WindowMask; 151 | u16 WindowCombine; 152 | 153 | u32 NumDeferredTiles; 154 | PPU_DeferredTile DeferredTiles[40]; 155 | 156 | PPU_BGSection* CurSection; 157 | PPU_BGSection Sections[240]; 158 | 159 | } PPU_Background; 160 | 161 | typedef struct 162 | { 163 | // no start offset in here; start offset is the end offset of the previous segment 164 | u16 EndOffset; // 256 = final segment 165 | u8 WindowMask; // each 2 bits: 2=inside, 3=outside 166 | u8 ColorMath; // 0x20 = inside color math window, 0x10 = outside 167 | u8 FinalMaskMain, FinalMaskSub; // for use by the hardware renderer 168 | 169 | } PPU_WindowSegment; 170 | 171 | typedef struct 172 | { 173 | u8 EndOffset; 174 | PPU_WindowSegment Window[5]; 175 | 176 | } PPU_WindowSection; 177 | 178 | typedef struct 179 | { 180 | u8 EndOffset; 181 | u8 ColorMath; // 0 = add, !0 = subtract 182 | u8 Brightness; // brightness (0-255) 183 | 184 | } PPU_ColorEffectSection; 185 | 186 | typedef struct 187 | { 188 | u8 EndOffset; 189 | u8 Mode; 190 | u16 MainScreen, SubScreen; 191 | u8 ColorMath1, ColorMath2; 192 | } PPU_ModeSection; 193 | 194 | typedef struct 195 | { 196 | u8 EndOffset; 197 | u16 Color; 198 | u8 ColorMath2; 199 | } PPU_MainBackdropSection; 200 | 201 | typedef struct 202 | { 203 | u8 EndOffset; 204 | u16 Color; 205 | u8 Div2; 206 | } PPU_SubBackdropSection; 207 | 208 | typedef struct 209 | { 210 | u8 EndOffset; 211 | const u8 *OBJWidth, *OBJHeight; 212 | u16 OBJTilesetAddr; 213 | u32 OBJGap; 214 | } PPU_OBJSection; 215 | 216 | 217 | typedef struct 218 | { 219 | //u16 VCount; 220 | //u8 ScreenHeight; 221 | 222 | u8 HardwareRenderer; 223 | 224 | // 16+256+16: we leave 16 extra pixels on both sides so we don't have to handle tiles that are partially offscreen 225 | u16* MainBuffer; 226 | u16* SubBuffer; 227 | 228 | // OBJ layer 229 | // bit0-7: color # (0-127, selecting from upper palette region) 230 | // bit8-15: BG-relative priority 231 | u16 OBJBuffer[16+256+16]; 232 | 233 | u8 SpritesOnLine[4] __attribute__((aligned(4))); 234 | 235 | u16 CGRAMAddr; 236 | u8 CGRAMVal; 237 | u16 CGRAM[256]; // SNES CGRAM, xBGR1555 238 | u16 Palette[256]; // our own palette, converted to RGBx5551 239 | u16 HardPalette[256]; // special copy for the hardware renderer 240 | u8 PaletteUpdateCount[64]; 241 | u16 PaletteUpdateCount256; 242 | 243 | u16 VRAMAddr; 244 | u16 VRAMPref; 245 | u8 VRAMInc; 246 | u16 VRAMStep; 247 | u8 VRAM[0x10000]; 248 | u8 VRAM7[0x8000]; 249 | u8 VRAMUpdateCount[0x1000]; 250 | u16 VRAM7UpdateCount[0x800]; 251 | 252 | u8 TileBitmap[0x74000]; 253 | u8 TileEmpty[0x1000]; 254 | 255 | u16 OAMAddr; 256 | u8 OAMVal; 257 | u8 OAMPrio; 258 | u8 FirstOBJ; 259 | u16 OAMReload; 260 | u8 OAM[0x220]; 261 | u8 HardOAM[0x220]; 262 | 263 | const u8* OBJWidth; 264 | const u8* OBJHeight; 265 | 266 | 267 | u8 CurBrightness; 268 | u8 ForcedBlank; 269 | u8 Interlace; 270 | 271 | u8 Mode; 272 | 273 | u8 ModeDirty; 274 | PPU_ModeSection ModeSections[240]; 275 | PPU_ModeSection* CurModeSection; 276 | 277 | union 278 | { 279 | struct 280 | { 281 | u8 MainLayerEnable; 282 | u8 MainWindowEnable; 283 | }; 284 | u16 MainScreen; 285 | }; 286 | union 287 | { 288 | struct 289 | { 290 | u8 SubLayerEnable; 291 | u8 SubWindowEnable; 292 | }; 293 | u16 SubScreen; 294 | }; 295 | u8 ColorMath1; 296 | u8 ColorMath2; 297 | 298 | 299 | u16 SubBackdrop; 300 | 301 | u8 MainBackdropDirty; 302 | PPU_MainBackdropSection MainBackdropSections[240]; 303 | PPU_MainBackdropSection* CurMainBackdrop; 304 | 305 | u8 SubBackdropDirty; 306 | PPU_SubBackdropSection SubBackdropSections[240]; 307 | PPU_SubBackdropSection* CurSubBackdrop; 308 | 309 | PPU_ColorEffectSection ColorEffectSections[240]; 310 | PPU_ColorEffectSection* CurColorEffect; 311 | u8 ColorEffectDirty; 312 | 313 | u16 WinX[4]; 314 | u8 WinSel[4] __attribute__((aligned(4))); 315 | u8 WinLogic[2] __attribute__((aligned(2))); 316 | 317 | u8 WinMask[3]; 318 | u8 WinCombine[2]; 319 | 320 | u8 BGOld; 321 | u8 M7Old; 322 | 323 | s16 MulA; 324 | s8 MulB; 325 | s32 MulResult; 326 | 327 | u8 M7Sel; 328 | union 329 | { 330 | struct 331 | { 332 | s16 M7A; 333 | s16 M7B; 334 | }; 335 | u32 M7AffineParams1; 336 | }; 337 | union 338 | { 339 | struct 340 | { 341 | s16 M7C; 342 | s16 M7D; 343 | }; 344 | u32 M7AffineParams2; 345 | }; 346 | union 347 | { 348 | struct 349 | { 350 | s16 M7RefX; 351 | s16 M7RefY; 352 | }; 353 | u32 M7RefParams; 354 | }; 355 | union 356 | { 357 | struct 358 | { 359 | s16 M7XScroll; 360 | s16 M7YScroll; 361 | }; 362 | u32 M7ScrollParams; 363 | }; 364 | 365 | u8 M7ExtBG; 366 | 367 | PPU_Mode7Section Mode7Sections[240]; 368 | PPU_Mode7Section* CurMode7Section; 369 | u8 Mode7Dirty; 370 | 371 | PPU_Background BG[4]; 372 | 373 | PPU_OBJSection OBJSections[240]; 374 | PPU_OBJSection* CurOBJSection; 375 | 376 | u16 OBJTilesetAddr; 377 | u16* OBJTileset; 378 | u32 OBJGap; 379 | u8 OBJVDir; 380 | 381 | u8 OBJDirty; 382 | 383 | u8 OBJWindowMask; 384 | u16 OBJWindowCombine; 385 | u8 ColorMathWindowMask; 386 | u16 ColorMathWindowCombine; 387 | 388 | PPU_WindowSegment Window[5]; 389 | 390 | PPU_WindowSection WindowSections[240]; 391 | PPU_WindowSection* CurWindowSection; 392 | 393 | u8 WindowDirty; 394 | 395 | 396 | u16 OPHCT, OPVCT; 397 | u8 OPHFlag, OPVFlag; 398 | u8 OPLatch; 399 | 400 | u8 OBJOverflow; 401 | 402 | } PPUState; 403 | 404 | extern PPUState PPU; 405 | 406 | extern int SkipThisFrame; 407 | 408 | 409 | void PPU_Init(); 410 | void PPU_SwitchRenderers(); 411 | void PPU_Reset(); 412 | void PPU_DeInit(); 413 | 414 | void PPU_SetColor(u32 num, u16 val); 415 | 416 | void PPU_LatchHVCounters(); 417 | 418 | u8 PPU_Read8(u32 addr); 419 | u16 PPU_Read16(u32 addr); 420 | void PPU_Write8(u32 addr, u8 val); 421 | void PPU_Write16(u32 addr, u16 val); 422 | 423 | void PPU_RenderScanline(u32 line); 424 | void PPU_VBlank(); 425 | 426 | 427 | void PPU_Init_Soft(); 428 | void PPU_DeInit_Soft(); 429 | 430 | void PPU_RenderScanline_Soft(u32 line); 431 | void PPU_VBlank_Soft(); 432 | 433 | 434 | void PPU_Init_Hard(); 435 | void PPU_DeInit_Hard(); 436 | void PPU_Reset_Hard(); 437 | 438 | void PPU_RenderScanline_Hard(u32 line); 439 | void PPU_VBlank_Hard(); 440 | 441 | 442 | u32 PPU_TranslateVRAMAddress(u32 addr); 443 | void PPU_ConvertVRAM8(u32 addr, u8 val); 444 | void PPU_ConvertVRAM16(u32 addr, u16 val); 445 | void PPU_ConvertVRAMAll(); 446 | 447 | void PPU_ComputeWindows(PPU_WindowSegment* s); 448 | void PPU_BlendScreens(u32 colorformat); 449 | 450 | void PPU_SwitchRenderers(); 451 | void ApplyScaling(); 452 | void FinishRendering(); 453 | void RenderTopScreen(); 454 | 455 | #endif 456 | -------------------------------------------------------------------------------- /source/render_hard.g.pica: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Copyright 2014-2022 Arisotura 3 | ; 4 | ; This file is part of blargSnes. 5 | ; 6 | ; blargSnes is free software: you can redistribute it and/or modify it under 7 | ; the terms of the GNU General Public License as published by the Free 8 | ; Software Foundation, either version 3 of the License, or (at your option) 9 | ; any later version. 10 | ; 11 | ; blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | ; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | ; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | ; 15 | ; You should have received a copy of the GNU General Public License along 16 | ; with blargSnes. If not, see http:;www.gnu.org/licenses/. 17 | ; ----------------------------------------------------------------------------- 18 | 19 | .gsh point c0 20 | 21 | ; setup constants 22 | .constf myconst(0.0, 0.0, 0.0078125, 1.0) 23 | 24 | ; setup outmap 25 | .out o0 position 26 | .out o1 color 27 | .out o2 texcoord0 28 | .out o3 texcoord1 29 | 30 | 31 | .entry gmain 32 | .proc gmain 33 | ; turn two vertices into a rectangle 34 | ; setemit: vtxid, primemit, winding 35 | 36 | ; v0 = vertex 0, position 37 | ; v1 = vertex 0, texcoord 38 | ; v2 = vertex 1, position 39 | ; v3 = vertex 1, texcoord 40 | 41 | ; x1 y1 42 | setemit 0 43 | mov o0, v0 44 | mov o1, myconst.wwww 45 | mov o2, v1 46 | mov o3, v1 47 | emit 48 | 49 | ; x2 y1 50 | setemit 1 51 | mov r0, v2 52 | mov r0.y, v0.y 53 | mov o0, r0 54 | mov o1, myconst.wwww 55 | mov r2, v3 56 | mov r2.y, v1.y 57 | mov o2, r2 58 | mov o3, r2 59 | emit 60 | 61 | ; x1 y2 62 | setemit 2, prim 63 | mov r0, v0 64 | mov r0.y, v2.y 65 | mov o0, r0 66 | mov o1, myconst.wwww 67 | mov r2, v1 68 | mov r2.y, v3.y 69 | mov o2, r2 70 | mov o3, r2 71 | emit 72 | 73 | ; x2 y2 74 | setemit 0, prim inv 75 | mov o0, v2 76 | mov o1, myconst.wwww 77 | mov o2, v3 78 | mov o3, v3 79 | emit 80 | 81 | end 82 | nop 83 | .end 84 | -------------------------------------------------------------------------------- /source/render_hard.v.pica: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Copyright 2014-2022 Arisotura 3 | ; 4 | ; This file is part of blargSnes. 5 | ; 6 | ; blargSnes is free software: you can redistribute it and/or modify it under 7 | ; the terms of the GNU General Public License as published by the Free 8 | ; Software Foundation, either version 3 of the License, or (at your option) 9 | ; any later version. 10 | ; 11 | ; blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | ; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | ; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | ; 15 | ; You should have received a copy of the GNU General Public License along 16 | ; with blargSnes. If not, see http:;www.gnu.org/licenses/. 17 | ; ----------------------------------------------------------------------------- 18 | 19 | ; setup constants 20 | .constf const1(0.0078125, 0.00390625, 0.0, 1.0) 21 | .constf const2(-1.0, 0.0, 0.0, 0.0) 22 | 23 | ; setup outmap 24 | .out o0 position 25 | .out o1 texcoord0 26 | 27 | ; INPUT 28 | ; - VERTEX ATTRIBUTES - 29 | ; v0: vertex (x, y, and optional z) 30 | ; v1: texcoord 31 | ; - UNIFORMS - 32 | ; c4: texcoord scale 33 | 34 | .entry vmain 35 | .proc vmain 36 | mul r0.xyzw, c4.zzzw, v0.xyzw 37 | add r0.xyz, const2.xxx, r0.xyz 38 | mov o0, r0 39 | 40 | ; result.texcoord = in.texcoord * (uniform scale in c4) 41 | mul r1, c4.xyzz, v1 42 | mov o1, r1 43 | end 44 | nop 45 | .end 46 | -------------------------------------------------------------------------------- /source/render_hard7.g.pica: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Copyright 2014-2022 Arisotura 3 | ; 4 | ; This file is part of blargSnes. 5 | ; 6 | ; blargSnes is free software: you can redistribute it and/or modify it under 7 | ; the terms of the GNU General Public License as published by the Free 8 | ; Software Foundation, either version 3 of the License, or (at your option) 9 | ; any later version. 10 | ; 11 | ; blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | ; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | ; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | ; 15 | ; You should have received a copy of the GNU General Public License along 16 | ; with blargSnes. If not, see http:;www.gnu.org/licenses/. 17 | ; ----------------------------------------------------------------------------- 18 | 19 | .gsh point c0 20 | 21 | ; setup constants 22 | .constf myconst (256.0, 1.0, 0.0, 1.0) 23 | 24 | .alias mycolor myconst.yyyy 25 | 26 | ; setup outmap 27 | .out outpos position 28 | .out outclr color 29 | .out outtc0 texcoord0.xy 30 | 31 | .alias projMtx c0 32 | .alias mode7Type c5 33 | 34 | ; INPUT 35 | ; - VERTEX ATTRIBUTES - 36 | ; v0: vertex (x, y, and optional z) 37 | ; v1: texcoord 38 | ; - UNIFORMS - 39 | ; c0-c3: projection matrix 40 | ; c4: texcoord scale 41 | 42 | .entry gmain 43 | .proc gmain 44 | ; turn two vertices into a rectangle 45 | ; setemit: vtxid, primemit winding 46 | 47 | ; v0 = (------, scanline) 48 | ; v1 = (1st tex coord) 49 | ; v2 = (1st ratio, 2nd ratio) 50 | ; v3 = (2nd tex coord) 51 | 52 | 53 | mov r4, v2.xxxx 54 | mov r5, v2.yyyy 55 | 56 | ; Are we handling the outer area or inner? 57 | mov r0, myconst.w 58 | cmp mode7Type, eq, eq, r0 59 | jmpc cmp.x, Draw_Outer 60 | 61 | Draw_Inner: 62 | ; If v2.x > v2.y, then there is no inner part to render 63 | cmp r4, gt, gt, r5 64 | ifc cmp.x 65 | end 66 | .end 67 | 68 | call CalcProjMtx 69 | 70 | ; Does the inner part cover the entire line? 71 | cmp myconst.zw, eq, eq, v2 72 | ifc cmp.x & cmp.y 73 | call Draw_Line 74 | end 75 | .end 76 | 77 | ; It does not, so let's adjust it 78 | 79 | add r6, r1, -r0 80 | mad r1.x, r5.x, r6.x, r0.x 81 | mad r0.x, r4.x, r6.x, r0.x 82 | 83 | add r6, r3, -r2 84 | mad r3, r5, r6, r2 85 | mad r2, r4, r6, r2 86 | 87 | call Draw_Line 88 | 89 | end 90 | 91 | Draw_Outer: 92 | ; Examine if there are even outer tiles to render (where v2.x > 0.0 and v2.y < 1.0) 93 | cmp myconst.zw, eq, eq, v2 94 | ifc cmp.x & cmp.y 95 | end 96 | .end 97 | 98 | call CalcProjMtx 99 | 100 | ; Is the entire line part of the outer area? 101 | cmp r4, gt, gt, r5 102 | ifc cmp.x 103 | call Draw_Line 104 | end 105 | .end 106 | 107 | ; If here, then the outer part is visible, but are parts of either one or both sides to the inner part 108 | 109 | ; Is there any outer part on the left side of the inner section? 110 | ;cmp myconst.zw, lt, lt, r4 111 | ;ifc cmp.x 112 | add r6, r1, -r0 113 | mad r1.x, r4.x, r6.x, r0.x 114 | 115 | add r6, r3, -r2 116 | mad r3, r4, r6, r2 117 | 118 | call Draw_Line 119 | ;.end 120 | 121 | ;end 122 | 123 | ; Is there any outer part on the right side of the inner section? 124 | ;cmp myconst.zw, gt, gt, r5 125 | ;ifc cmp.y 126 | ; Because we may have done an outer left side render, we need to move the original values back into place 127 | mov r1, r8 128 | mov r3, v3 129 | 130 | add r6, r1, -r0 131 | mad r0.x, r5.x, r6.x, r0.x 132 | 133 | add r6, r3, -r2 134 | mad r2, r5, r6, r2 135 | 136 | call Draw_Line 137 | ;.end 138 | 139 | end 140 | .end 141 | 142 | .proc CalcProjMtx 143 | mov r2.xzw, myconst.zzw 144 | mov r2.y, v0.y 145 | mov r3, myconst 146 | add r3.y, v0.y, r3.y 147 | 148 | dp4 r7.x, projMtx[0], r2 149 | dp4 r7.y, projMtx[1], r2 150 | dp4 r7.z, projMtx[2], r2 151 | dp4 r7.w, projMtx[3], r2 152 | 153 | dp4 r8.x, projMtx[0], r3 154 | dp4 r8.y, projMtx[1], r3 155 | dp4 r8.z, projMtx[2], r3 156 | dp4 r8.w, projMtx[3], r3 157 | 158 | mov r0, r7 159 | mov r1, r8 160 | mov r2, v1 161 | mov r3, v3 162 | .end 163 | 164 | 165 | .proc Draw_Line 166 | ; x1 y1 167 | setemit 0 168 | mov outpos, r0 169 | mov outclr, myconst.w 170 | mov outtc0, r2 171 | emit 172 | 173 | ; x2 y1 174 | setemit 1 175 | mov outpos.x, r1.x 176 | mov outpos.yzw, r0 177 | mov outclr, myconst.w 178 | mov outtc0, r3 179 | emit 180 | 181 | ; x1 y2 182 | setemit 2, prim 183 | mov outpos.xzw, r0 184 | mov outpos.y, r1.y 185 | mov outclr, myconst.w 186 | mov outtc0, r2 187 | emit 188 | 189 | ; x2 y2 190 | setemit 0, prim inv 191 | mov outpos, r1 192 | mov outclr, myconst.w 193 | mov outtc0, r3 194 | emit 195 | .end 196 | -------------------------------------------------------------------------------- /source/render_hard7.v.pica: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Copyright 2014-2022 Arisotura 3 | ; 4 | ; This file is part of blargSnes. 5 | ; 6 | ; blargSnes is free software: you can redistribute it and/or modify it under 7 | ; the terms of the GNU General Public License as published by the Free 8 | ; Software Foundation, either version 3 of the License, or (at your option) 9 | ; any later version. 10 | ; 11 | ; blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | ; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | ; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | ; 15 | ; You should have received a copy of the GNU General Public License along 16 | ; with blargSnes. If not, see http:;www.gnu.org/licenses/. 17 | ; ----------------------------------------------------------------------------- 18 | 19 | ; setup constants 20 | .constf const1(0.0078125, -1.0, 0.0, 1.0) 21 | 22 | ; setup outmap 23 | .out o0 position 24 | .out o1 texcoord0 25 | 26 | ; INPUT 27 | ; - VERTEX ATTRIBUTES - 28 | ; v0: vertex (x, y, and optional z) 29 | ; v1: texcoord 30 | ; - UNIFORMS - 31 | ; c0-c3: projection matrix 32 | ; c4: texcoord scale 33 | 34 | .entry vmain 35 | .proc vmain 36 | mov o0, v0 37 | 38 | ; result.texcoord = in.texcoord * (uniform scale in c4) 39 | mul r1, c4, v1 40 | mov o1, r1 41 | end 42 | nop 43 | .end 44 | -------------------------------------------------------------------------------- /source/render_hard_obj.g.pica: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Copyright 2014-2022 Arisotura 3 | ; 4 | ; This file is part of blargSnes. 5 | ; 6 | ; blargSnes is free software: you can redistribute it and/or modify it under 7 | ; the terms of the GNU General Public License as published by the Free 8 | ; Software Foundation, either version 3 of the License, or (at your option) 9 | ; any later version. 10 | ; 11 | ; blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | ; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | ; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | ; 15 | ; You should have received a copy of the GNU General Public License along 16 | ; with blargSnes. If not, see http:;www.gnu.org/licenses/. 17 | ; ----------------------------------------------------------------------------- 18 | 19 | .gsh point c0 20 | 21 | ; setup constants 22 | .constf myconst(0.0, 0.0, 0.0078125, 1.0) 23 | 24 | ; setup outmap 25 | .out o0 position 26 | .out o1 color 27 | .out o2 texcoord0 28 | 29 | 30 | .entry gmain 31 | .proc gmain 32 | ; turn two vertices into a rectangle 33 | ; setemit: vtxid, primemit, winding 34 | 35 | ; v0 = vertex 0, position 36 | ; v1 = vertex 0, color 37 | ; v2 = vertex 0, texcoord 38 | ; v3 = vertex 1, position 39 | ; v4 = vertex 1, color 40 | ; v5 = vertex 1, texcoord 41 | 42 | ; x1 y1 43 | setemit 0 44 | mov o0, v0 45 | mov o1, v1 46 | mov o2, v2 47 | emit 48 | 49 | ; x2 y1 50 | setemit 1 51 | mov r0, v3 52 | mov r0.y, v0.y 53 | mov o0, r0 54 | mov o1, v4 55 | mov r2, v5 56 | mov r2.y, v2.y 57 | mov o2, r2 58 | emit 59 | 60 | ; x1 y2 61 | setemit 2, prim 62 | mov r0, v0 63 | mov r0.y, v3.y 64 | mov o0, r0 65 | mov o1, v1 66 | mov r2, v2 67 | mov r2.y, v5.y 68 | mov o2, r2 69 | emit 70 | 71 | ; x2 y2 72 | setemit 0, prim inv 73 | mov o0, v3 74 | mov o1, v4 75 | mov o2, v5 76 | emit 77 | 78 | end 79 | nop 80 | .end 81 | -------------------------------------------------------------------------------- /source/render_hard_obj.v.pica: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Copyright 2014-2022 Arisotura 3 | ; 4 | ; This file is part of blargSnes. 5 | ; 6 | ; blargSnes is free software: you can redistribute it and/or modify it under 7 | ; the terms of the GNU General Public License as published by the Free 8 | ; Software Foundation, either version 3 of the License, or (at your option) 9 | ; any later version. 10 | ; 11 | ; blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | ; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | ; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | ; 15 | ; You should have received a copy of the GNU General Public License along 16 | ; with blargSnes. If not, see http:;www.gnu.org/licenses/. 17 | ; ----------------------------------------------------------------------------- 18 | 19 | ; setup constants 20 | .constf const1(0.0078125, 0.00390625, 0.0, 1.0) 21 | .constf const2(-1.0, 0.0, 0.0, 0.0) 22 | .constf zfactors(-0.99609375, -0.0038909912109375, -0.00000762939453125, 0.0) 23 | 24 | ; setup outmap 25 | .out o0 position 26 | .out o1 color 27 | .out o2 texcoord0 28 | 29 | ; INPUT 30 | ; - VERTEX ATTRIBUTES - 31 | ; v0: vertex (x, y, and optional z) 32 | ; v1: texcoord 33 | ; - UNIFORMS - 34 | ; c4: texcoord scale 35 | 36 | .entry vmain 37 | .proc vmain 38 | ; calculate Z based on prio values in v1.zw 39 | mov r1.xy, v1.zw 40 | mov r1.zw, const1.wz 41 | 42 | mov r0, const1 43 | mul r0.xy, r0.xx, v0.xy 44 | add r0.xy, const2.xx, r0.xy 45 | dp3 r0.z, zfactors.xyz, r1.xyz 46 | mov o0, r0 47 | 48 | ; color 49 | mov r1.rgb, const1.www 50 | mul r1.a, const1.y, v0.z 51 | mov o1, r1 52 | 53 | ; result.texcoord = in.texcoord * (uniform scale in c4) 54 | mul r1, c4, v1 55 | mov r1.zw, const1.zw 56 | mov o2, r1 57 | end 58 | nop 59 | .end 60 | -------------------------------------------------------------------------------- /source/render_soft.g.pica: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Copyright 2014-2022 Arisotura 3 | ; 4 | ; This file is part of blargSnes. 5 | ; 6 | ; blargSnes is free software: you can redistribute it and/or modify it under 7 | ; the terms of the GNU General Public License as published by the Free 8 | ; Software Foundation, either version 3 of the License, or (at your option) 9 | ; any later version. 10 | ; 11 | ; blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | ; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | ; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | ; 15 | ; You should have received a copy of the GNU General Public License along 16 | ; with blargSnes. If not, see http:;www.gnu.org/licenses/. 17 | ; ----------------------------------------------------------------------------- 18 | 19 | .gsh point c0 20 | 21 | ; setup constants 22 | .constf myconst(0.0, 0.0, 0.00390625, 1.0) 23 | 24 | ; setup outmap 25 | .out o0 position 26 | .out o1 color 27 | .out o2 texcoord0 28 | .out o3 texcoord1 29 | 30 | .entry gmain 31 | .proc gmain 32 | ; turn two vertices into a rectangle 33 | ; setemit: vtxid, primemit, winding 34 | 35 | ; v0 = vertex 0, position 36 | ; v1 = vertex 0, texcoord 37 | ; v2 = vertex 1, position 38 | ; v3 = vertex 1, texcoord 39 | 40 | ; x1 y1 41 | setemit 0 42 | mov o0, v0 43 | mov o1, myconst.wwww 44 | mov o2, v1 45 | mov o3, v1 46 | emit 47 | 48 | ; x2 y1 49 | setemit 1 50 | mov r0, v2 51 | mov r0.y, v0.y 52 | mov o0, r0 53 | mov o1, myconst.wwww 54 | mov r2, v3 55 | mov r2.y, v1.y 56 | mov o2, r2 57 | mov r3, v3 58 | mov r3.y, v1.y 59 | mov o3, r3 60 | emit 61 | 62 | ; x1 y2 63 | setemit 2, prim 64 | mov r0, v0 65 | mov r0.y, v2.y 66 | mov o0, r0 67 | mov o1, myconst.wwww 68 | mov r2, v1 69 | mov r2.y, v3.y 70 | mov o2, r2 71 | mov r3, v1 72 | mov r3.y, v3.y 73 | mov o3, r3 74 | emit 75 | 76 | ; x2 y2 77 | setemit 0, prim inv 78 | mov o0, v2 79 | ;mov o1, myconst (0x8) 80 | mov o1, myconst ; TODO??? 81 | mov o2, v3 82 | mov o3, v3 83 | emit 84 | 85 | end 86 | nop 87 | .end 88 | -------------------------------------------------------------------------------- /source/render_soft.v.pica: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Copyright 2014-2022 Arisotura 3 | ; 4 | ; This file is part of blargSnes. 5 | ; 6 | ; blargSnes is free software: you can redistribute it and/or modify it under 7 | ; the terms of the GNU General Public License as published by the Free 8 | ; Software Foundation, either version 3 of the License, or (at your option) 9 | ; any later version. 10 | ; 11 | ; blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | ; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | ; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | ; 15 | ; You should have received a copy of the GNU General Public License along 16 | ; with blargSnes. If not, see http:;www.gnu.org/licenses/. 17 | ; ----------------------------------------------------------------------------- 18 | 19 | ; setup constants 20 | .constf const1(0.0078125, 0.00390625, 0.0, 1.0) 21 | .constf const2(-1.0, 0.0, 0.0, 0.0) 22 | 23 | ; setup outmap 24 | .out o0 position 25 | .out o1 texcoord0 26 | 27 | .entry vmain 28 | .proc vmain 29 | mov r0, const1 30 | mul r0.xyz, r0.xxz, v0.xyz 31 | add r0.xyz, const2.xxx, r0.xyz 32 | mov o0, r0 33 | 34 | ; result.texcoord = in.texcoord * (1/256) 35 | mul o1, const1.yyww, v1 ; multiply s/t, multiply r/q by 1 36 | end 37 | nop 38 | .end 39 | -------------------------------------------------------------------------------- /source/rom.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include <3ds/types.h> 23 | #include <3ds/svc.h> 24 | 25 | #include "mem.h" 26 | #include "cpu.h" 27 | #include "snes.h" 28 | 29 | 30 | u8* ROM_Buffer = NULL; 31 | u32 ROM_BufferSize; 32 | u32 ROM_FileSize; 33 | u32 ROM_BaseOffset; 34 | u32 ROM_HeaderOffset; 35 | u32 ROM_NumBanks; 36 | 37 | 38 | // TODO find a better way to do speedhacks 39 | // (like, detecting branches with offset -4 or -5 at runtime) 40 | void ROM_ApplySpeedHacks(int banknum, u8* bank) 41 | { 42 | return; 43 | int i; 44 | int bsize = SNES_HiROM ? 0x10000 : 0x8000; 45 | 46 | for (i = 2; i < bsize-4;) 47 | { 48 | //if (bank[i] == 0xA5 && bank[i+2] == 0xF0 && bank[i+3] == 0xFC) 49 | if (bank[i] == 0xA5 && (bank[i+2] & 0x1F) == 0x10 && bank[i+3] == 0xFC) 50 | { 51 | u8 branchtype = bank[i+2]; 52 | bank[i+2] = 0x42; 53 | bank[i+3] = (bank[i+3] & 0x0F) | (branchtype & 0xF0); 54 | 55 | bprintf("Speed hack installed @ %02X:%04X\n", banknum, (SNES_HiROM?0:0x8000)+i); 56 | 57 | i += 4; 58 | } 59 | else if ((bank[i] == 0xAD || bank[i] == 0xCD) 60 | && (bank[i+3] & 0x1F) == 0x10 && bank[i+4] == 0xFB) 61 | { 62 | u16 addr = bank[i+1] | (bank[i+2] << 8); 63 | 64 | if ((addr & 0xFFF0) != 0x2140) 65 | { 66 | u8 branchtype = bank[i+3]; 67 | bank[i+3] = 0x42; 68 | bank[i+4] = (bank[i+4] & 0x0F) | (branchtype & 0xF0); 69 | 70 | bprintf("Speed hack installed @ %02X:%04X\n", banknum, (SNES_HiROM?0:0x8000)+i); 71 | } 72 | 73 | i += 5; 74 | } 75 | else 76 | i++; 77 | } 78 | } 79 | 80 | 81 | void ROM_MapBank(u32 bank, u8* ptr) 82 | { 83 | u32 hi_slow = SNES_FastROM ? 0 : MPTR_SLOW; 84 | 85 | if (SNES_HiROM) 86 | { 87 | for (; bank < 0x80; bank += ROM_NumBanks) 88 | { 89 | if (bank < 0x7E) 90 | { 91 | MEM_PTR(bank, 0x0000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0x0000]; 92 | MEM_PTR(bank, 0x2000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0x2000]; 93 | MEM_PTR(bank, 0x4000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0x4000]; 94 | MEM_PTR(bank, 0x6000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0x6000]; 95 | MEM_PTR(bank, 0x8000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0x8000]; 96 | MEM_PTR(bank, 0xA000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0xA000]; 97 | MEM_PTR(bank, 0xC000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0xC000]; 98 | MEM_PTR(bank, 0xE000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0xE000]; 99 | } 100 | 101 | MEM_PTR(0x80 + bank, 0x0000) = hi_slow | MPTR_READONLY | (u32)&ptr[0x0000]; 102 | MEM_PTR(0x80 + bank, 0x2000) = hi_slow | MPTR_READONLY | (u32)&ptr[0x2000]; 103 | MEM_PTR(0x80 + bank, 0x4000) = hi_slow | MPTR_READONLY | (u32)&ptr[0x4000]; 104 | MEM_PTR(0x80 + bank, 0x6000) = hi_slow | MPTR_READONLY | (u32)&ptr[0x6000]; 105 | MEM_PTR(0x80 + bank, 0x8000) = hi_slow | MPTR_READONLY | (u32)&ptr[0x8000]; 106 | MEM_PTR(0x80 + bank, 0xA000) = hi_slow | MPTR_READONLY | (u32)&ptr[0xA000]; 107 | MEM_PTR(0x80 + bank, 0xC000) = hi_slow | MPTR_READONLY | (u32)&ptr[0xC000]; 108 | MEM_PTR(0x80 + bank, 0xE000) = hi_slow | MPTR_READONLY | (u32)&ptr[0xE000]; 109 | 110 | MEM_PTR(bank - 0x40, 0x8000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0x8000]; 111 | MEM_PTR(bank - 0x40, 0xA000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0xA000]; 112 | MEM_PTR(bank - 0x40, 0xC000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0xC000]; 113 | MEM_PTR(bank - 0x40, 0xE000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0xE000]; 114 | 115 | MEM_PTR(0x40 + bank, 0x8000) = hi_slow | MPTR_READONLY | (u32)&ptr[0x8000]; 116 | MEM_PTR(0x40 + bank, 0xA000) = hi_slow | MPTR_READONLY | (u32)&ptr[0xA000]; 117 | MEM_PTR(0x40 + bank, 0xC000) = hi_slow | MPTR_READONLY | (u32)&ptr[0xC000]; 118 | MEM_PTR(0x40 + bank, 0xE000) = hi_slow | MPTR_READONLY | (u32)&ptr[0xE000]; 119 | } 120 | } 121 | else 122 | { 123 | for (; bank < 0x80; bank += ROM_NumBanks) 124 | { 125 | if (bank >= 0x40 && bank < 0x70) 126 | { 127 | MEM_PTR(bank, 0x0000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0x0000]; 128 | MEM_PTR(bank, 0x2000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0x2000]; 129 | MEM_PTR(bank, 0x4000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0x4000]; 130 | MEM_PTR(bank, 0x6000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0x6000]; 131 | 132 | MEM_PTR(0x80 + bank, 0x0000) = hi_slow | MPTR_READONLY | (u32)&ptr[0x0000]; 133 | MEM_PTR(0x80 + bank, 0x2000) = hi_slow | MPTR_READONLY | (u32)&ptr[0x2000]; 134 | MEM_PTR(0x80 + bank, 0x4000) = hi_slow | MPTR_READONLY | (u32)&ptr[0x4000]; 135 | MEM_PTR(0x80 + bank, 0x6000) = hi_slow | MPTR_READONLY | (u32)&ptr[0x6000]; 136 | } 137 | 138 | if (bank < 0x7E) 139 | { 140 | MEM_PTR(bank, 0x8000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0x0000]; 141 | MEM_PTR(bank, 0xA000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0x2000]; 142 | MEM_PTR(bank, 0xC000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0x4000]; 143 | MEM_PTR(bank, 0xE000) = MPTR_SLOW | MPTR_READONLY | (u32)&ptr[0x6000]; 144 | } 145 | 146 | MEM_PTR(0x80 + bank, 0x8000) = hi_slow | MPTR_READONLY | (u32)&ptr[0x0000]; 147 | MEM_PTR(0x80 + bank, 0xA000) = hi_slow | MPTR_READONLY | (u32)&ptr[0x2000]; 148 | MEM_PTR(0x80 + bank, 0xC000) = hi_slow | MPTR_READONLY | (u32)&ptr[0x4000]; 149 | MEM_PTR(0x80 + bank, 0xE000) = hi_slow | MPTR_READONLY | (u32)&ptr[0x6000]; 150 | } 151 | } 152 | 153 | ROM_ApplySpeedHacks(bank, ptr); 154 | } 155 | 156 | int ROM_ScoreHeader(FILE *pFile, u32 offset) 157 | { 158 | if ((offset + 0x20) >= ROM_FileSize) 159 | return -1; 160 | 161 | int score = 0; 162 | int i; 163 | 164 | // 1. check opcodes at reset vector 165 | 166 | u16 resetvec; 167 | fseek(pFile, offset + 0x3C, SEEK_SET); 168 | fread(&resetvec, sizeof(char), 2, pFile); 169 | 170 | if (resetvec < 0x8000) // invalid reset vector, not likely to go anywhere with this header 171 | return -1; 172 | 173 | u32 firstops; 174 | fseek(pFile, (offset - 0x7FC0) + (resetvec - 0x8000), SEEK_SET); 175 | fread(&firstops, sizeof(char), 4, pFile); 176 | 177 | if ((firstops & 0xFFFFFF) == 0xFB1878) // typical SEI/CLC/XCE sequence 178 | score += 100; 179 | else if ((firstops & 0xFFFF) == 0xFB18) // CLC/XCE sequence 180 | score += 100; 181 | else if (firstops == 0xFB18D878) // SEI/CLD/CLC/XCE sequence 182 | score += 100; 183 | else if ((firstops & 0xFF) == 0x5C) // possible JML 184 | { 185 | // if a JML is used, chances are that it will go to the FastROM banks 186 | if (firstops >= 0x80000000) score += 90; 187 | else score += 80; 188 | } 189 | else // look for a more atypical sequence 190 | { 191 | u8 firstbytes[0x40]; 192 | *(u32*)&firstbytes[0] = firstops; 193 | fseek(pFile, (offset - 0x7FC0) + (resetvec - 0x8000) + 4, SEEK_SET); 194 | fread(&firstbytes[4], sizeof(char), 0x3C, pFile); 195 | 196 | for (i = 0; i < 0x3F; i++) 197 | { 198 | if (*(u16*)&firstbytes[i] == 0xFB18) 199 | { 200 | score += 90; 201 | break; 202 | } 203 | } 204 | } 205 | 206 | // 2. check the checksum 207 | 208 | u16 chksum, chkcomp; 209 | fseek(pFile, offset + 0x1C, SEEK_SET); 210 | fread(&chkcomp, sizeof(char), 2, pFile); 211 | fseek(pFile, offset + 0x1E, SEEK_SET); 212 | fread(&chksum, sizeof(char), 2, pFile); 213 | 214 | if ((chkcomp ^ chksum) == 0xFFFF) score += 50; 215 | 216 | // 3. check the characters in the title 217 | 218 | char title[21]; 219 | fseek(pFile, offset, SEEK_SET); 220 | fread(title, sizeof(char), 21, pFile); 221 | 222 | for (i = 0; i < 21; i++) 223 | { 224 | if (title[i] >= 0x20 && title[i] <= 0x7F) 225 | score++; 226 | } 227 | 228 | return score; 229 | } 230 | 231 | bool ROM_LoadFile(char* name) 232 | { 233 | FILE *pFile = fopen(name, "rb"); 234 | if (pFile == NULL) 235 | { 236 | bprintf("Error while opening file\n"); 237 | return false; 238 | } 239 | 240 | fseek(pFile, 0, SEEK_END); 241 | u32 size = ftell(pFile); 242 | if ((size < 16) || (size >= 0x8000000)) 243 | { 244 | fclose(pFile); 245 | bprintf("File size bad: size=%lld\n", size); 246 | return false; 247 | } 248 | ROM_FileSize = size; 249 | 250 | 251 | int bestone = 0; 252 | int score[4]; 253 | score[0] = ROM_ScoreHeader(pFile, 0x7FC0); 254 | score[1] = ROM_ScoreHeader(pFile, 0x81C0); 255 | score[2] = ROM_ScoreHeader(pFile, 0xFFC0); 256 | score[3] = ROM_ScoreHeader(pFile, 0x101C0); 257 | 258 | if (score[1] > score[0]) 259 | { 260 | score[0] = score[1]; 261 | bestone = 1; 262 | } 263 | if (score[2] > score[0]) 264 | { 265 | score[0] = score[2]; 266 | bestone = 2; 267 | } 268 | if (score[3] > score[0]) 269 | bestone = 3; 270 | 271 | if (bestone == 0 && score[0] < 0) 272 | { 273 | bprintf("Invalid ROM\n"); 274 | return false; 275 | } 276 | 277 | ROM_BaseOffset = (bestone & 1) ? 0x200 : 0; 278 | SNES_HiROM = (bestone & 2) ? true : false; 279 | ROM_HeaderOffset = SNES_HiROM ? 0xFFC0 : 0x7FC0; 280 | 281 | bprintf("ROM type: %s %s\n", (bestone & 1) ? "headered":"headerless", SNES_HiROM ? "HiROM":"LoROM"); 282 | 283 | size -= ROM_BaseOffset; 284 | 285 | u32 nbanks = (size + (SNES_HiROM ? 0xFFFF:0x7FFF)) >> (SNES_HiROM ? 16:15); 286 | ROM_NumBanks = 1; 287 | while (ROM_NumBanks < nbanks) ROM_NumBanks <<= 1; 288 | 289 | bprintf("ROM size: %dKB / %d banks\n", ((u32)size) >> 10, ROM_NumBanks); 290 | 291 | if (ROM_Buffer) 292 | { 293 | MemFree(ROM_Buffer); 294 | ROM_Buffer = NULL; 295 | } 296 | 297 | ROM_BufferSize = ROM_NumBanks << (SNES_HiROM ? 16:15); 298 | ROM_Buffer = (u8*)MemAlloc(ROM_BufferSize); 299 | if (!ROM_Buffer) 300 | { 301 | fclose(pFile); 302 | bprintf("Error while allocating ROM buffer\n"); 303 | return false; 304 | } 305 | 306 | fseek(pFile, ROM_BaseOffset, SEEK_SET); 307 | fread(ROM_Buffer, sizeof(char), size, pFile); 308 | fclose(pFile); 309 | 310 | u32 b = 0; 311 | u32 offset = 0; 312 | for (; b < ROM_NumBanks; b++) 313 | { 314 | ROM_MapBank((SNES_HiROM ? 0x40:0x00) + b, &ROM_Buffer[offset]); 315 | offset += (SNES_HiROM ? 0x10000:0x8000); 316 | } 317 | 318 | return true; 319 | } 320 | 321 | 322 | void ROM_SpeedChanged() 323 | { 324 | u32 b, a; 325 | 326 | if (SNES_FastROM) 327 | { 328 | bprintf("Fast ROM\n"); 329 | 330 | for (b = 0x80; b < 0xC0; b++) 331 | for (a = 0x8000; a < 0x10000; a += 0x2000) 332 | MEM_PTR(b, a) &= ~MPTR_SLOW; 333 | 334 | for (b = 0xC0; b < 0x100; b++) 335 | for (a = 0x0000; a < 0x10000; a += 0x2000) 336 | MEM_PTR(b, a) &= ~MPTR_SLOW; 337 | } 338 | else 339 | { 340 | bprintf("Slow ROM\n"); 341 | 342 | for (b = 0x80; b < 0xC0; b++) 343 | for (a = 0x8000; a < 0x10000; a += 0x2000) 344 | MEM_PTR(b, a) |= MPTR_SLOW; 345 | 346 | for (b = 0xC0; b < 0x100; b++) 347 | for (a = 0x0000; a < 0x10000; a += 0x2000) 348 | MEM_PTR(b, a) |= MPTR_SLOW; 349 | } 350 | } 351 | -------------------------------------------------------------------------------- /source/snes.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #include <3ds.h> 20 | #include 21 | #include 22 | #include 23 | 24 | #include "main.h" 25 | #include "mem.h" 26 | #include "snes.h" 27 | #include "cpu.h" 28 | #include "ppu.h" 29 | #include "spc700.h" 30 | 31 | 32 | u8* ROM_Bank0; 33 | u8* ROM_Bank0End; 34 | 35 | u8 ROM_Region; 36 | 37 | bool SNES_HiROM; 38 | u8 SNES_SysRAM[0x20000] __attribute__((aligned(256))); 39 | u32 SNES_SRAMMask; 40 | u8* SNES_SRAM = NULL; 41 | 42 | char SNES_SRAMPath[300]; 43 | 44 | // addressing: BBBBBBBB:AAAaaaaa:aaaaaaaa 45 | // bit4-31: argument 46 | // bit0: access speed (0 = 6 cycles, 1 = 8 cycles) 47 | // bit1: special bit (0 = argument is a RAM pointer, 1 = other case) 48 | // bit2: write permission (0 = can write, 1 = read-only) 49 | // bit3: SRAM bit 50 | // common cases: 51 | // * b1=0, b2=0: system RAM, SRAM; arg = pointer to RAM 52 | // * b1=1, b2=0: I/O, expansion RAM; arg = zero 53 | // * b1=0, b2=1: ROM; arg = pointer to RAM 54 | // 55 | // cheat: we place stuff before the start of the actual array-- those 56 | // can be accessed quickly by the CPU core since it keeps a pointer to 57 | // this table in one of the CPU registers 58 | // 59 | // table[-1] -> SRAM dirty flag 60 | // table[-2] -> HBlank/VBlank flags 61 | u32 _Mem_PtrTable[(SNESSTATUS_SIZE >> 2) + 0x800]; 62 | u32* Mem_PtrTable; 63 | SNES_StatusData* SNES_Status; 64 | 65 | u8 SNES_HVBJOY = 0x00; 66 | u8 SNES_WRIO = 0; 67 | 68 | u8 SNES_AutoJoypad = 0; 69 | u8 SNES_JoyBit = 0; 70 | u32 SNES_JoyBuffer = 0; 71 | u8 SNES_Joy16 = 0; 72 | 73 | u8 SNES_MulA = 0; 74 | u16 SNES_MulRes = 0; 75 | u16 SNES_DivA = 0; 76 | u16 SNES_DivRes = 0; 77 | 78 | bool SNES_FastROM = false; 79 | 80 | extern u8 DMA_HDMAFlag; 81 | extern u8 DMA_HDMACurFlag; 82 | extern u8 DMA_HDMAEnded; 83 | 84 | // execution trap 85 | // I/O regions are mapped to this buffer, so that when an accidental jump to those regions occurs, 86 | // we can trace it instead of just crashing 87 | #ifdef OPENBUS_EXEC_TRAP 88 | u8 SNES_ExecTrap[8192] __attribute__((aligned(256))); 89 | #else 90 | #define SNES_ExecTrap ((u8*)NULL) 91 | #endif 92 | 93 | 94 | void SNES_Init() 95 | { 96 | // TODO get rid of this junk! 97 | SNES_Status = (SNES_StatusData*)&_Mem_PtrTable[0]; 98 | Mem_PtrTable = &_Mem_PtrTable[SNESSTATUS_SIZE >> 2]; 99 | } 100 | 101 | 102 | bool SNES_LoadROM(char* path) 103 | { 104 | if (!ROM_LoadFile(path)) 105 | return false; 106 | 107 | ROM_Bank0 = ROM_Buffer; 108 | ROM_Bank0End = ROM_Bank0 + (SNES_HiROM ? 0x10000:0x8000); 109 | 110 | u8 sramsize = ROM_Buffer[ROM_HeaderOffset + 0x18]; 111 | u8 region = ROM_Buffer[ROM_HeaderOffset + 0x19]; 112 | 113 | if (region <= 0x01 || (region >= 0x0D && region <= 0x10)) 114 | ROM_Region = 0; 115 | else 116 | ROM_Region = 1; 117 | 118 | SNES_Status->TotalLines = (ROM_Region ? 312 : 262) >> 1; 119 | SNES_Status->ScreenHeight = 224; 120 | 121 | //SNES_Status->SPC_CycleRatio = ROM_Region ? 0x000C51D9 : 0x000C39C6; 122 | //SNES_Status->SPC_CycleRatio += 0x1000; // hax -- TODO investigate why we need this to run at a somewhat proper rate 123 | SNES_Status->SPC_CycleRatio = 6400;//6418;//6400;//ROM_Region ? 132990 : 134013; 124 | SNES_Status->SPC_CyclesPerLine = SNES_Status->SPC_CycleRatio * 1364; 125 | //SNES_Status->SPC_CyclesPerLine = ROM_Region ? 0x41A41A42 : 0x4123D3B5; 126 | 127 | SPC_CycleRatio = ROM_Region ? 132990 : 134013; 128 | 129 | SNES_SRAMMask = sramsize ? ((1024 << sramsize) - 1) : 0; 130 | SNES_SRAMMask &= 0x000FFFFF; 131 | bprintf("SRAM size: %dKB\n", (SNES_SRAMMask+1) >> 10); 132 | 133 | if (SNES_SRAMMask) 134 | { 135 | int pathlen = strlen(path); 136 | if (pathlen > 299) pathlen = 299; 137 | strncpy(SNES_SRAMPath, path, pathlen-3); 138 | strncpy(SNES_SRAMPath + pathlen-3, "srm", 4); 139 | 140 | FILE *pFile = fopen(SNES_SRAMPath, "rb+"); 141 | if (pFile == NULL) 142 | { 143 | pFile = fopen(SNES_SRAMPath, "wb"); 144 | if (pFile == NULL) 145 | bprintf("Error while trying to open the savefile.\nMake sure it isn't read-only.\n"); 146 | else 147 | { 148 | u8* temp = malloc(SNES_SRAMMask + 1); 149 | memset(temp, 0, SNES_SRAMMask + 1); 150 | fwrite(temp, sizeof(char), SNES_SRAMMask + 1, pFile); 151 | free(temp); 152 | } 153 | } 154 | if (pFile != NULL) 155 | fclose(pFile); 156 | } 157 | 158 | return true; 159 | } 160 | 161 | void SNES_Reset() 162 | { 163 | u32 i, a, b; 164 | 165 | // generate random garbage to fill the RAM with 166 | u64 t = osGetTime(); 167 | u32 randblarg = (u32)(t ^ (t >> 32ULL) ^ (t >> 19ULL) ^ (t << 7ULL) ^ (t >> 53ULL)); 168 | 169 | for (i = 0; i < (128 * 1024); i += 4) 170 | { 171 | *(u32*)&SNES_SysRAM[i] = randblarg ^ (randblarg << 15) ^ (randblarg << 26) ^ (randblarg * 0x00700000); 172 | randblarg = (randblarg * 0x17374) ^ (randblarg * 0x327) ^ (randblarg << 2) ^ (randblarg << 17); 173 | } 174 | 175 | // debug: make mainRAM predictable 176 | //memset(SNES_SysRAM, 0, 128*1024); 177 | 178 | // fill it with STP opcodes 179 | #ifdef OPENBUS_EXEC_TRAP 180 | memset(SNES_ExecTrap, 0xDB, 8192); 181 | #endif 182 | 183 | SNES_FastROM = false; 184 | 185 | DMA_HDMAFlag = 0; 186 | 187 | if (SNES_SRAM) 188 | { 189 | MemFree(SNES_SRAM); 190 | SNES_SRAM = NULL; 191 | } 192 | if (SNES_SRAMMask) 193 | { 194 | SNES_SRAM = (u8*)MemAlloc(SNES_SRAMMask + 1); 195 | for (i = 0; i <= SNES_SRAMMask; i += 4) 196 | *(u32*)&SNES_SRAM[i] = 0; 197 | 198 | FILE *pFile = fopen(SNES_SRAMPath, "rb"); 199 | if (pFile != NULL) 200 | { 201 | fread(SNES_SRAM, sizeof(char), SNES_SRAMMask + 1, pFile); 202 | fclose(pFile); 203 | } 204 | } 205 | 206 | SNES_Status->SRAMDirty = 0; 207 | SNES_Status->HVBFlags = 0x00; 208 | SNES_Status->SRAMMask = SNES_SRAMMask; 209 | SNES_Status->IRQCond = 0; 210 | 211 | SNES_Status->VCount = 0; 212 | SNES_Status->HCount = 0; 213 | SNES_Status->IRQ_VMatch = 0; 214 | SNES_Status->IRQ_HMatch = 0; 215 | SNES_Status->IRQ_CurHMatch = 0x8000; 216 | 217 | SNES_Status->SPC_LastCycle = 0; 218 | 219 | for (b = 0; b < 0x40; b++) 220 | { 221 | MEM_PTR(b, 0x0000) = MEM_PTR(0x80 + b, 0x0000) = MPTR_SLOW | (u32)&SNES_SysRAM[0]; 222 | MEM_PTR(b, 0x2000) = MEM_PTR(0x80 + b, 0x2000) = MPTR_SPECIAL | (u32)&SNES_ExecTrap[0]; 223 | MEM_PTR(b, 0x4000) = MEM_PTR(0x80 + b, 0x4000) = MPTR_SPECIAL | (u32)&SNES_ExecTrap[0]; 224 | 225 | if ((b >= 0x30) && SNES_HiROM && SNES_SRAMMask) 226 | MEM_PTR(b, 0x6000) = MEM_PTR(0x80 + b, 0x6000) = MPTR_SLOW | MPTR_SRAM | (u32)&SNES_SRAM[(b << 13) & SNES_SRAMMask]; 227 | else 228 | MEM_PTR(b, 0x6000) = MEM_PTR(0x80 + b, 0x6000) = MPTR_SLOW | MPTR_SPECIAL | (u32)&SNES_ExecTrap[0]; 229 | } 230 | 231 | if (SNES_HiROM) 232 | { 233 | for (b = 0; b < 0x02; b++) 234 | for (a = 0; a < 0x10000; a += 0x2000) 235 | MEM_PTR(0x7E + b, a) = MPTR_SLOW | (u32)&SNES_SysRAM[(b << 16) + a]; 236 | } 237 | else 238 | { 239 | if (SNES_SRAMMask) 240 | { 241 | for (b = 0; b < 0x0E; b++) 242 | for (a = 0; a < 0x8000; a += 0x2000) 243 | MEM_PTR(0x70 + b, a) = MEM_PTR(0xF0 + b, a) = MPTR_SLOW | MPTR_SRAM | (u32)&SNES_SRAM[((b << 15) + a) & SNES_SRAMMask]; 244 | for (a = 0; a < 0x8000; a += 0x2000) 245 | { 246 | MEM_PTR(0xFE + b, a) = MPTR_SLOW | MPTR_SRAM | (u32)&SNES_SRAM[((0xE << 15) + a) & SNES_SRAMMask]; 247 | MEM_PTR(0xFF + b, a) = MPTR_SLOW | MPTR_SRAM | (u32)&SNES_SRAM[((0xF << 15) + a) & SNES_SRAMMask]; 248 | } 249 | } 250 | else 251 | { 252 | for (b = 0; b < 0x0E; b++) 253 | for (a = 0; a < 0x8000; a += 0x2000) 254 | MEM_PTR(0x70 + b, a) = MEM_PTR(0xF0 + b, a) = MPTR_SLOW | MPTR_SPECIAL | (u32)&SNES_ExecTrap[0]; 255 | for (a = 0; a < 0x8000; a += 0x2000) 256 | { 257 | MEM_PTR(0xFE + b, a) = MPTR_SLOW | MPTR_SPECIAL | (u32)&SNES_ExecTrap[0]; 258 | MEM_PTR(0xFF + b, a) = MPTR_SLOW | MPTR_SPECIAL | (u32)&SNES_ExecTrap[0]; 259 | } 260 | } 261 | 262 | for (b = 0; b < 0x02; b++) 263 | for (a = 0; a < 0x10000; a += 0x2000) 264 | MEM_PTR(0x7E + b, a) = MEM_PTR(0xFE + b, a) = MPTR_SLOW | (u32)&SNES_SysRAM[(b << 16) + a]; 265 | } 266 | 267 | SNES_HVBJOY = 0x00; 268 | SNES_WRIO = 0; 269 | 270 | SNES_MulA = 0; 271 | SNES_MulRes = 0; 272 | SNES_DivA = 0; 273 | SNES_DivRes = 0; 274 | 275 | SNES_AutoJoypad = 0; 276 | SNES_JoyBit = 0; 277 | SNES_JoyBuffer = 0; 278 | SNES_Joy16 = 0; 279 | 280 | PPU_Reset(); 281 | } 282 | 283 | 284 | void SNES_SaveSRAM() 285 | { 286 | if (!SNES_SRAMMask) 287 | return; 288 | 289 | if (!SNES_Status->SRAMDirty) 290 | return; 291 | 292 | FILE *pFile = fopen(SNES_SRAMPath, "wb"); 293 | if (pFile != NULL) 294 | { 295 | fwrite(SNES_SRAM, sizeof(char), SNES_SRAMMask + 1, pFile); 296 | fclose(pFile); 297 | bprintf("SRAM saved\n"); 298 | } 299 | else 300 | bprintf("SRAM save failed\n"); 301 | 302 | SNES_Status->SRAMDirty = 0; 303 | } 304 | 305 | 306 | inline u8 IO_ReadKeysLow() 307 | { 308 | u32 keys = hidKeysHeld(); 309 | u8 ret = 0; 310 | 311 | if (keys & KEY_A) ret |= 0x80; 312 | if (keys & KEY_X) ret |= 0x40; 313 | if (keys & KEY_L) ret |= 0x20; 314 | if (keys & KEY_R) ret |= 0x10; 315 | 316 | return ret; 317 | } 318 | 319 | inline u8 IO_ReadKeysHigh() 320 | { 321 | u32 keys = hidKeysHeld(); 322 | u8 ret = 0; 323 | 324 | if (keys & KEY_B) ret |= 0x80; 325 | if (keys & KEY_Y) ret |= 0x40; 326 | if (keys & KEY_SELECT) ret |= 0x20; 327 | if (keys & KEY_START) ret |= 0x10; 328 | if (keys & KEY_UP) ret |= 0x08; 329 | if (keys & KEY_DOWN) ret |= 0x04; 330 | if (keys & KEY_LEFT) ret |= 0x02; 331 | if (keys & KEY_RIGHT) ret |= 0x01; 332 | 333 | return ret; 334 | } 335 | 336 | void IO_ManualReadKeys() 337 | { 338 | // normal joypad 339 | SNES_JoyBuffer = 0xFFFF0000 | IO_ReadKeysLow() | (IO_ReadKeysHigh() << 8); 340 | } 341 | 342 | 343 | void SNES_RescheduleIRQ(u8 val) 344 | { 345 | switch (val & 0x30) 346 | { 347 | case 0x00: SNES_Status->IRQ_CurHMatch = 0x8000; break; 348 | case 0x10: 349 | SNES_Status->IRQ_CurHMatch = (SNES_Status->HCount > SNES_Status->IRQ_HMatch) ? 0x8000:SNES_Status->IRQ_HMatch; 350 | break; 351 | case 0x20: 352 | SNES_Status->IRQ_CurHMatch = (SNES_Status->VCount != SNES_Status->IRQ_VMatch) ? 0x8000:0; 353 | break; 354 | case 0x30: 355 | SNES_Status->IRQ_CurHMatch = 356 | ((SNES_Status->VCount != SNES_Status->IRQ_VMatch) || 357 | (SNES_Status->HCount > SNES_Status->IRQ_HMatch)) 358 | ? 0x8000:SNES_Status->IRQ_HMatch; 359 | break; 360 | } 361 | } 362 | 363 | 364 | u8 SNES_GIORead8(u32 addr) 365 | { 366 | u8 ret = 0; 367 | 368 | switch (addr) 369 | { 370 | case 0x10: 371 | if (SNES_Status->HVBFlags & 0x20) 372 | { 373 | ret = 0x80; 374 | SNES_Status->HVBFlags &= 0xDF; 375 | } 376 | break; 377 | 378 | case 0x11: 379 | if (SNES_Status->HVBFlags & 0x10) 380 | { 381 | ret = 0x80; 382 | SNES_Status->HVBFlags &= 0xEF; 383 | } 384 | break; 385 | 386 | case 0x12: 387 | ret = SNES_Status->HVBFlags & 0x80; 388 | if (SNES_Status->HCount >= 1024) ret |= 0x40; 389 | break; 390 | 391 | case 0x14: 392 | ret = SNES_DivRes & 0xFF; 393 | break; 394 | case 0x15: 395 | ret = SNES_DivRes >> 8; 396 | break; 397 | 398 | case 0x16: 399 | ret = SNES_MulRes & 0xFF; 400 | break; 401 | case 0x17: 402 | ret = SNES_MulRes >> 8; 403 | break; 404 | 405 | case 0x18: 406 | ret = SNES_JoyBuffer & 0xFF; 407 | break; 408 | case 0x19: 409 | ret = (SNES_JoyBuffer >> 8) & 0xFF; 410 | break; 411 | 412 | case 0x13: 413 | case 0x1A: 414 | case 0x1B: 415 | case 0x1C: 416 | case 0x1D: 417 | case 0x1E: 418 | case 0x1F: 419 | // unimplemented 420 | break; 421 | 422 | default: // open bus 423 | ret = SNES_Status->LastBusVal; 424 | break; 425 | } 426 | 427 | return ret; 428 | } 429 | 430 | u16 SNES_GIORead16(u32 addr) 431 | { 432 | u16 ret = 0; 433 | switch (addr) 434 | { 435 | case 0x14: 436 | ret = SNES_DivRes; 437 | break; 438 | 439 | case 0x16: 440 | ret = SNES_MulRes; 441 | break; 442 | 443 | case 0x18: 444 | ret = SNES_JoyBuffer & 0xFFFF; 445 | break; 446 | 447 | default: 448 | ret = SNES_GIORead8(addr); 449 | ret |= (SNES_GIORead8(addr + 1) << 8); 450 | break; 451 | } 452 | 453 | return ret; 454 | } 455 | 456 | void SNES_GIOWrite8(u32 addr, u8 val) 457 | { 458 | switch (addr) 459 | { 460 | case 0x00: 461 | if ((SNES_Status->IRQCond ^ val) & 0x30) // reschedule the IRQ if needed 462 | SNES_RescheduleIRQ(val); 463 | if (!(val & 0x30)) // acknowledge current IRQ if needed 464 | SNES_Status->HVBFlags &= 0xEF; 465 | SNES_Status->IRQCond = val; 466 | SNES_AutoJoypad = (val & 0x01); 467 | break; 468 | 469 | case 0x01: 470 | if ((SNES_WRIO & ~val) & 0x80) PPU_LatchHVCounters(); 471 | SNES_WRIO = val; 472 | break; 473 | 474 | case 0x02: 475 | SNES_MulA = val; 476 | break; 477 | case 0x03: 478 | SNES_MulRes = (u16)SNES_MulA * (u16)val; 479 | SNES_DivRes = (u16)val; 480 | break; 481 | 482 | case 0x04: 483 | SNES_DivA = (SNES_DivA & 0xFF00) | val; 484 | break; 485 | case 0x05: 486 | SNES_DivA = (SNES_DivA & 0x00FF) | (val << 8); 487 | break; 488 | case 0x06: 489 | { 490 | if (val == 0) 491 | { 492 | SNES_DivRes = 0xFFFF; 493 | SNES_MulRes = SNES_DivA; 494 | } 495 | else 496 | { 497 | SNES_DivRes = (u16)(SNES_DivA / val); 498 | SNES_MulRes = (u16)(SNES_DivA % val); 499 | } 500 | } 501 | break; 502 | 503 | case 0x07: 504 | SNES_Status->IRQ_HMatch &= 0x0400; 505 | SNES_Status->IRQ_HMatch |= (val << 2); 506 | if (SNES_Status->IRQCond & 0x10) SNES_RescheduleIRQ(SNES_Status->IRQCond); 507 | break; 508 | case 0x08: 509 | SNES_Status->IRQ_HMatch &= 0x03FC; 510 | SNES_Status->IRQ_HMatch |= ((val & 0x01) << 10); 511 | if (SNES_Status->IRQCond & 0x10) SNES_RescheduleIRQ(SNES_Status->IRQCond); 512 | break; 513 | 514 | case 0x09: 515 | SNES_Status->IRQ_VMatch &= 0x0100; 516 | SNES_Status->IRQ_VMatch |= val; 517 | if (SNES_Status->IRQCond & 0x20) SNES_RescheduleIRQ(SNES_Status->IRQCond); 518 | break; 519 | case 0x0A: 520 | SNES_Status->IRQ_VMatch &= 0x00FF; 521 | SNES_Status->IRQ_VMatch |= ((val & 0x01) << 8); 522 | if (SNES_Status->IRQCond & 0x20) SNES_RescheduleIRQ(SNES_Status->IRQCond); 523 | break; 524 | 525 | case 0x0B: 526 | DMA_Enable(val); 527 | break; 528 | case 0x0C: 529 | DMA_HDMAFlag = val; 530 | DMA_HDMACurFlag = val & ~DMA_HDMAEnded; 531 | break; 532 | 533 | case 0x0D: 534 | { 535 | bool fast = (val & 0x01); 536 | if (fast ^ SNES_FastROM) 537 | { 538 | SNES_FastROM = fast; 539 | ROM_SpeedChanged(); 540 | } 541 | } 542 | break; 543 | } 544 | } 545 | 546 | void SNES_GIOWrite16(u32 addr, u16 val) 547 | { 548 | switch (addr) 549 | { 550 | case 0x02: 551 | SNES_MulA = val & 0xFF; 552 | SNES_MulRes = (u16)SNES_MulA * (val >> 8); 553 | SNES_DivRes = (u16)val; 554 | break; 555 | 556 | case 0x04: 557 | SNES_DivA = val; 558 | break; 559 | 560 | case 0x07: 561 | SNES_Status->IRQ_HMatch = (val & 0x01FF) << 2; 562 | if (SNES_Status->IRQCond & 0x10) SNES_RescheduleIRQ(SNES_Status->IRQCond); 563 | break; 564 | 565 | case 0x09: 566 | SNES_Status->IRQ_VMatch = val & 0x01FF; 567 | if (SNES_Status->IRQCond & 0x20) SNES_RescheduleIRQ(SNES_Status->IRQCond); 568 | break; 569 | 570 | case 0x0B: 571 | DMA_Enable(val & 0xFF); 572 | DMA_HDMAFlag = val >> 8; 573 | DMA_HDMACurFlag = DMA_HDMAFlag & ~DMA_HDMAEnded; 574 | break; 575 | 576 | default: 577 | SNES_GIOWrite8(addr, val & 0xFF); 578 | SNES_GIOWrite8(addr + 1, val >> 8); 579 | break; 580 | } 581 | } 582 | 583 | 584 | u8 SNES_JoyRead8(u32 addr) 585 | { 586 | u8 ret = 0; 587 | 588 | if (addr == 0x16) 589 | { 590 | if (SNES_Joy16 & 0x01) 591 | { 592 | // Return Controller connected status (to which Pad 1 is always connected and Pad 3 is not, Pad 2/4 are linked to 4017h, but neither are connected) 593 | ret = 0x1; 594 | } 595 | else 596 | { 597 | if (SNES_JoyBit == 0) IO_ManualReadKeys(); 598 | 599 | if (SNES_JoyBit < 16) 600 | { 601 | ret = (SNES_JoyBuffer >> (SNES_JoyBit ^ 15)) & 1; 602 | SNES_JoyBit++; 603 | } 604 | else 605 | ret = 0x1; 606 | } 607 | ret = 0x01; 608 | } 609 | else if (addr != 0x17) 610 | ret = SNES_Status->LastBusVal; 611 | 612 | return ret; 613 | } 614 | 615 | u16 SNES_JoyRead16(u32 addr) 616 | { 617 | return SNES_JoyRead8(addr) | (SNES_JoyRead8(addr+1) << 8); 618 | } 619 | 620 | void SNES_JoyWrite8(u32 addr, u8 val) 621 | { 622 | if (addr == 0x16) 623 | { 624 | if (!(SNES_Joy16 & 0x01) && (val & 0x01)) 625 | SNES_JoyBit = 0; 626 | 627 | SNES_Joy16 = val; 628 | } 629 | } 630 | 631 | void SNES_JoyWrite16(u32 addr, u16 val) 632 | { 633 | SNES_JoyWrite8(addr, val&0xFF); 634 | SNES_JoyWrite8(addr+1, val>>8); 635 | } 636 | 637 | 638 | // this used for DMA 639 | // I/O only available for 4210-421F, they say 640 | 641 | u8 SNES_Read8(u32 addr) 642 | { 643 | u32 ptr = Mem_PtrTable[addr >> 13]; 644 | if (ptr & MPTR_SPECIAL) 645 | { 646 | if ((addr & 0xFFF0) != 0x4210) 647 | return 0xFF; 648 | 649 | return SNES_IORead8(addr); 650 | } 651 | else 652 | { 653 | u8* mptr = (u8*)(ptr & 0xFFFFFFF0); 654 | return mptr[addr & 0x1FFF]; 655 | } 656 | } 657 | 658 | u16 SNES_Read16(u32 addr) 659 | { 660 | u32 ptr = Mem_PtrTable[addr >> 13]; 661 | if (ptr & MPTR_SPECIAL) 662 | { 663 | if ((addr & 0xFFF0) != 0x4210) 664 | return 0xFFFF; 665 | 666 | return SNES_IORead16(addr); 667 | } 668 | else 669 | { 670 | u8* mptr = (u8*)(ptr & 0xFFFFFFF0); 671 | addr &= 0x1FFF; 672 | return mptr[addr] | (mptr[addr + 1] << 8); 673 | } 674 | } 675 | 676 | void SNES_Write8(u32 addr, u8 val) 677 | { 678 | u32 ptr = Mem_PtrTable[addr >> 13]; 679 | if (ptr & MPTR_READONLY) return; 680 | if (ptr & MPTR_SPECIAL) 681 | { 682 | // CHECKME: what are the writable ranges with DMA? 683 | if ((addr & 0xFFF0) != 0x4000) 684 | SNES_IOWrite8(addr, val); 685 | } 686 | else 687 | { 688 | u8* mptr = (u8*)(ptr & 0xFFFFFFF0); 689 | mptr[addr & 0x1FFF] = val; 690 | } 691 | } 692 | 693 | void SNES_Write16(u32 addr, u8 val) 694 | { 695 | u32 ptr = Mem_PtrTable[addr >> 13]; 696 | if (ptr & MPTR_READONLY) return; 697 | if (ptr & MPTR_SPECIAL) 698 | { 699 | // CHECKME: what are the writable ranges with DMA? 700 | if ((addr & 0xFFF0) != 0x4000) 701 | SNES_IOWrite16(addr, val); 702 | } 703 | else 704 | { 705 | u8* mptr = (u8*)(ptr & 0xFFFFFFF0); 706 | addr &= 0x1FFF; 707 | mptr[addr] = val & 0xFF; 708 | mptr[addr + 1] = val >> 8; 709 | } 710 | } 711 | -------------------------------------------------------------------------------- /source/snes.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #ifndef _SNES_H_ 20 | #define _SNES_H_ 21 | 22 | #include 23 | #include <3ds/types.h> 24 | 25 | typedef struct 26 | { 27 | u8 __pad3[2]; // -40 28 | u8 LastBusVal; // -38 29 | u8 __pad2; // -37 30 | 31 | s32 SPC_CyclesPerLine; // -36 | cycleratio * 1364 32 | s32 SPC_CycleRatio; // -32 33 | s32 SPC_LastCycle; // -28 | SPC cycle count (<<24) at last SPC run 34 | 35 | u16 IRQ_VMatch; // -24 36 | u16 IRQ_HMatch; // -22 37 | u16 IRQ_CurHMatch; // -20 | reset when the IRQ is fired 38 | 39 | u16 VCount; // -18 40 | 41 | u8 __pad1[2]; // -16 for 'full' HCount (lower 16 bits are garbage) 42 | u16 HCount; // -14 43 | 44 | u32 SRAMMask; // -0xC 45 | 46 | u8 TotalLines; // -8 | 262 for NTSC, 312 for PAL 47 | u8 ScreenHeight; // -7 | 224 or 239 48 | 49 | u8 IRQCond; // -0x6 50 | 51 | // bit7: vblank 52 | // bit6: hblank (retired) 53 | // bit5: vblank (ack) 54 | // bit4: IRQ (ack) 55 | u8 HVBFlags; // -0x5 56 | 57 | u32 SRAMDirty; // -0x4 58 | 59 | } SNES_StatusData; 60 | 61 | extern u8 SNES_AutoJoypad; 62 | extern u8 SNES_JoyBit; 63 | extern u32 SNES_JoyBuffer; 64 | extern u8 SNES_Joy16; 65 | 66 | #define SNESSTATUS_SIZE ((sizeof(SNES_StatusData) + 3) & ~3) 67 | 68 | #define MEM_PTR(b, a) Mem_PtrTable[((b) << 3) | ((a) >> 13)] 69 | 70 | #define MPTR_SLOW (1 << 0) 71 | #define MPTR_SPECIAL (1 << 1) 72 | #define MPTR_READONLY (1 << 2) 73 | #define MPTR_SRAM (1 << 3) 74 | 75 | extern u32 ROM_BaseOffset; 76 | extern u8* ROM_Buffer; 77 | extern u32 ROM_HeaderOffset; 78 | 79 | extern u8* ROM_Bank0; 80 | extern u8* ROM_Bank0End; 81 | 82 | extern u8 ROM_Region; 83 | 84 | extern bool SNES_HiROM; 85 | extern bool SNES_FastROM; 86 | extern u32* Mem_PtrTable; 87 | extern SNES_StatusData* SNES_Status; 88 | 89 | extern u8 SNES_SysRAM[0x20000]; 90 | 91 | extern u8 SNES_WRIO; 92 | 93 | extern u8 SPC_IOPorts[8]; 94 | 95 | 96 | bool ROM_LoadFile(char* name); 97 | void ROM_MapBank(u32 bank, u8* ptr); 98 | void ROM_SpeedChanged(); 99 | 100 | void IO_ManualReadKeys(); 101 | 102 | void SNES_Init(); 103 | 104 | bool SNES_LoadROM(char* path); 105 | void SNES_Reset(); 106 | 107 | void SNES_SaveSRAM(); 108 | 109 | u8 SNES_IORead8(u32 addr); 110 | u16 SNES_IORead16(u32 addr); 111 | void SNES_IOWrite8(u32 addr, u32 val); 112 | void SNES_IOWrite16(u32 addr, u32 val); 113 | 114 | void report_unk_lol(u32 op, u32 pc); 115 | void reportBRK(u32 pc); 116 | 117 | u8 SNES_GIORead8(u32 addr); 118 | u16 SNES_GIORead16(u32 addr); 119 | void SNES_GIOWrite8(u32 addr, u8 val); 120 | void SNES_GIOWrite16(u32 addr, u16 val); 121 | 122 | u8 SNES_JoyRead8(u32 addr); 123 | u16 SNES_JoyRead16(u32 addr); 124 | void SNES_JoyWrite8(u32 addr, u8 val); 125 | void SNES_JoyWrite16(u32 addr, u16 val); 126 | 127 | u8 DMA_Read8(u32 addr); 128 | u16 DMA_Read16(u32 addr); 129 | void DMA_Write8(u32 addr, u8 val); 130 | void DMA_Write16(u32 addr, u16 val); 131 | void DMA_Enable(u8 flag); 132 | 133 | u8 SNES_Read8(u32 addr); 134 | u16 SNES_Read16(u32 addr); 135 | void SNES_Write8(u32 addr, u8 val); 136 | void SNES_Write16(u32 addr, u8 val); 137 | 138 | void SPC_IORead(int side); 139 | void SPC_IOWrite(int side); 140 | void SPC_IOWriteDone(int side); 141 | 142 | void bprintf(char* fmt, ...); 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /source/spc700.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #ifndef _SPC700_H_ 20 | #define _SPC700_H_ 21 | 22 | typedef union 23 | { 24 | u32 Val; 25 | struct 26 | { 27 | u16 LowPart; 28 | u16 HighPart; 29 | }; 30 | 31 | } SPC_Timer; 32 | 33 | extern u8 SPC_RAM[0x10040]; 34 | 35 | extern u32 SPC_ElapsedCycles; 36 | extern u32 SPC_CycleRatio; 37 | 38 | extern u8 SPC_TimerEnable; 39 | extern u32 SPC_TimerReload[3]; 40 | extern SPC_Timer SPC_TimerVal[3]; 41 | 42 | typedef struct 43 | { 44 | u32 _memoryMap; 45 | s32 nCycles; 46 | u16 PSW; 47 | u16 PC; 48 | u32 SP; 49 | u32 Y; 50 | u32 X; 51 | u32 A; 52 | } SPC_Regs_t; 53 | 54 | extern SPC_Regs_t SPC_Regs; 55 | 56 | extern u8 SPC_ROM[0x40]; 57 | 58 | 59 | void SPC_Reset(); 60 | void SPC_Run(int cycles); 61 | 62 | void SPC_InitMisc(); 63 | 64 | u8 SPC_IORead8(u16 addr); 65 | u16 SPC_IORead16(u16 addr); 66 | void SPC_IOWrite8(u16 addr, u8 val); 67 | void SPC_IOWrite16(u16 addr, u16 val); 68 | 69 | 70 | void DSP_Reset(); 71 | 72 | void DSP_Mix(); 73 | 74 | u8 DSP_Read(u8 reg); 75 | void DSP_Write(u8 reg, u8 val); 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /source/spc700.inc: -------------------------------------------------------------------------------- 1 | @ ----------------------------------------------------------------------------- 2 | @ Copyright 2014-2022 Arisotura 3 | @ 4 | @ This file is part of blargSnes. 5 | @ 6 | @ blargSnes is free software: you can redistribute it and/or modify it under 7 | @ the terms of the GNU General Public License as published by the Free 8 | @ Software Foundation, either version 3 of the License, or (at your option) 9 | @ any later version. 10 | @ 11 | @ blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | @ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | @ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | @ 15 | @ You should have received a copy of the GNU General Public License along 16 | @ with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | @ ----------------------------------------------------------------------------- 18 | 19 | @ --- Register aliases -------------------------------------------------------- 20 | 21 | spcA .req r11 22 | spcX .req r10 23 | spcY .req r9 24 | spcSP .req r8 25 | spcPC .req r7 @ high hword: PC 26 | spcPSW .req r7 @ low hword: PSW 27 | spcCycles .req r6 @ SPC700 cycle count 28 | memory .req r5 @ pointer to the memory 29 | 30 | @ --- Variables and whatever -------------------------------------------------- 31 | 32 | .equ flagC, 0x01 33 | .equ flagZ, 0x02 34 | .equ flagI, 0x04 @ interrupt enable (unused) 35 | .equ flagH, 0x08 @ half carry 36 | .equ flagB, 0x10 @ break (unused) 37 | .equ flagP, 0x20 @ direct page 38 | .equ flagV, 0x40 39 | .equ flagN, 0x80 40 | .equ flagR, 0x100 @ ROM access flag (0 = RAM, 1 = ROM) 41 | .equ flagPause, 0x200 42 | 43 | .equ flagNVHZC, 0xCB 44 | .equ flagNVZC, 0xC3 45 | .equ flagNZ, 0x82 46 | .equ flagNZC, 0x83 47 | .equ flagNVZ, 0xC2 48 | .equ flagVH, 0x48 49 | .equ flagNVH, 0xC8 50 | .equ flagNVHZ, 0xCA 51 | .equ flagZC, 0x03 52 | -------------------------------------------------------------------------------- /source/spc700io.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #include <3ds/types.h> 20 | #include 21 | 22 | #include "snes.h" 23 | #include "spc700.h" 24 | #include "dsp.h" 25 | 26 | 27 | u8 SPC_ROMAccess; 28 | u8 SPC_DSPAddr; 29 | 30 | 31 | void SPC_InitMisc() 32 | { 33 | SPC_ROMAccess = 1; 34 | SPC_DSPAddr = 0; 35 | 36 | memset(&SPC_RAM[0], 0, 0x10040); 37 | memcpy(&SPC_RAM[0xFFC0], &SPC_ROM[0], 64); 38 | 39 | *(u32*)&SPC_IOPorts[0] = 0; 40 | *(u32*)&SPC_IOPorts[4] = 0; 41 | 42 | SPC_TimerEnable = 0; 43 | SPC_TimerReload[0] = 0; 44 | SPC_TimerReload[1] = 0; 45 | SPC_TimerReload[2] = 0; 46 | SPC_TimerVal[0].Val = 0; 47 | SPC_TimerVal[1].Val = 0; 48 | SPC_TimerVal[2].Val = 0; 49 | 50 | SPC_ElapsedCycles = 0; 51 | 52 | DspReset(); 53 | } 54 | 55 | u8 SPC_IORead8(u16 addr) 56 | { 57 | u8 ret = 0; 58 | switch (addr) 59 | { 60 | case 0xF2: ret = SPC_DSPAddr; break; 61 | case 0xF3: ret = DSP_MEM[SPC_DSPAddr]; break; 62 | 63 | case 0xF4: ret = SPC_IOPorts[0]; break; 64 | case 0xF5: ret = SPC_IOPorts[1]; break; 65 | case 0xF6: ret = SPC_IOPorts[2]; break; 66 | case 0xF7: ret = SPC_IOPorts[3]; break; 67 | 68 | case 0xFD: ret = SPC_TimerVal[0].HighPart & 0x0F; SPC_TimerVal[0].HighPart = 0; break; 69 | case 0xFE: ret = SPC_TimerVal[1].HighPart & 0x0F; SPC_TimerVal[1].HighPart = 0; break; 70 | case 0xFF: ret = SPC_TimerVal[2].HighPart & 0x0F; SPC_TimerVal[2].HighPart = 0; break; 71 | } 72 | 73 | return ret; 74 | } 75 | 76 | u16 SPC_IORead16(u16 addr) 77 | { 78 | u16 ret = 0; 79 | switch (addr) 80 | { 81 | case 0xF4: ret = *(u16*)&SPC_IOPorts[0]; break; 82 | case 0xF6: ret = *(u16*)&SPC_IOPorts[2]; break; 83 | 84 | default: 85 | ret = SPC_IORead8(addr); 86 | ret |= ((u16)SPC_IORead8(addr+1) << 8); 87 | break; 88 | } 89 | 90 | return ret; 91 | } 92 | 93 | void SPC_IOWrite8(u16 addr, u8 val) 94 | { 95 | switch (addr) 96 | { 97 | case 0xF0: 98 | if (val != 0x0A) bprintf("!! SPC CONFIG F0 = %02X\n", val); 99 | break; 100 | 101 | case 0xF1: 102 | { 103 | SPC_TimerEnable = val & 0x07; 104 | 105 | if (!(val & 0x01)) 106 | SPC_TimerVal[0].Val = 0; 107 | else 108 | SPC_TimerVal[0].Val = SPC_TimerReload[0]; 109 | 110 | if (!(val & 0x02)) 111 | SPC_TimerVal[1].Val = 0; 112 | else 113 | SPC_TimerVal[1].Val = SPC_TimerReload[1]; 114 | 115 | if (!(val & 0x04)) 116 | SPC_TimerVal[2].Val = 0; 117 | else 118 | SPC_TimerVal[2].Val = SPC_TimerReload[2]; 119 | 120 | if (val & 0x10) *(u16*)&SPC_IOPorts[0] = 0x0000; 121 | if (val & 0x20) *(u16*)&SPC_IOPorts[2] = 0x0000; 122 | 123 | SPC_ROMAccess = (val & 0x80) ? 1:0; 124 | } 125 | break; 126 | 127 | case 0xF2: SPC_DSPAddr = val; break; 128 | case 0xF3: DspWriteByte(val, SPC_DSPAddr); break; 129 | 130 | case 0xF4: SPC_IOPorts[4] = val; break; 131 | case 0xF5: SPC_IOPorts[5] = val; break; 132 | case 0xF6: SPC_IOPorts[6] = val; break; 133 | case 0xF7: SPC_IOPorts[7] = val; break; 134 | 135 | case 0xF8: 136 | case 0xF9: 137 | if (val) bprintf("what?\n"); 138 | break; 139 | 140 | case 0xFA: SPC_TimerReload[0] = 0x10000 - (val << 7); break; 141 | case 0xFB: SPC_TimerReload[1] = 0x10000 - (val << 7); break; 142 | case 0xFC: SPC_TimerReload[2] = 0x10000 - (val << 4); break; 143 | } 144 | } 145 | 146 | void SPC_IOWrite16(u16 addr, u16 val) 147 | { 148 | switch (addr) 149 | { 150 | case 0xF4: *(u16*)&SPC_IOPorts[4] = val; break; 151 | case 0xF6: *(u16*)&SPC_IOPorts[6] = val; break; 152 | 153 | default: 154 | SPC_IOWrite8(addr, val & 0xFF); 155 | SPC_IOWrite8(addr+1, val >> 8); 156 | break; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /source/superfasthash.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2010, Paul Hsieh 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright notice, this 8 | // list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright notice, 10 | // this list of conditions and the following disclaimer in the documentation 11 | // and/or other materials provided with the distribution. 12 | // * Neither my name, Paul Hsieh, nor the names of any other contributors to the 13 | // code use may not be used to endorse or promote products derived from this 14 | // software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | 28 | #include <3ds.h> 29 | 30 | u32 SuperFastHash(const u8* data, u32 len) 31 | { 32 | u32 hash = len, tmp; 33 | 34 | len >>= 2; 35 | /* Main loop */ 36 | for (; len > 0; len--) 37 | { 38 | hash += *(u16*)&data[0]; 39 | tmp = (*(u16*)&data[2] << 11) ^ hash; 40 | hash = (hash << 16) ^ tmp; 41 | data += 4; 42 | hash += hash >> 11; 43 | } 44 | 45 | /* Force "avalanching" of final 127 bits */ 46 | hash ^= hash << 3; 47 | hash += hash >> 5; 48 | hash ^= hash << 4; 49 | hash += hash >> 17; 50 | hash ^= hash << 25; 51 | hash += hash >> 6; 52 | return hash; 53 | } 54 | 55 | u32 SuperFastPalHash(const u8* data, u32 len) 56 | { 57 | u32 hash = len, tmp; 58 | 59 | len >>= 2; 60 | 61 | /* skip first halfword (palette entry 0, transparent) */ 62 | tmp = (*(u16*)&data[2] << 11) ^ hash; 63 | hash = (hash << 16) ^ tmp; 64 | data += 4; 65 | hash += hash >> 11; 66 | len--; 67 | 68 | /* Main loop */ 69 | for (; len > 0; len--) 70 | { 71 | hash += *(u16*)&data[0]; 72 | tmp = (*(u16*)&data[2] << 11) ^ hash; 73 | hash = (hash << 16) ^ tmp; 74 | data += 4; 75 | hash += hash >> 11; 76 | } 77 | 78 | /* Force "avalanching" of final 127 bits */ 79 | hash ^= hash << 3; 80 | hash += hash >> 5; 81 | hash ^= hash << 4; 82 | hash += hash >> 17; 83 | hash ^= hash << 25; 84 | hash += hash >> 6; 85 | return hash; 86 | } 87 | -------------------------------------------------------------------------------- /source/ui.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | // ui.c -- generic bottomscreen UI functions 20 | 21 | #include <3ds.h> 22 | #include "ui.h" 23 | #include "font.h" 24 | 25 | 26 | UIController* CurrentUI = NULL; 27 | 28 | #define UISTACK_SIZE 16 29 | UIController* UIStack[UISTACK_SIZE]; 30 | u32 UIStackPointer = 0; 31 | 32 | extern int forceexit; 33 | 34 | void UI_Switch(UIController* ui) 35 | { 36 | if (CurrentUI) 37 | (*CurrentUI->DeInit)(); 38 | 39 | CurrentUI = ui; 40 | (*CurrentUI->Init)(); 41 | } 42 | 43 | void UI_SaveAndSwitch(UIController* ui) 44 | { 45 | if (UIStackPointer >= UISTACK_SIZE) 46 | { 47 | bprintf("!! UI STACK FULL\n"); 48 | return; 49 | } 50 | 51 | UIStack[UIStackPointer] = CurrentUI; 52 | UIStackPointer++; 53 | 54 | CurrentUI = ui; 55 | (*CurrentUI->Init)(); 56 | } 57 | 58 | void UI_Restore() 59 | { 60 | if (UIStackPointer < 1) 61 | { 62 | bprintf("!! UI STACK EMPTY\n"); 63 | return; 64 | } 65 | 66 | (*CurrentUI->DeInit)(); 67 | 68 | UIStackPointer--; 69 | CurrentUI = UIStack[UIStackPointer]; 70 | (*CurrentUI->Render)(true); 71 | } 72 | 73 | int UI_Level() 74 | { 75 | return UIStackPointer; 76 | } 77 | 78 | 79 | void UI_Render() 80 | { 81 | (*CurrentUI->Render)(false); 82 | } 83 | 84 | void UI_ButtonPress(u32 btn) 85 | { 86 | (*CurrentUI->ButtonPress)(btn); 87 | } 88 | 89 | void UI_Touch(int touch, u32 x, u32 y) 90 | { 91 | (*CurrentUI->Touch)(touch, x, y); 92 | } 93 | 94 | 95 | u8* BottomFB; 96 | 97 | #define CLAMP(x, min, max) if (x < min) x = min; else if (x > max) x = max; 98 | 99 | #define DRAW_PIXEL(x, y, color) \ 100 | { \ 101 | int idx = ((x)*240) + (239-(y)); \ 102 | BottomFB[idx*3+0] = (color); \ 103 | BottomFB[idx*3+1] = (color) >> 8; \ 104 | BottomFB[idx*3+2] = (color) >> 16; \ 105 | } 106 | 107 | 108 | void UI_SetFramebuffer(u8* buffer) 109 | { 110 | BottomFB = buffer; 111 | } 112 | 113 | void DrawRect(int x1, int x2, int y1, int y2, u32 color) 114 | { 115 | int x, y; 116 | 117 | CLAMP(x1, 0, 319); CLAMP(x2, 0, 319); 118 | CLAMP(y1, 0, 239); CLAMP(y2, 0, 239); 119 | 120 | for (x = x1; x <= x2; x++) 121 | { 122 | DRAW_PIXEL(x, y1, color); 123 | DRAW_PIXEL(x, y2, color); 124 | } 125 | 126 | for (y = y1; y <= y2; y++) 127 | { 128 | DRAW_PIXEL(x1, y, color); 129 | DRAW_PIXEL(x2, y, color); 130 | } 131 | } 132 | 133 | void DrawRectOutline(int x1, int x2, int y1, int y2, u32 colorin, u32 colorout) 134 | { 135 | int x, y; 136 | 137 | CLAMP(x1, 1, 318); CLAMP(x2, 1, 318); 138 | CLAMP(y1, 1, 238); CLAMP(y2, 1, 238); 139 | 140 | for (x = x1; x <= x2; x++) 141 | { 142 | DRAW_PIXEL(x, y1-1, colorout); 143 | DRAW_PIXEL(x, y1, colorin); 144 | DRAW_PIXEL(x, y2, colorin); 145 | DRAW_PIXEL(x, y2+1, colorout); 146 | } 147 | 148 | for (y = y1; y <= y2; y++) 149 | { 150 | DRAW_PIXEL(x1-1, y, colorout); 151 | DRAW_PIXEL(x1, y, colorin); 152 | DRAW_PIXEL(x2, y, colorin); 153 | DRAW_PIXEL(x2+1, y, colorout); 154 | } 155 | } 156 | 157 | void FillRect(int x1, int x2, int y1, int y2, u32 color) 158 | { 159 | int x, y; 160 | 161 | CLAMP(x1, 0, 319); CLAMP(x2, 0, 319); 162 | CLAMP(y1, 0, 239); CLAMP(y2, 0, 239); 163 | 164 | for (y = y1; y <= y2; y++) 165 | { 166 | for (x = x1; x <= x2; x++) 167 | { 168 | DRAW_PIXEL(x, y, color); 169 | } 170 | } 171 | } 172 | 173 | void ClearFramebuffer() 174 | { 175 | FillRect(0, 319, 0, 239, RGB(0,0,32)); 176 | } 177 | 178 | void ClearFramebufferWithColor(u32 color) 179 | { 180 | FillRect(0, 319, 0, 239, color); 181 | } 182 | 183 | inline int MeasureCharacter(char ch) 184 | { 185 | if (ch < 0x10 || ch > 0x7E) ch = 0x7F; 186 | 187 | u16 glyphsize = font[(ch-0x10) << 4]; 188 | 189 | if (!glyphsize) return 6; 190 | return glyphsize+2; 191 | } 192 | 193 | int MeasureText(char* str) 194 | { 195 | int i, x = 0; 196 | 197 | for (i = 0; str[i] != '\0'; i++) 198 | { 199 | x += MeasureCharacter(str[i]); 200 | } 201 | 202 | return x; 203 | } 204 | 205 | void DrawText(int x, int y, u32 color, char* str) 206 | { 207 | unsigned short* ptr; 208 | unsigned short glyphsize; 209 | int i, cx, cy; 210 | 211 | for (i = 0; str[i] != '\0'; i++) 212 | { 213 | if (str[i] == 0x20) 214 | { 215 | x += 6; 216 | continue; 217 | } 218 | 219 | u32 ch = str[i]; 220 | if (ch < 0x10 || ch > 0x7E) ch = 0x7F; 221 | 222 | ptr = &font[(ch-0x10) << 4]; 223 | glyphsize = ptr[0]; 224 | if (!glyphsize) 225 | { 226 | x += 6; 227 | continue; 228 | } 229 | 230 | x++; 231 | for (cy = 0; cy < 12; cy++) 232 | { 233 | if ((y+cy) >= 240) break; 234 | 235 | unsigned short val = ptr[4+cy]; 236 | 237 | for (cx = 0; cx < glyphsize; cx++) 238 | { 239 | if ((x+cx) < 0) continue; 240 | if ((x+cx) >= 320) break; 241 | 242 | if (val & (1 << cx)) 243 | DRAW_PIXEL(x+cx, y+cy, color); 244 | } 245 | } 246 | x += glyphsize; 247 | x++; 248 | if (x >= 320) break; 249 | } 250 | } 251 | 252 | 253 | #define TOOLBAR_HEIGHT 22 254 | #define BTN_WIDTH 26 255 | 256 | void DrawToolbar(char * dir) 257 | { 258 | u32 x; 259 | u32 basey = (TOOLBAR_HEIGHT-12)/2; 260 | 261 | DrawText(basey, basey, RGB(255,255,255), dir); 262 | 263 | FillRect(0, 319, TOOLBAR_HEIGHT, TOOLBAR_HEIGHT, RGB(0,255,255)); 264 | FillRect(0, 319, TOOLBAR_HEIGHT+1, TOOLBAR_HEIGHT+1, RGB(0,128,255)); 265 | 266 | // X button 267 | x = 320-BTN_WIDTH; 268 | FillRect(x, x, 0, TOOLBAR_HEIGHT-1, RGB(0,255,255)); 269 | DrawText(x+((BTN_WIDTH-12)/2), basey, RGB(255,32,32), "\x10"); 270 | 271 | // config button 272 | x = 320-(BTN_WIDTH*2); 273 | FillRect(x, x, 0, TOOLBAR_HEIGHT-1, RGB(0,255,255)); 274 | DrawText(x+((BTN_WIDTH-12)/2), basey, RGB(32,255,255), "\x11"); 275 | } 276 | 277 | bool HandleToolbar(u32 x, u32 y) 278 | { 279 | if (y >= TOOLBAR_HEIGHT) return false; 280 | 281 | u32 btn1 = 320-(BTN_WIDTH*2); 282 | u32 btn2 = 320-BTN_WIDTH; 283 | 284 | if (x >= btn1 && x < btn2) 285 | { 286 | UI_SaveAndSwitch(&UI_Config); 287 | return true; 288 | } 289 | else if (x >= btn2) 290 | { 291 | forceexit = 1; 292 | return true; 293 | } 294 | 295 | return false; 296 | } 297 | 298 | 299 | void DrawButton(int x, int y, int width, u32 color, char* text) 300 | { 301 | int txtwidth = MeasureText(text); 302 | if (!width) width = txtwidth + 6; 303 | 304 | if (x < 0) x += 320 - width; 305 | 306 | DrawRectOutline(x, x+width, y, y+18, RGB(0,255,255), RGB(0,128,255)); 307 | DrawText(x+((width-txtwidth)/2), y+4, color, text); 308 | } 309 | 310 | void DrawCheckBox(int x, int y, u32 color, char* text, bool check) 311 | { 312 | DrawRectOutline(x+1, x+18, y, y+17, RGB(0,255,255), RGB(0,128,255)); 313 | if (check) DrawText(x+4, y+4, RGB(255,255,255), "X"); 314 | DrawText(x+18+6, y+4, color, text); 315 | } 316 | -------------------------------------------------------------------------------- /source/ui.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #ifndef _UI_H_ 20 | #define _UI_H_ 21 | 22 | #include <3ds/types.h> 23 | 24 | #define RGB(r,g,b) ((b) | ((g) << 8) | ((r) << 16)) 25 | 26 | void UI_SetFramebuffer(u8* buffer); 27 | void ClearFramebuffer(); 28 | void ClearFramebufferWithColor(u32 color); 29 | void DrawRect(int x1, int x2, int y1, int y2, u32 color); 30 | void DrawRectOutline(int x1, int x2, int y1, int y2, u32 colorin, u32 colorout); 31 | void FillRect(int x1, int y1, int x2, int y2, u32 color); 32 | int MeasureCharacter(char ch); 33 | int MeasureText(char* str); 34 | void DrawText(int x, int y, u32 color, char* str); 35 | 36 | void DrawToolbar(char * dir); 37 | bool HandleToolbar(u32 x, u32 y); 38 | 39 | void DrawButton(int x, int y, int width, u32 color, char* text); 40 | void DrawCheckBox(int x, int y, u32 color, char* text, bool check); 41 | 42 | 43 | typedef struct 44 | { 45 | void (*Init)(); 46 | void (*DeInit)(); 47 | 48 | void (*Render)(bool force); 49 | void (*ButtonPress)(u32 btn); 50 | void (*Touch)(bool touch, u32 x, u32 y); 51 | 52 | } UIController; 53 | 54 | extern UIController UI_ROMMenu; 55 | extern UIController UI_Console; 56 | extern UIController UI_Config; 57 | 58 | void UI_Switch(UIController* ui); 59 | void UI_SaveAndSwitch(UIController* ui); 60 | void UI_Restore(); 61 | int UI_Level(); 62 | 63 | void UI_Render(); 64 | void UI_ButtonPress(u32 btn); 65 | void UI_Touch(int touch, u32 x, u32 y); 66 | 67 | 68 | bool StartROM(char* path, char* dir); 69 | void bprintf(char* fmt, ...); 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /source/ui_config.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #include <3ds.h> 20 | #include "ui.h" 21 | #include "config.h" 22 | #include "ppu.h" 23 | 24 | 25 | void ApplyScaling(); 26 | 27 | int configdirty = 0; 28 | 29 | 30 | void Config_Init() 31 | { 32 | configdirty = 2; 33 | } 34 | 35 | void Config_DeInit() 36 | { 37 | } 38 | 39 | void Config_Render(bool force) 40 | { 41 | int x, y; 42 | 43 | if (force) configdirty = 2; 44 | if (!configdirty) return; 45 | configdirty--; 46 | 47 | ClearFramebuffer(); 48 | 49 | DrawText(2, 2, RGB(255, 255, 255), "blargSNES config"); 50 | 51 | y = 2 + 12 + 10; 52 | 53 | DrawCheckBox(10, y, RGB(255,255,255), "Hardware renderer", Config.HardwareRenderer); 54 | 55 | y += 26; 56 | 57 | DrawCheckBox(26, y, RGB(255,255,255), "Mode 7 filtering", Config.HardwareMode7Filter); 58 | 59 | y += 26; 60 | 61 | DrawText(10, y+1, RGB(255,255,255), "Scaling:"); 62 | x = 10 + MeasureText("Scaling:") + 6; 63 | 64 | char* scalemodes[] = {"1:1", "Fullscreen", "Cropped", "4:3", "Cropped 4:3"}; 65 | int themode = Config.ScaleMode; 66 | if (themode < 0 || themode > 4) themode = 0; 67 | DrawButton(x, y-3, 140, RGB(255,255,255), scalemodes[themode]); 68 | 69 | y += 26; 70 | 71 | DrawCheckBox(10, y, RGB(255,255,255), "Force VSync", Config.VSync); 72 | 73 | y += 26; 74 | 75 | DrawText(10, y+1, RGB(255,255,255), "Frameskip:"); 76 | x = 10 + MeasureText("Frameskip:") + 6; 77 | 78 | char* fskip[] = {"None", "1", "2", "3", "4", "Auto"}; 79 | themode = Config.FrameSkip; 80 | if (themode < 0 || themode > 5) themode = 0; 81 | DrawButton(x, y-3, 100, RGB(255,255,255), fskip[themode]); 82 | 83 | DrawButton(10, 212, 0, RGB(255,128,128), "Cancel"); 84 | DrawButton(-10, 212, 0, RGB(128,255,128), "Save changes"); 85 | } 86 | 87 | void Config_ButtonPress(u32 btn) 88 | { 89 | } 90 | 91 | void Config_Touch(bool touch, u32 x, u32 y) 92 | { 93 | if (touch != 0) return; 94 | 95 | // bounding boxes are gross 96 | // TODO: eventually adopt a real widget system? 97 | 98 | if (y >= 24 && y < 44) 99 | { 100 | Config.HardwareRenderer = !Config.HardwareRenderer; 101 | configdirty = 2; 102 | } 103 | else if (y >= 50 && y < 70) 104 | { 105 | Config.HardwareMode7Filter = !Config.HardwareMode7Filter; 106 | configdirty = 2; 107 | } 108 | else if (y >= 76 && y < 96) 109 | { 110 | Config.ScaleMode++; 111 | if (Config.ScaleMode > 4) Config.ScaleMode = 0; 112 | configdirty = 2; 113 | } 114 | else if (y >= 102 && y < 122) 115 | { 116 | Config.VSync = !Config.VSync; 117 | configdirty = 2; 118 | } 119 | else if (y >= 126 && y < 146) 120 | { 121 | Config.FrameSkip++; 122 | if (Config.FrameSkip > 5) Config.FrameSkip = 0; 123 | configdirty = 2; 124 | } 125 | else if (x < 106 && y >= 200) 126 | { 127 | LoadConfig(0); 128 | UI_Restore(); 129 | } 130 | else if (x > 212 && y >= 200) 131 | { 132 | SaveConfig(0); 133 | UI_Restore(); 134 | } 135 | 136 | PPU_SwitchRenderers(); 137 | ApplyScaling(); 138 | } 139 | 140 | 141 | UIController UI_Config = 142 | { 143 | Config_Init, 144 | Config_DeInit, 145 | 146 | Config_Render, 147 | Config_ButtonPress, 148 | Config_Touch 149 | }; 150 | -------------------------------------------------------------------------------- /source/ui_console.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include <3ds.h> 24 | #include "ui.h" 25 | 26 | 27 | #define CONSOLE_MAX 20 28 | char consolebuf[CONSOLE_MAX][64] = {{0}}; 29 | int consoleidx = 0; 30 | int consoledirty = 0; 31 | 32 | extern int running, pause; 33 | 34 | 35 | void bprintf(char* fmt, ...) 36 | { 37 | char buf[1024]; 38 | va_list args; 39 | 40 | va_start(args, fmt); 41 | vsnprintf(buf, 1024, fmt, args); 42 | va_end(args); 43 | 44 | int i = 0, j = 0, x = 0; 45 | for (;;) 46 | { 47 | j = 0; x = 0; 48 | while (buf[i] != '\0' && buf[i] != '\n' && j<63) 49 | { 50 | x += MeasureCharacter(buf[i]); 51 | if (x >= 320) break; 52 | 53 | consolebuf[consoleidx][j++] = buf[i++]; 54 | } 55 | consolebuf[consoleidx][j] = '\0'; 56 | 57 | consoleidx++; 58 | if (consoleidx >= CONSOLE_MAX) consoleidx = 0; 59 | 60 | if (buf[i] == '\0' || buf[i+1] == '\0') 61 | break; 62 | } 63 | 64 | consoledirty = 2; 65 | } 66 | 67 | void ClearConsole() 68 | { 69 | consoleidx = 0; 70 | memset(consolebuf, 0, CONSOLE_MAX*64); 71 | 72 | consoledirty = 2; 73 | } 74 | 75 | void DrawConsole() 76 | { 77 | int i, j, y; 78 | 79 | y = 0; 80 | j = consoleidx; 81 | for (i = 0; i < CONSOLE_MAX; i++) 82 | { 83 | if (consolebuf[j][0] != '\0') 84 | { 85 | DrawText(0, y, (running&&!pause)?RGB(128,128,128):RGB(255,255,255), consolebuf[j]); 86 | y += 12; 87 | } 88 | 89 | j++; 90 | if (j >= CONSOLE_MAX) j = 0; 91 | } 92 | } 93 | 94 | 95 | void Console_Init() 96 | { 97 | } 98 | 99 | void Console_DeInit() 100 | { 101 | } 102 | 103 | void Console_Render(bool force) 104 | { 105 | if (force) consoledirty = 2; 106 | if (!consoledirty) return; 107 | consoledirty--; 108 | 109 | if (running&&!pause) ClearFramebufferWithColor(RGB(0,0,0)); 110 | else ClearFramebuffer(); 111 | DrawConsole(); 112 | } 113 | 114 | void Console_ButtonPress(u32 btn) 115 | { 116 | if (btn & (KEY_A|KEY_B)) 117 | { 118 | if (!running) 119 | UI_Switch(&UI_ROMMenu); 120 | } 121 | } 122 | 123 | void Console_Touch(bool touch, u32 x, u32 y) 124 | { 125 | } 126 | 127 | 128 | UIController UI_Console = 129 | { 130 | Console_Init, 131 | Console_DeInit, 132 | 133 | Console_Render, 134 | Console_ButtonPress, 135 | Console_Touch 136 | }; 137 | -------------------------------------------------------------------------------- /source/ui_rommenu.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014-2022 Arisotura 3 | 4 | This file is part of blargSnes. 5 | 6 | blargSnes is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free 8 | Software Foundation, either version 3 of the License, or (at your option) 9 | any later version. 10 | 11 | blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with blargSnes. If not, see http://www.gnu.org/licenses/. 17 | */ 18 | 19 | #include <3ds.h> 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "ui.h" 25 | #include "config.h" 26 | 27 | 28 | int nfiles; 29 | int menusel = 0; 30 | int menuscroll = 0; 31 | #define MENU_MAX 18 32 | int menudirty = 0; 33 | 34 | int scrolltouch_y = 0; 35 | int scrolling = 0; 36 | 37 | int marquee_pos = 0; 38 | int marquee_dir = 0; 39 | 40 | struct LIST 41 | { 42 | struct LIST * pNext; 43 | void * item; 44 | }; 45 | 46 | struct LISTITEM 47 | { 48 | char * name; 49 | int type; 50 | }; 51 | 52 | struct LIST * head = NULL; 53 | struct LIST * curr = NULL; 54 | struct LISTITEM ** fileIdx; 55 | 56 | char dirshort[0x40]; 57 | 58 | 59 | void strncpy_u2a(char* dst, u16* src, int n) 60 | { 61 | int i = 0; 62 | while (i < n && src[i] != '\0') 63 | { 64 | if (src[i] & 0xFF00) 65 | dst[i] = 0x7F; 66 | else 67 | dst[i] = (char)src[i]; 68 | 69 | i++; 70 | } 71 | 72 | dst[i] = '\0'; 73 | } 74 | 75 | int itemcmp(void * first, void * second) 76 | { 77 | struct LISTITEM * firstli = (struct LISTITEM *)first; 78 | struct LISTITEM * secondli = (struct LISTITEM *)second; 79 | if(firstli->type != secondli->type) 80 | { 81 | if(firstli->type > secondli->type) 82 | return -1; 83 | else 84 | return 1; 85 | } 86 | return strncasecmp(firstli->name, secondli->name, 0x105); 87 | } 88 | 89 | struct LIST * CreateList(void * item) 90 | { 91 | struct LIST * ptr = (struct LIST*)malloc(sizeof(struct LIST)); 92 | if(ptr == NULL) 93 | return NULL; 94 | ptr->item = item; 95 | ptr->pNext = NULL; 96 | head = curr = ptr; 97 | return ptr; 98 | } 99 | 100 | struct LIST * AddToList(void * item) 101 | { 102 | if(head == NULL) 103 | return CreateList(item); 104 | struct LIST * ptr = (struct LIST*)malloc(sizeof(struct LIST)); 105 | if(ptr == NULL) 106 | return NULL; 107 | ptr->item = item; 108 | ptr->pNext = NULL; 109 | 110 | curr->pNext = ptr; 111 | curr = ptr; 112 | 113 | return ptr; 114 | } 115 | 116 | void DeleteItem(void * item) 117 | { 118 | struct LISTITEM * thisitem = (struct LISTITEM *)(item); 119 | free(thisitem->name); 120 | free(item); 121 | } 122 | 123 | void DeleteList() 124 | { 125 | while (head != NULL) 126 | { 127 | curr = head; 128 | DeleteItem(curr->item); 129 | head = head->pNext; 130 | free(curr); 131 | } 132 | } 133 | 134 | 135 | struct LIST * SortList(struct LIST * pList) { 136 | // zero or one element in list 137 | if(pList == NULL || pList->pNext == NULL) 138 | return pList; 139 | // head is the first element of resulting sorted list 140 | struct LIST * head = NULL; 141 | while(pList != NULL) { 142 | struct LIST * current = pList; 143 | pList = pList->pNext; 144 | if(head == NULL || (itemcmp(current->item, head->item) < 0)) { 145 | //current->iValue < head->iValue) { 146 | // insert into the head of the sorted list 147 | // or as the first element into an empty sorted list 148 | current->pNext = head; 149 | head = current; 150 | } else { 151 | // insert current element into proper position in non-empty sorted list 152 | struct LIST * p = head; 153 | while(p != NULL) { 154 | if(p->pNext == NULL || // last element of the sorted list 155 | (itemcmp(current->item, p->pNext->item) < 0)) 156 | //current->iValue < p->pNext->iValue) // middle of the list 157 | { 158 | // insert into middle of the sorted list or as the last element 159 | current->pNext = p->pNext; 160 | p->pNext = current; 161 | break; // done 162 | } 163 | p = p->pNext; 164 | } 165 | } 166 | } 167 | return head; 168 | } 169 | 170 | 171 | bool IsGoodEntry(struct dirent *entry) 172 | { 173 | if (entry->d_type & DT_DIR) return true; 174 | 175 | char *name = (char*)entry->d_name; 176 | char *pch = strchr(name, '.'); 177 | char *ext = NULL; 178 | while (pch != NULL) 179 | { 180 | ext = pch; 181 | pch = strchr(pch + 1, '.'); 182 | } 183 | if (ext == NULL) return false; 184 | 185 | if (strncasecmp(ext, ".SMC", 4) && strncasecmp(ext, ".SFC", 4)) return false; 186 | 187 | return true; 188 | } 189 | 190 | void DrawROMList() 191 | { 192 | int i, y; 193 | int maxfile; 194 | int menuy; 195 | 196 | DrawToolbar(dirshort); 197 | 198 | menuy = 26; 199 | y = menuy; 200 | 201 | if (nfiles < 1) 202 | { 203 | DrawText(3, y, RGB(255,64,64), "No ROMs found."); 204 | return; 205 | } 206 | 207 | if ((nfiles - menuscroll) <= MENU_MAX) maxfile = (nfiles - menuscroll); 208 | else maxfile = MENU_MAX; 209 | 210 | for (i = 0; i < maxfile; i++) 211 | { 212 | int xoffset = 3; 213 | 214 | // blue highlight for the selected ROM 215 | if ((menuscroll+i) == menusel) 216 | { 217 | FillRect(0, 319, y, y+11, RGB(0,0,255)); 218 | 219 | int textwidth = MeasureText(fileIdx[(menuscroll+i)]->name); 220 | int maxwidth = (nfiles>MENU_MAX) ? 308:320; 221 | if (textwidth > maxwidth) 222 | { 223 | maxwidth -= 6; 224 | if (!marquee_dir) 225 | { 226 | marquee_pos -= 1; 227 | if (marquee_pos < -(textwidth-maxwidth)) 228 | { 229 | marquee_pos = -(textwidth-maxwidth) - 1; 230 | marquee_dir = 1; 231 | } 232 | } 233 | else 234 | { 235 | marquee_pos += 1; 236 | if (marquee_pos >= 0) 237 | { 238 | marquee_pos = 0; 239 | marquee_dir = 0; 240 | } 241 | } 242 | 243 | // force refreshing 244 | menudirty++; 245 | } 246 | else 247 | { 248 | marquee_dir = 0; 249 | marquee_pos = 0; 250 | } 251 | 252 | xoffset += marquee_pos; 253 | } 254 | 255 | DrawText(xoffset, y, (fileIdx[(menuscroll+i)]->type ? RGB(255,255,64) : RGB(255,255,255)), fileIdx[(menuscroll+i)]->name); 256 | y += 12; 257 | } 258 | 259 | // scrollbar 260 | if (nfiles > MENU_MAX) 261 | { 262 | int shownheight = 240-menuy; 263 | int fullheight = 12*nfiles; 264 | 265 | int sbheight = (shownheight * shownheight) / fullheight; 266 | if (sbheight < 10) sbheight = 10; 267 | 268 | int sboffset = (menuscroll * 12 * shownheight) / fullheight; 269 | if ((sboffset+sbheight) > shownheight) 270 | sboffset = shownheight-sbheight; 271 | 272 | FillRect(308, 319, menuy, 239, RGB(0,0,64)); 273 | FillRect(308, 319, menuy+sboffset, menuy+sboffset+sbheight-1, RGB(0,255,255)); 274 | } 275 | } 276 | 277 | 278 | void ROMMenu_Init() 279 | { 280 | int i; 281 | struct dirent *entry; 282 | DIR *pDir = opendir(Config.DirPath); 283 | 284 | 285 | head = NULL; 286 | curr = NULL; 287 | 288 | if (strcmp(Config.DirPath,"/") == 0) 289 | { 290 | nfiles = 0; 291 | } 292 | else 293 | { 294 | struct LISTITEM * newItem = (struct LISTITEM *)malloc(sizeof(struct LISTITEM)); 295 | newItem->name = (char*)malloc(0x106); 296 | strncpy(newItem->name, "/..", 0x105); 297 | newItem->type = 2; 298 | AddToList((void *)(newItem)); 299 | nfiles = 1; 300 | } 301 | 302 | for (;;) 303 | { 304 | entry = readdir(pDir); 305 | 306 | if (entry == NULL) break; 307 | if (!IsGoodEntry(entry)) continue; 308 | 309 | struct LISTITEM * newItem = (struct LISTITEM *)malloc(sizeof(struct LISTITEM)); 310 | newItem->name = (char*)malloc(0x106); 311 | 312 | newItem->type = (entry->d_type & DT_DIR ? 1 : 0); 313 | 314 | if (newItem->type) 315 | { 316 | newItem->name[0] = '/'; 317 | strncpy(&(newItem->name[1]), entry->d_name, 0x104); 318 | } 319 | else 320 | strncpy(newItem->name, entry->d_name, 0x105); 321 | 322 | AddToList((void *)(newItem)); 323 | nfiles++; 324 | } 325 | 326 | closedir(pDir); 327 | 328 | head = SortList(head); 329 | 330 | fileIdx = (struct LISTITEM**)malloc(nfiles * sizeof(struct LISTITEM*)); 331 | 332 | curr = head; 333 | for (i = 0; i < nfiles; i++) 334 | { 335 | fileIdx[i] = (struct LISTITEM *)(curr->item); 336 | curr = curr->pNext; 337 | } 338 | 339 | char * dirname = Config.DirPath; 340 | char * dirend = strrchr(dirname, '/'); 341 | char dirdeep = 0; 342 | strcpy(dirshort, "/\0"); 343 | if (dirname < dirend) 344 | { 345 | char * curdir = dirname; 346 | while (MeasureText(dirname) > (256 - (dirdeep ? 22 : 0))) 347 | { 348 | if ((curdir = strchr(&(dirname[1]), '/')) == dirend) 349 | break; 350 | dirdeep = 1; 351 | dirname = curdir; 352 | } 353 | if (dirdeep) 354 | strcat(dirshort, "../"); 355 | if (MeasureText(dirname) > (256 - (dirdeep ? 22 : 0))) 356 | { 357 | strncat(dirshort, &(dirname[1]), 62 - (dirdeep ? 3 : 0)); 358 | dirend = dirshort + strlen(dirshort) - 1; 359 | while (MeasureText(dirshort) > (241 - (dirdeep ? 22 : 0))) 360 | { 361 | dirend[0] = '\0'; 362 | dirend--; 363 | } 364 | strcat(dirshort,"..."); 365 | } 366 | else 367 | strcat(dirshort, &(dirname[1])); 368 | } 369 | 370 | menudirty = 2; 371 | } 372 | 373 | void ROMMenu_DeInit() 374 | { 375 | free(fileIdx); 376 | DeleteList(); 377 | } 378 | 379 | void ROMMenu_Render(bool force) 380 | { 381 | if (force) menudirty = 2; 382 | if (!menudirty) return; 383 | menudirty--; 384 | 385 | ClearFramebuffer(); 386 | DrawROMList(); 387 | } 388 | 389 | void ROMMenu_ExamineExec() 390 | { 391 | if (!fileIdx[menusel]->type) 392 | { 393 | if (!StartROM(fileIdx[menusel]->name, Config.DirPath)) 394 | bprintf("Failed to load this ROM\nPress A to return to menu\n"); 395 | 396 | UI_Switch(&UI_Console); 397 | } 398 | else 399 | { 400 | 401 | if (fileIdx[menusel]->type == 2) 402 | { 403 | char* findpath = strrchr(Config.DirPath,'/'); 404 | if (findpath != Config.DirPath) 405 | { 406 | findpath[0] = '\0'; 407 | findpath = strrchr(Config.DirPath,'/'); 408 | findpath[1] = '\0'; 409 | } 410 | } 411 | else 412 | { 413 | strcat(Config.DirPath,&(fileIdx[menusel]->name[1])); 414 | strcat(Config.DirPath,"/"); 415 | } 416 | menusel = 0; 417 | ROMMenu_DeInit(); 418 | ROMMenu_Init(); 419 | ROMMenu_Render(2); 420 | } 421 | } 422 | 423 | void ROMMenu_ButtonPress(u32 btn) 424 | { 425 | if (btn & (KEY_A|KEY_B)) 426 | ROMMenu_ExamineExec(); 427 | else if (btn & KEY_UP) // up 428 | { 429 | menusel--; 430 | if (menusel < 0) 431 | { 432 | menusel = nfiles - 1; 433 | //menuscroll = menusel-(MENU_MAX-1); 434 | } 435 | if (menusel < menuscroll) menuscroll = menusel; 436 | if (menusel-(MENU_MAX-1) > menuscroll) menuscroll = menusel-(MENU_MAX-1); 437 | 438 | menudirty = 2; 439 | } 440 | else if (btn & KEY_DOWN) // down 441 | { 442 | menusel++; 443 | if (menusel > nfiles-1) 444 | { 445 | menusel = 0; 446 | //menuscroll = menusel; 447 | } 448 | if (menusel < menuscroll) menuscroll = menusel; 449 | if (menusel-(MENU_MAX-1) > menuscroll) menuscroll = menusel-(MENU_MAX-1); 450 | 451 | menudirty = 2; 452 | } 453 | else if (btn & KEY_LEFT) // left 454 | { 455 | menusel -= MENU_MAX; 456 | if (menusel < 0) menusel = 0; 457 | if (menusel < menuscroll) menuscroll = menusel; 458 | if (menusel-(MENU_MAX-1) > menuscroll) menuscroll = menusel-(MENU_MAX-1); 459 | 460 | menudirty = 2; 461 | } 462 | else if (btn & KEY_RIGHT) // right 463 | { 464 | menusel += MENU_MAX; 465 | if (menusel > nfiles-1) menusel = nfiles - 1; 466 | if (menusel < menuscroll) menuscroll = menusel; 467 | if (menusel-(MENU_MAX-1) > menuscroll) menuscroll = menusel-(MENU_MAX-1); 468 | 469 | menudirty = 2; 470 | } 471 | 472 | marquee_pos = 0; 473 | marquee_dir = 0; 474 | } 475 | 476 | void ROMMenu_Touch(bool touch, u32 x, u32 y) 477 | { 478 | int menuy = 26; 479 | 480 | if (y >= menuy) 481 | { 482 | if (x < 308 && touch == 0) 483 | { 484 | y -= menuy; 485 | y /= 12; 486 | 487 | y += menuscroll; 488 | if (y >= nfiles) return; 489 | 490 | menusel = y; 491 | ROMMenu_ExamineExec(); 492 | 493 | marquee_pos = 0; 494 | marquee_dir = 0; 495 | } 496 | /*else 497 | { 498 | if (touch == 0) 499 | scrolling = 0; 500 | else if (touch == 1) 501 | { 502 | scrolling = 1; 503 | scrolltouch_y = y; 504 | } 505 | else 506 | { 507 | int dy = y - scrolltouch_y; 508 | scrolltouch_y = y; 509 | 510 | int shownheight = 240-menuy; 511 | int fullheight = 12*nfiles; 512 | 513 | int sboffset = (menuscroll * 12 * shownheight) / fullheight; 514 | sboffset += dy; 515 | 516 | menuscroll = (sboffset * fullheight) / (12 * shownheight); 517 | if (menuscroll < 0) menuscroll = 0; 518 | else if (menuscroll >= nfiles) menuscroll = nfiles-1; 519 | 520 | if (menusel < menuscroll) menusel = menuscroll; 521 | else if (menusel >= menuscroll+MENU_MAX) menusel = menuscroll+MENU_MAX-1; 522 | } 523 | 524 | menudirty = 2; 525 | }*/ 526 | } 527 | else if (touch == 0) 528 | HandleToolbar(x, y); 529 | } 530 | 531 | 532 | UIController UI_ROMMenu = 533 | { 534 | ROMMenu_Init, 535 | ROMMenu_DeInit, 536 | 537 | ROMMenu_Render, 538 | ROMMenu_ButtonPress, 539 | ROMMenu_Touch 540 | }; 541 | -------------------------------------------------------------------------------- /source/window_mask.g.pica: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Copyright 2014-2022 Arisotura 3 | ; 4 | ; This file is part of blargSnes. 5 | ; 6 | ; blargSnes is free software: you can redistribute it and/or modify it under 7 | ; the terms of the GNU General Public License as published by the Free 8 | ; Software Foundation, either version 3 of the License, or (at your option) 9 | ; any later version. 10 | ; 11 | ; blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | ; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | ; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | ; 15 | ; You should have received a copy of the GNU General Public License along 16 | ; with blargSnes. If not, see http:;www.gnu.org/licenses/. 17 | ; ----------------------------------------------------------------------------- 18 | 19 | .gsh point c0 20 | 21 | ; setup outmap 22 | .out o0 position 23 | .out o1 color 24 | 25 | ; input 26 | ; v0: XY coordinates 27 | ; v1: 'alpha' in X 28 | 29 | .entry gmain 30 | .proc gmain 31 | ; turn two vertices into a rectangle 32 | ; setemit: vtxid, primemit, winding 33 | 34 | ; v0 = vertex 0, position 35 | ; v1 = vertex 0, color 36 | ; v2 = vertex 1, position 37 | ; v3 = vertex 1, color 38 | 39 | ; x1 y1 40 | setemit 0 41 | mov o0, v0 42 | mov o1, v1 43 | emit 44 | 45 | ; x2 y1 46 | setemit 1 47 | mov r0, v2 48 | mov r0.y, v0.y 49 | mov o0, r0 50 | mov o1, v3 51 | emit 52 | 53 | ; x1 y2 54 | setemit 2, prim 55 | mov r0, v0 56 | mov r0.y, v2.y 57 | mov o0, r0 58 | mov o1, v1 59 | emit 60 | 61 | ; x2 y2 62 | setemit 0, prim inv 63 | mov o0, v2 64 | mov o1, v3 65 | emit 66 | 67 | end 68 | nop 69 | .end 70 | -------------------------------------------------------------------------------- /source/window_mask.v.pica: -------------------------------------------------------------------------------- 1 | ; ----------------------------------------------------------------------------- 2 | ; Copyright 2014-2022 Arisotura 3 | ; 4 | ; This file is part of blargSnes. 5 | ; 6 | ; blargSnes is free software: you can redistribute it and/or modify it under 7 | ; the terms of the GNU General Public License as published by the Free 8 | ; Software Foundation, either version 3 of the License, or (at your option) 9 | ; any later version. 10 | ; 11 | ; blargSnes is distributed in the hope that it will be useful, but WITHOUT ANY 12 | ; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | ; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | ; 15 | ; You should have received a copy of the GNU General Public License along 16 | ; with blargSnes. If not, see http:;www.gnu.org/licenses/. 17 | ; ----------------------------------------------------------------------------- 18 | 19 | ; setup constants 20 | .constf const1(0.0078125, 0.00390625, 0.0, 1.0) 21 | .constf const2(-1.0, 0.0, 0.0, 0.0) 22 | 23 | ; setup outmap 24 | .out o0 position 25 | .out o1 color 26 | 27 | ; input 28 | ; v0: XY coordinates 29 | ; v1: 'alpha' in X 30 | 31 | .entry vmain 32 | .proc vmain 33 | mov r0, const1 34 | mul r0.xyz, r0.xyz, v0.xyz 35 | add r0.xyz, const2.xxx, r0.xyz 36 | mov o0, r0 37 | 38 | ; result.color = in.color 39 | mul o1, const1.yyyy, v1.xxxx 40 | end 41 | nop 42 | .end 43 | --------------------------------------------------------------------------------