├── .github └── workflows │ └── build.yaml ├── LICENSE ├── Makefile.3ds ├── Makefile.blocksds ├── Makefile.blocksds.arm7 ├── Makefile.blocksds.arm9 ├── Makefile.nds ├── README.md ├── arm7 ├── Makefile └── source │ ├── apu.c │ └── main.c ├── arm9 ├── Makefile └── source │ ├── emulator.c │ ├── lut_expand_8_32.inc │ ├── lut_expand_8_32_f.inc │ ├── lut_expand_8_32_f_flipx.inc │ ├── lut_expand_8_32_flipx.inc │ ├── nds_apu.c │ ├── nds_keyboard.c │ ├── nds_keyboard.h │ ├── nds_ppu.c │ ├── nds_ppu.h │ └── stubs.c ├── assets ├── 3ds │ ├── gfx_keyboard.png │ └── gfx_keyboard.t3s └── nds │ ├── gfx_keyboard.grit │ └── gfx_keyboard.png ├── include ├── emulator_config.h └── nds │ ├── apu.h │ └── fifo.h ├── misc ├── uxn32.bmp ├── uxn32.png └── uxn48.png ├── source ├── 3ds │ ├── ctr_keyboard.c │ ├── ctr_keyboard.h │ ├── ctr_screen.c │ ├── ctr_screen.h │ ├── ctr_util.c │ ├── ctr_util.h │ └── emulator.c ├── devices │ ├── audio.c │ ├── audio.h │ ├── controller.c │ ├── controller.h │ ├── datetime.c │ ├── datetime.h │ ├── file.c │ ├── file.h │ ├── mouse.c │ ├── mouse.h │ ├── screen.c │ ├── screen.h │ ├── system.c │ └── system.h ├── ndsabi │ ├── macros.inc │ ├── memcpy.s │ ├── memmove.s │ ├── memset.s │ └── rmemcpy.s ├── util.c ├── util.h ├── uxn.h ├── uxngba-c.c └── uxngba.s └── uxn ├── asma.rom ├── calc.rom ├── catclock.rom ├── dexe.rom ├── launcher.rom ├── left.rom ├── nasu.rom ├── noodle.rom ├── orca.rom ├── piano.rom └── turye.rom /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build_nds: 11 | name: Build NDS 12 | runs-on: ubuntu-latest 13 | container: skylyrac/blocksds:slim-latest 14 | steps: 15 | - name: Clone project 16 | uses: actions/checkout@v4 17 | with: 18 | submodules: recursive 19 | 20 | - name: Build 21 | run: make -f Makefile.blocksds 22 | 23 | - name: Archive artifacts 24 | uses: actions/upload-artifact@v4 25 | with: 26 | name: uxnds (NDS) 27 | path: | 28 | LICENSE 29 | uxn*.nds 30 | uxn/ 31 | 32 | build_3ds: 33 | name: Build 3DS 34 | runs-on: ubuntu-latest 35 | container: devkitpro/devkitarm 36 | steps: 37 | - name: Clone project 38 | uses: actions/checkout@v4 39 | with: 40 | submodules: recursive 41 | 42 | - name: Build (release) 43 | run: make -f Makefile.3ds DEBUG=false 44 | 45 | - name: Build (debug) 46 | run: make -f Makefile.3ds DEBUG=true 47 | 48 | - name: Archive artifacts 49 | uses: actions/upload-artifact@v4 50 | with: 51 | name: uxnds (3DS) 52 | path: | 53 | LICENSE 54 | uxn*.3dsx 55 | uxn/ 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Bad Diode and Devine Lu Linvega 4 | Copyright (c) 2021, 2022, 2023 Adrian "asie" Siekierka 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile.3ds: -------------------------------------------------------------------------------- 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 | #--------------------------------------------------------------------------------- 13 | # TARGET is the name of the output 14 | # BUILD is the directory where object files & intermediate files will be placed 15 | # SOURCES is a list of directories containing source code 16 | # DATA is a list of directories containing data files 17 | # INCLUDES is a list of directories containing header files 18 | # GRAPHICS is a list of directories containing graphics files 19 | # GFXBUILD is the directory where converted graphics files will be placed 20 | # If set to $(BUILD), it will statically link in the converted 21 | # files as if they were data files. 22 | # 23 | # NO_SMDH: if set to anything, no SMDH file is generated. 24 | # ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional) 25 | # APP_TITLE is the name of the app stored in the SMDH file (Optional) 26 | # APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) 27 | # APP_AUTHOR is the author of the app stored in the SMDH file (Optional) 28 | # ICON is the filename of the icon (.png), relative to the project folder. 29 | # If not set, it attempts to use one of the following (in this order): 30 | # - .png 31 | # - icon.png 32 | # - /default_icon.png 33 | #--------------------------------------------------------------------------------- 34 | ifeq ($(DEBUG),true) 35 | TARGET := $(notdir $(CURDIR))_debug 36 | else 37 | TARGET := $(notdir $(CURDIR)) 38 | endif 39 | ifeq ($(DEBUG),true) 40 | BUILD := build_3ds_debug 41 | else 42 | BUILD := build_3ds 43 | endif 44 | SOURCES := source source/3ds source/devices source/ndsabi 45 | DATA := 46 | INCLUDES := include source 47 | GRAPHICS := assets/3ds 48 | ROMFS := romfs/3ds 49 | GFXBUILD := $(ROMFS) 50 | APP_TITLE := uxnds v0.5.3 51 | ifeq ($(DEBUG),true) 52 | APP_DESCRIPTION := varvara virtual machine (debug) 53 | else 54 | APP_DESCRIPTION := varvara virtual machine 55 | endif 56 | APP_AUTHOR := asie 57 | ICON := misc/uxn48.png 58 | 59 | #TARGET := donsol 60 | #APP_TITLE = Donsol 61 | #APP_DESCRIPTION := Exploring a dungeon made of a standard 54 poker card set 62 | #APP_AUTHOR := Rek & Devine 63 | #ICON := misc/donsol48.png 64 | 65 | #--------------------------------------------------------------------------------- 66 | # options for code generation 67 | #--------------------------------------------------------------------------------- 68 | ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft 69 | 70 | CFLAGS := -g -Wall -O3 -mword-relocations \ 71 | -ffunction-sections \ 72 | $(ARCH) 73 | 74 | CFLAGS += $(INCLUDE) -DARM11 -D__3DS__ 75 | 76 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 77 | 78 | ifeq ($(DEBUG),true) 79 | CFLAGS += -DDEBUG -DDEBUG_PROFILE -DCPU_ERROR_CHECKING 80 | CXXFLAGS += -DDEBUG -DDEBUG_PROFILE -DCPU_ERROR_CHECKING 81 | endif 82 | 83 | ASFLAGS := -g $(ARCH) -D__3DS__ 84 | LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 85 | 86 | LIBS := -lcitro2d -lcitro3d -lctru -lm 87 | 88 | #--------------------------------------------------------------------------------- 89 | # list of directories containing libraries, this must be the top level containing 90 | # include and lib 91 | #--------------------------------------------------------------------------------- 92 | LIBDIRS := $(CTRULIB) 93 | 94 | 95 | #--------------------------------------------------------------------------------- 96 | # no real need to edit anything past this point unless you need to add additional 97 | # rules for different file extensions 98 | #--------------------------------------------------------------------------------- 99 | ifneq ($(BUILD),$(notdir $(CURDIR))) 100 | #--------------------------------------------------------------------------------- 101 | 102 | export OUTPUT := $(CURDIR)/$(TARGET) 103 | export TOPDIR := $(CURDIR) 104 | 105 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 106 | $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) \ 107 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 108 | 109 | export DEPSDIR := $(CURDIR)/$(BUILD) 110 | 111 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 112 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 113 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 114 | PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) 115 | SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist))) 116 | GFXFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.t3s))) 117 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 118 | 119 | #--------------------------------------------------------------------------------- 120 | # use CXX for linking C++ projects, CC for standard C 121 | #--------------------------------------------------------------------------------- 122 | ifeq ($(strip $(CPPFILES)),) 123 | #--------------------------------------------------------------------------------- 124 | export LD := $(CC) 125 | #--------------------------------------------------------------------------------- 126 | else 127 | #--------------------------------------------------------------------------------- 128 | export LD := $(CXX) 129 | #--------------------------------------------------------------------------------- 130 | endif 131 | #--------------------------------------------------------------------------------- 132 | 133 | #--------------------------------------------------------------------------------- 134 | ifeq ($(GFXBUILD),$(BUILD)) 135 | #--------------------------------------------------------------------------------- 136 | export T3XFILES := $(GFXFILES:.t3s=.t3x) 137 | #--------------------------------------------------------------------------------- 138 | else 139 | #--------------------------------------------------------------------------------- 140 | export ROMFS_T3XFILES := $(patsubst %.t3s, $(GFXBUILD)/%.t3x, $(GFXFILES)) 141 | export T3XHFILES := $(patsubst %.t3s, $(BUILD)/%.h, $(GFXFILES)) 142 | #--------------------------------------------------------------------------------- 143 | endif 144 | #--------------------------------------------------------------------------------- 145 | 146 | export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 147 | 148 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) \ 149 | $(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \ 150 | $(addsuffix .o,$(T3XFILES)) 151 | 152 | export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) 153 | 154 | export HFILES := $(PICAFILES:.v.pica=_shbin.h) $(SHLISTFILES:.shlist=_shbin.h) \ 155 | $(addsuffix .h,$(subst .,_,$(BINFILES))) \ 156 | $(GFXFILES:.t3s=.h) 157 | 158 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 159 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 160 | -I$(CURDIR)/$(BUILD) 161 | 162 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 163 | 164 | export _3DSXDEPS := $(if $(NO_SMDH),,$(OUTPUT).smdh) 165 | 166 | ifeq ($(strip $(ICON)),) 167 | icons := $(wildcard *.png) 168 | ifneq (,$(findstring $(TARGET).png,$(icons))) 169 | export APP_ICON := $(TOPDIR)/$(TARGET).png 170 | else 171 | ifneq (,$(findstring icon.png,$(icons))) 172 | export APP_ICON := $(TOPDIR)/icon.png 173 | endif 174 | endif 175 | else 176 | export APP_ICON := $(TOPDIR)/$(ICON) 177 | endif 178 | 179 | ifeq ($(strip $(NO_SMDH)),) 180 | export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh 181 | endif 182 | 183 | ifneq ($(ROMFS),) 184 | export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) 185 | endif 186 | 187 | .PHONY: all clean 188 | 189 | #--------------------------------------------------------------------------------- 190 | all: $(BUILD) $(GFXBUILD) $(DEPSDIR) $(ROMFS_T3XFILES) $(T3XHFILES) 191 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile.3ds 192 | 193 | $(BUILD): 194 | @mkdir -p $@ 195 | 196 | ifneq ($(GFXBUILD),$(BUILD)) 197 | $(GFXBUILD): 198 | @mkdir -p $@ 199 | endif 200 | 201 | ifneq ($(DEPSDIR),$(BUILD)) 202 | $(DEPSDIR): 203 | @mkdir -p $@ 204 | endif 205 | 206 | #--------------------------------------------------------------------------------- 207 | clean: 208 | @echo clean ... 209 | @rm -fr build_3ds build_3ds_debug $(TARGET).3dsx $(OUTPUT).smdh $(TARGET)_3ds.elf $(GFXBUILD) 210 | 211 | #--------------------------------------------------------------------------------- 212 | $(GFXBUILD)/%.t3x $(BUILD)/%.h : %.t3s 213 | #--------------------------------------------------------------------------------- 214 | @echo $(notdir $<) 215 | @tex3ds -i $< -H $(BUILD)/$*.h -d $(DEPSDIR)/$*.d -o $(GFXBUILD)/$*.t3x 216 | 217 | #--------------------------------------------------------------------------------- 218 | else 219 | 220 | #--------------------------------------------------------------------------------- 221 | # main targets 222 | #--------------------------------------------------------------------------------- 223 | $(OUTPUT).3dsx : $(OUTPUT)_3ds.elf $(_3DSXDEPS) 224 | 225 | $(OFILES_SOURCES) : $(HFILES) 226 | 227 | $(OUTPUT)_3ds.elf : $(OFILES) 228 | 229 | #--------------------------------------------------------------------------------- 230 | # you need a rule like this for each extension you use as binary data 231 | #--------------------------------------------------------------------------------- 232 | %.bin.o %_bin.h : %.bin 233 | #--------------------------------------------------------------------------------- 234 | @echo $(notdir $<) 235 | @$(bin2o) 236 | 237 | #--------------------------------------------------------------------------------- 238 | .PRECIOUS : %.t3x 239 | #--------------------------------------------------------------------------------- 240 | %.t3x.o %_t3x.h : %.t3x 241 | #--------------------------------------------------------------------------------- 242 | @echo $(notdir $<) 243 | @$(bin2o) 244 | 245 | #--------------------------------------------------------------------------------- 246 | # rules for assembling GPU shaders 247 | #--------------------------------------------------------------------------------- 248 | define shader-as 249 | $(eval CURBIN := $*.shbin) 250 | $(eval DEPSFILE := $(DEPSDIR)/$*.shbin.d) 251 | echo "$(CURBIN).o: $< $1" > $(DEPSFILE) 252 | echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h 253 | echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h 254 | echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h 255 | picasso -o $(CURBIN) $1 256 | bin2s $(CURBIN) | $(AS) -o $*.shbin.o 257 | endef 258 | 259 | %.shbin.o %_shbin.h : %.v.pica %.g.pica 260 | @echo $(notdir $^) 261 | @$(call shader-as,$^) 262 | 263 | %.shbin.o %_shbin.h : %.v.pica 264 | @echo $(notdir $<) 265 | @$(call shader-as,$<) 266 | 267 | %.shbin.o %_shbin.h : %.shlist 268 | @echo $(notdir $<) 269 | @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)$(file))) 270 | 271 | #--------------------------------------------------------------------------------- 272 | %.t3x %.h : %.t3s 273 | #--------------------------------------------------------------------------------- 274 | @echo $(notdir $<) 275 | @tex3ds -i $< -H $*.h -d $*.d -o $*.t3x 276 | 277 | -include $(DEPSDIR)/*.d 278 | 279 | #--------------------------------------------------------------------------------------- 280 | endif 281 | #--------------------------------------------------------------------------------------- 282 | -------------------------------------------------------------------------------- /Makefile.blocksds: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: CC0-1.0 2 | # 3 | # SPDX-FileContributor: Antonio Niño Díaz, 2023 4 | 5 | # User config 6 | # =========== 7 | 8 | NAME := uxnds 9 | 10 | GAME_TITLE := uxnds v0.5.3 11 | GAME_SUBTITLE := varvara virtual machine 12 | GAME_AUTHOR := 30/03/2025 13 | GAME_ICON := misc/uxn32.png 14 | 15 | #NAME := donsol 16 | #GAME_TITLE = Donsol 17 | #GAME_AUTHOR := Rek & Devine 18 | #GAME_ICON := misc/donsol32.png 19 | #NITROFATDIR := romfs/nds 20 | 21 | # DLDI and internal SD slot of DSi 22 | # -------------------------------- 23 | 24 | # Root folder of the SD image 25 | SDROOT := sdroot 26 | # Name of the generated image it "DSi-1.sd" for no$gba in DSi mode 27 | SDIMAGE := image.bin 28 | 29 | # Tools 30 | # ----- 31 | 32 | MAKE := make 33 | RM := rm -rf 34 | 35 | # Verbose flag 36 | # ------------ 37 | 38 | ifeq ($(VERBOSE),1) 39 | V := 40 | else 41 | V := @ 42 | endif 43 | 44 | # Directories 45 | # ----------- 46 | 47 | ARM9DIR := arm9 48 | ARM7DIR := arm7 49 | 50 | # Build artfacts 51 | # -------------- 52 | 53 | NITROFAT_IMG := build_blocksds/nitrofat.bin 54 | ROM := $(NAME).nds 55 | ROM_DEBUG := $(NAME)_debug.nds 56 | ROM_PROFILE := $(NAME)_profile.nds 57 | 58 | # Targets 59 | # ------- 60 | 61 | .PHONY: all clean arm9 arm9debug arm9profile arm7 sdimage 62 | 63 | all: $(ROM) $(ROM_DEBUG) $(ROM_PROFILE) 64 | 65 | clean: 66 | @echo " CLEAN" 67 | $(V)$(MAKE) -f Makefile.blocksds.arm9 clean --no-print-directory DEBUG=false PROFILE=false 68 | $(V)$(MAKE) -f Makefile.blocksds.arm9 clean --no-print-directory DEBUG=true PROFILE=false 69 | $(V)$(MAKE) -f Makefile.blocksds.arm9 clean --no-print-directory DEBUG=false PROFILE=true 70 | $(V)$(MAKE) -f Makefile.blocksds.arm7 clean --no-print-directory 71 | $(V)$(RM) $(ROM) build $(SDIMAGE) 72 | 73 | arm9: 74 | $(V)+$(MAKE) -f Makefile.blocksds.arm9 --no-print-directory DEBUG=false PROFILE=false 75 | 76 | arm9debug: 77 | $(V)+$(MAKE) -f Makefile.blocksds.arm9 --no-print-directory DEBUG=true PROFILE=false 78 | 79 | arm9profile: 80 | $(V)+$(MAKE) -f Makefile.blocksds.arm9 --no-print-directory DEBUG=false PROFILE=true 81 | 82 | arm7: 83 | $(V)+$(MAKE) -f Makefile.blocksds.arm7 --no-print-directory 84 | 85 | ifneq ($(strip $(NITROFATDIR)),) 86 | # Additional arguments for ndstool 87 | NDSTOOL_FAT := -F $(NITROFAT_IMG) 88 | 89 | $(NITROFAT_IMG): $(NITROFATDIR) 90 | @echo " MKFATIMG $@ $(NITROFATDIR)" 91 | $(V)$(BLOCKSDS)/tools/mkfatimg/mkfatimg -t $(NITROFATDIR) $@ 0 92 | 93 | # Make the NDS ROM depend on the filesystem image only if it is needed 94 | $(ROM): $(NITROFAT_IMG) 95 | $(ROM_DEBUG): $(NITROFAT_IMG) 96 | $(ROM_PROFILE): $(NITROFAT_IMG) 97 | endif 98 | 99 | # Combine the title strings 100 | ifeq ($(strip $(GAME_SUBTITLE)),) 101 | GAME_FULL_TITLE := $(GAME_TITLE);$(GAME_AUTHOR) 102 | else 103 | GAME_FULL_TITLE := $(GAME_TITLE);$(GAME_SUBTITLE);$(GAME_AUTHOR) 104 | endif 105 | 106 | $(ROM): arm9 arm7 107 | @echo " NDSTOOL $@" 108 | $(V)$(BLOCKSDS)/tools/ndstool/ndstool -c $@ \ 109 | -7 build_blocksds/arm7/arm7.elf -9 build_blocksds/arm9/arm9.elf \ 110 | -b $(GAME_ICON) "$(GAME_FULL_TITLE)" \ 111 | $(NDSTOOL_FAT) 112 | 113 | $(ROM_DEBUG): arm9debug arm7 114 | @echo " NDSTOOL $@" 115 | $(V)$(BLOCKSDS)/tools/ndstool/ndstool -c $@ \ 116 | -7 build_blocksds/arm7/arm7.elf -9 build_blocksds/arm9_debug/arm9.elf \ 117 | -b $(GAME_ICON) "$(GAME_FULL_TITLE)" \ 118 | $(NDSTOOL_FAT) 119 | 120 | $(ROM_PROFILE): arm9profile arm7 121 | @echo " NDSTOOL $@" 122 | $(V)$(BLOCKSDS)/tools/ndstool/ndstool -c $@ \ 123 | -7 build_blocksds/arm7/arm7.elf -9 build_blocksds/arm9_profile/arm9.elf \ 124 | -b $(GAME_ICON) "$(GAME_FULL_TITLE)" \ 125 | $(NDSTOOL_FAT) 126 | 127 | sdimage: 128 | @echo " IMGBUILD $(SDIMAGE) $(SDROOT)" 129 | $(V)sh $(BLOCKSDS)/tools/imgbuild/imgbuild.sh $(SDIMAGE) $(SDROOT) 130 | -------------------------------------------------------------------------------- /Makefile.blocksds.arm7: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: CC0-1.0 2 | # 3 | # SPDX-FileContributor: Antonio Niño Díaz, 2023 4 | 5 | BLOCKSDS ?= /opt/blocksds/core 6 | BLOCKSDSEXT ?= /opt/blocksds/external 7 | 8 | WONDERFUL_TOOLCHAIN ?= /opt/wonderful 9 | ARM_NONE_EABI_PATH ?= $(WONDERFUL_TOOLCHAIN)/toolchain/gcc-arm-none-eabi/bin/ 10 | 11 | # Source code paths 12 | # ----------------- 13 | 14 | SOURCEDIRS := arm7/source 15 | INCLUDEDIRS := arm7/include include source 16 | BINDIRS := 17 | 18 | # Defines passed to all files 19 | # --------------------------- 20 | 21 | DEFINES := 22 | 23 | # Libraries 24 | # --------- 25 | 26 | LIBS := -lnds7 -lmm7 -lc 27 | LIBDIRS := $(BLOCKSDS)/libs/libnds \ 28 | $(BLOCKSDS)/libs/maxmod \ 29 | $(BLOCKSDS)/libs/libc7 30 | 31 | # Build artifacts 32 | # ----------------- 33 | 34 | BUILDDIR := build_blocksds/arm7 35 | NAME := arm7 36 | ELF := build_blocksds/arm7/$(NAME).elf 37 | DUMP := build_blocksds/arm7/$(NAME).dump 38 | MAP := build_blocksds/arm7/$(NAME).map 39 | 40 | # Tools 41 | # ----- 42 | 43 | PREFIX := $(ARM_NONE_EABI_PATH)arm-none-eabi- 44 | CC := $(PREFIX)gcc 45 | CXX := $(PREFIX)g++ 46 | OBJDUMP := $(PREFIX)objdump 47 | MKDIR := mkdir 48 | RM := rm -rf 49 | 50 | # Verbose flag 51 | # ------------ 52 | 53 | ifeq ($(VERBOSE),1) 54 | V := 55 | else 56 | V := @ 57 | endif 58 | 59 | # Source files 60 | # ------------ 61 | 62 | ifneq ($(BINDIRS),) 63 | SOURCES_BIN := $(shell find -L $(BINDIRS) -name "*.bin") 64 | INCLUDEDIRS += $(addprefix $(BUILDDIR)/,$(BINDIRS)) 65 | endif 66 | 67 | SOURCES_S := $(shell find -L $(SOURCEDIRS) -name "*.s") 68 | SOURCES_C := $(shell find -L $(SOURCEDIRS) -name "*.c") 69 | SOURCES_CPP := $(shell find -L $(SOURCEDIRS) -name "*.cpp") 70 | 71 | # Compiler and linker flags 72 | # ------------------------- 73 | 74 | DEFINES += -D__NDS__ -D__BLOCKSDS__ -DARM7 75 | 76 | ARCH := -mcpu=arm7tdmi 77 | 78 | WARNFLAGS := -Wall 79 | 80 | ifeq ($(SOURCES_CPP),) 81 | LD := $(CC) 82 | else 83 | LD := $(CXX) 84 | endif 85 | 86 | INCLUDEFLAGS := $(foreach path,$(INCLUDEDIRS),-I$(path)) \ 87 | $(foreach path,$(LIBDIRS),-I$(path)/include) 88 | 89 | LIBDIRSFLAGS := $(foreach path,$(LIBDIRS),-L$(path)/lib) 90 | 91 | ASFLAGS += -x assembler-with-cpp $(DEFINES) $(ARCH) \ 92 | -mthumb -mthumb-interwork $(INCLUDEFLAGS) \ 93 | -ffunction-sections -fdata-sections 94 | 95 | CFLAGS += -std=gnu11 $(WARNFLAGS) $(DEFINES) $(ARCH) \ 96 | -mthumb -mthumb-interwork $(INCLUDEFLAGS) -O3 \ 97 | -ffunction-sections -fdata-sections \ 98 | -fomit-frame-pointer 99 | 100 | CXXFLAGS += -std=gnu++14 $(WARNFLAGS) $(DEFINES) $(ARCH) \ 101 | -mthumb -mthumb-interwork $(INCLUDEFLAGS) -O3 \ 102 | -ffunction-sections -fdata-sections \ 103 | -fno-exceptions -fno-rtti \ 104 | -fomit-frame-pointer 105 | 106 | LDFLAGS := -mthumb -mthumb-interwork $(LIBDIRSFLAGS) \ 107 | -Wl,-Map,$(MAP) -Wl,--gc-sections -nostdlib \ 108 | -T$(BLOCKSDS)/sys/crts/ds_arm7.ld \ 109 | -Wl,--no-warn-rwx-segments \ 110 | -Wl,--start-group $(LIBS) -lgcc -Wl,--end-group 111 | 112 | # Intermediate build files 113 | # ------------------------ 114 | 115 | OBJS_ASSETS := $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_BIN))) 116 | 117 | HEADERS_ASSETS := $(patsubst %.bin,%_bin.h,$(addprefix $(BUILDDIR)/,$(SOURCES_BIN))) 118 | 119 | OBJS_SOURCES := $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_S))) \ 120 | $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_C))) \ 121 | $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_CPP))) 122 | 123 | OBJS := $(OBJS_ASSETS) $(OBJS_SOURCES) 124 | 125 | DEPS := $(OBJS:.o=.d) 126 | 127 | # Targets 128 | # ------- 129 | 130 | .PHONY: all clean dump 131 | 132 | all: $(ELF) 133 | 134 | $(ELF): $(OBJS) 135 | @echo " LD.7 $@" 136 | $(V)$(LD) -o $@ $(OBJS) $(BLOCKSDS)/sys/crts/ds_arm7_crt0.o $(LDFLAGS) 137 | 138 | $(DUMP): $(ELF) 139 | @echo " OBJDUMP.7 $@" 140 | $(V)$(OBJDUMP) -h -C -S $< > $@ 141 | 142 | dump: $(DUMP) 143 | 144 | clean: 145 | @echo " CLEAN.7" 146 | $(V)$(RM) $(ELF) $(DUMP) $(MAP) $(BUILDDIR) 147 | 148 | # Rules 149 | # ----- 150 | 151 | $(BUILDDIR)/%.s.o : %.s 152 | @echo " AS.7 $<" 153 | @$(MKDIR) -p $(@D) 154 | $(V)$(CC) $(ASFLAGS) -MMD -MP -c -o $@ $< 155 | 156 | $(BUILDDIR)/%.c.o : %.c 157 | @echo " CC.7 $<" 158 | @$(MKDIR) -p $(@D) 159 | $(V)$(CC) $(CFLAGS) -MMD -MP -c -o $@ $< 160 | 161 | $(BUILDDIR)/%.cpp.o : %.cpp 162 | @echo " CXX.7 $<" 163 | @$(MKDIR) -p $(@D) 164 | $(V)$(CXX) $(CXXFLAGS) -MMD -MP -c -o $@ $< 165 | 166 | $(BUILDDIR)/%.bin.o $(BUILDDIR)/%_bin.h : %.bin 167 | @echo " BIN2C.7 $<" 168 | @$(MKDIR) -p $(@D) 169 | $(V)$(BLOCKSDS)/tools/bin2c/bin2c $< $(@D) 170 | $(V)$(CC) $(CFLAGS) -MMD -MP -c -o $(BUILDDIR)/$*.bin.o $(BUILDDIR)/$*_bin.c 171 | 172 | # All assets must be built before the source code 173 | # ----------------------------------------------- 174 | 175 | $(SOURCES_S) $(SOURCES_C) $(SOURCES_CPP): $(HEADERS_ASSETS) 176 | 177 | # Include dependency files if they exist 178 | # -------------------------------------- 179 | 180 | -include $(DEPS) 181 | -------------------------------------------------------------------------------- /Makefile.blocksds.arm9: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: CC0-1.0 2 | # 3 | # SPDX-FileContributor: Antonio Niño Díaz, 2023 4 | 5 | ifeq ($(DEBUG),true) 6 | BUILDDIR := build_blocksds/arm9_debug 7 | else 8 | ifeq ($(PROFILE),true) 9 | BUILDDIR := build_blocksds/arm9_profile 10 | else 11 | BUILDDIR := build_blocksds/arm9 12 | endif 13 | endif 14 | 15 | BLOCKSDS ?= /opt/blocksds/core 16 | BLOCKSDSEXT ?= /opt/blocksds/external 17 | 18 | WONDERFUL_TOOLCHAIN ?= /opt/wonderful 19 | ARM_NONE_EABI_PATH ?= $(WONDERFUL_TOOLCHAIN)/toolchain/gcc-arm-none-eabi/bin/ 20 | 21 | # Source code paths 22 | # ----------------- 23 | 24 | SOURCEDIRS := source/devices source arm9/source 25 | INCLUDEDIRS := arm9/include include source $(BUILDDIR) 26 | GFXDIRS := assets/nds 27 | BINDIRS := 28 | AUDIODIRS := 29 | 30 | # Defines passed to all files 31 | # --------------------------- 32 | 33 | DEFINES := 34 | 35 | # Libraries 36 | # --------- 37 | 38 | LIBS := -lmm9 -lnds9 -lc 39 | LIBDIRS := $(BLOCKSDS)/libs/maxmod \ 40 | $(BLOCKSDS)/libs/libnds \ 41 | $(BLOCKSDS)/libs/libc9 42 | 43 | # Build artifacts 44 | # --------------- 45 | 46 | NAME := arm9 47 | ELF := $(BUILDDIR)/$(NAME).elf 48 | DUMP := $(BUILDDIR)/$(NAME).dump 49 | MAP := $(BUILDDIR)/$(NAME).map 50 | SOUNDBANKDIR := $(BUILDDIR)/maxmod 51 | 52 | # Tools 53 | # ----- 54 | 55 | PREFIX := $(ARM_NONE_EABI_PATH)arm-none-eabi- 56 | CC := $(PREFIX)gcc 57 | CXX := $(PREFIX)g++ 58 | OBJDUMP := $(PREFIX)objdump 59 | MKDIR := mkdir 60 | RM := rm -rf 61 | 62 | # Verbose flag 63 | # ------------ 64 | 65 | ifeq ($(VERBOSE),1) 66 | V := 67 | else 68 | V := @ 69 | endif 70 | 71 | # Source files 72 | # ------------ 73 | 74 | ifneq ($(BINDIRS),) 75 | SOURCES_BIN := $(shell find -L $(BINDIRS) -name "*.bin") 76 | INCLUDEDIRS += $(addprefix $(BUILDDIR)/,$(BINDIRS)) 77 | endif 78 | ifneq ($(GFXDIRS),) 79 | SOURCES_PNG := $(shell find -L $(GFXDIRS) -name "*.png") 80 | INCLUDEDIRS += $(addprefix $(BUILDDIR)/,$(GFXDIRS)) 81 | endif 82 | ifneq ($(AUDIODIRS),) 83 | SOURCES_AUDIO := $(shell find -L $(AUDIODIRS) -regex '.*\.\(it\|mod\|s3m\|wav\|xm\)') 84 | ifneq ($(SOURCES_AUDIO),) 85 | INCLUDEDIRS += $(SOUNDBANKDIR) 86 | endif 87 | endif 88 | 89 | SOURCES_S := $(shell find -L $(SOURCEDIRS) -maxdepth 1 -name "*.s") 90 | SOURCES_C := $(shell find -L $(SOURCEDIRS) -maxdepth 1 -name "*.c") 91 | SOURCES_CPP := $(shell find -L $(SOURCEDIRS) -maxdepth 1 -name "*.cpp") 92 | 93 | # Compiler and linker flags 94 | # ------------------------- 95 | 96 | DEFINES += -D__NDS__ -DARM9 -D__BLOCKSDS__ 97 | 98 | ARCH := -mcpu=arm946e-s+nofp 99 | 100 | WARNFLAGS := -Wall 101 | 102 | ifeq ($(SOURCES_CPP),) 103 | LD := $(CC) 104 | else 105 | LD := $(CXX) 106 | endif 107 | 108 | INCLUDEFLAGS := $(foreach path,$(INCLUDEDIRS),-I$(path)) \ 109 | $(foreach path,$(LIBDIRS),-I$(path)/include) 110 | 111 | LIBDIRSFLAGS := $(foreach path,$(LIBDIRS),-L$(path)/lib) 112 | 113 | ASFLAGS += -x assembler-with-cpp $(DEFINES) $(ARCH) \ 114 | -mthumb -mthumb-interwork $(INCLUDEFLAGS) \ 115 | -ffunction-sections -fdata-sections 116 | 117 | CFLAGS += -std=gnu11 $(WARNFLAGS) $(DEFINES) $(ARCH) \ 118 | -mthumb -mthumb-interwork $(INCLUDEFLAGS) -O3 \ 119 | -ffunction-sections -fdata-sections \ 120 | -fomit-frame-pointer -ffast-math 121 | 122 | 123 | CXXFLAGS += -std=gnu++14 $(WARNFLAGS) $(DEFINES) $(ARCH) \ 124 | -mthumb -mthumb-interwork $(INCLUDEFLAGS) -O3 \ 125 | -ffunction-sections -fdata-sections \ 126 | -fno-exceptions -fno-rtti \ 127 | -fomit-frame-pointer 128 | 129 | ifeq ($(DEBUG),true) 130 | CFLAGS += -DDEBUG -DCPU_ERROR_CHECKING -DDEBUG_PROFILE 131 | CXXFLAGS += -DDEBUG -DCPU_ERROR_CHECKING -DDEBUG_PROFILE 132 | else 133 | ifeq ($(PROFILE),true) 134 | CFLAGS += -DDEBUG -DDEBUG_PROFILE 135 | CXXFLAGS += -DDEBUG -DDEBUG_PROFILE 136 | endif 137 | endif 138 | 139 | LDFLAGS := -Wl,--defsym=vfprintf=__i_vfprintf -Wl,--defsym=vfscanf=__i_vfscanf \ 140 | -mthumb -mthumb-interwork $(LIBDIRSFLAGS) $(ARCH) \ 141 | -Wl,-Map,$(MAP) -Wl,--gc-sections -nostdlib \ 142 | -T$(BLOCKSDS)/sys/crts/ds_arm9.mem \ 143 | -T$(BLOCKSDS)/sys/crts/ds_arm9.ld \ 144 | -Wl,--no-warn-rwx-segments -Wl,--use-blx \ 145 | -Wl,--start-group $(LIBS) -lgcc -Wl,--end-group 146 | 147 | # Intermediate build files 148 | # ------------------------ 149 | 150 | OBJS_ASSETS := $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_BIN))) \ 151 | $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_PNG))) 152 | 153 | HEADERS_ASSETS := $(patsubst %.bin,%_bin.h,$(addprefix $(BUILDDIR)/,$(SOURCES_BIN))) \ 154 | $(patsubst %.png,%.h,$(addprefix $(BUILDDIR)/,$(SOURCES_PNG))) 155 | 156 | ifneq ($(SOURCES_AUDIO),) 157 | OBJS_ASSETS += $(SOUNDBANKDIR)/soundbank.c.o 158 | HEADERS_ASSETS += $(SOUNDBANKDIR)/soundbank.h 159 | endif 160 | 161 | OBJS_SOURCES := $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_S))) \ 162 | $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_C))) \ 163 | $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_CPP))) 164 | 165 | OBJS := $(OBJS_ASSETS) $(OBJS_SOURCES) 166 | 167 | DEPS := $(OBJS:.o=.d) 168 | 169 | # Targets 170 | # ------- 171 | 172 | .PHONY: all clean dump 173 | 174 | all: $(ELF) 175 | 176 | $(ELF): $(OBJS) 177 | @echo " LD.9 $@" 178 | $(V)$(LD) -o $@ $(OBJS) $(BLOCKSDS)/sys/crts/ds_arm9_crt0.o $(LDFLAGS) 179 | 180 | $(DUMP): $(ELF) 181 | @echo " OBJDUMP.9 $@" 182 | $(V)$(OBJDUMP) -h -C -S $< > $@ 183 | 184 | dump: $(DUMP) 185 | 186 | clean: 187 | @echo " CLEAN.9" 188 | $(V)$(RM) $(ELF) $(DUMP) $(MAP) $(BUILDDIR) 189 | 190 | # Rules 191 | # ----- 192 | 193 | $(BUILDDIR)/%.s.o : %.s 194 | @echo " AS.9 $<" 195 | @$(MKDIR) -p $(@D) 196 | $(V)$(CC) $(ASFLAGS) -MMD -MP -c -o $@ $< 197 | 198 | $(BUILDDIR)/%.c.o : %.c 199 | @echo " CC.9 $<" 200 | @$(MKDIR) -p $(@D) 201 | $(V)$(CC) $(CFLAGS) -MMD -MP -c -o $@ $< 202 | 203 | $(BUILDDIR)/%.cpp.o : %.cpp 204 | @echo " CXX.9 $<" 205 | @$(MKDIR) -p $(@D) 206 | $(V)$(CXX) $(CXXFLAGS) -MMD -MP -c -o $@ $< 207 | 208 | $(BUILDDIR)/%.bin.o $(BUILDDIR)/%_bin.h : %.bin 209 | @echo " BIN2C.9 $<" 210 | @$(MKDIR) -p $(@D) 211 | $(V)$(BLOCKSDS)/tools/bin2c/bin2c $< $(@D) 212 | $(V)$(CC) $(CFLAGS) -MMD -MP -c -o $(BUILDDIR)/$*.bin.o $(BUILDDIR)/$*_bin.c 213 | 214 | $(BUILDDIR)/%.png.o $(BUILDDIR)/%.h : %.png %.grit 215 | @echo " GRIT.9 $<" 216 | @$(MKDIR) -p $(@D) 217 | $(V)$(BLOCKSDS)/tools/grit/grit $< -ftc -W1 -o$(BUILDDIR)/$* 218 | $(V)$(CC) $(CFLAGS) -MMD -MP -c -o $(BUILDDIR)/$*.png.o $(BUILDDIR)/$*.c 219 | 220 | $(SOUNDBANKDIR)/soundbank.c.o $(SOUNDBANKDIR)/soundbank.h : $(SOURCES_AUDIO) 221 | @echo " MMUTIL $^" 222 | @$(MKDIR) -p $(@D) 223 | @$(BLOCKSDS)/tools/mmutil/mmutil $^ -d \ 224 | -o$(SOUNDBANKDIR)/soundbank.bin -h$(SOUNDBANKDIR)/soundbank.h 225 | $(V)$(BLOCKSDS)/tools/bin2c/bin2c $(SOUNDBANKDIR)/soundbank.bin \ 226 | $(SOUNDBANKDIR) 227 | $(V)$(CC) $(CFLAGS) -MMD -MP -c -o $(SOUNDBANKDIR)/soundbank.c.o \ 228 | $(SOUNDBANKDIR)/soundbank_bin.c 229 | 230 | # All assets must be built before the source code 231 | # ----------------------------------------------- 232 | 233 | $(SOURCES_S) $(SOURCES_C) $(SOURCES_CPP): $(HEADERS_ASSETS) 234 | 235 | # Include dependency files if they exist 236 | # -------------------------------------- 237 | 238 | -include $(DEPS) 239 | -------------------------------------------------------------------------------- /Makefile.nds: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | ifeq ($(strip $(DEVKITARM)),) 5 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 6 | endif 7 | 8 | export TARGET := $(shell basename $(CURDIR)) 9 | export TOPDIR := $(CURDIR) 10 | 11 | # specify a directory which contains the nitro filesystem 12 | # this is relative to the Makefile 13 | NITRO_FILES := 14 | 15 | # These set the information text in the nds file 16 | GAME_TITLE := uxnds v0.3.6 17 | GAME_SUBTITLE1 := tiny virtual machine 18 | GAME_SUBTITLE2 := 18/02/2023 19 | 20 | include $(DEVKITARM)/ds_rules 21 | 22 | .PHONY: checkarm7 checkarm9 checkarm9debug checkarm9profile clean 23 | 24 | #--------------------------------------------------------------------------------- 25 | # main targets 26 | #--------------------------------------------------------------------------------- 27 | all: checkarm7 checkarm9 checkarm9debug checkarm9profile $(TARGET).nds $(TARGET)_debug.nds $(TARGET)_profile.nds 28 | 29 | #--------------------------------------------------------------------------------- 30 | checkarm7: 31 | $(MAKE) -C arm7 32 | 33 | #--------------------------------------------------------------------------------- 34 | checkarm9: 35 | $(MAKE) -C arm9 DEBUG=false PROFILE=false 36 | 37 | checkarm9debug: 38 | $(MAKE) -C arm9 DEBUG=true PROFILE=false 39 | 40 | checkarm9profile: 41 | $(MAKE) -C arm9 DEBUG=false PROFILE=true 42 | 43 | #--------------------------------------------------------------------------------- 44 | $(TARGET).nds : $(NITRO_FILES) arm7/$(TARGET).elf arm9/$(TARGET).elf misc/uxn32.bmp 45 | ndstool -c $(TARGET).nds -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf \ 46 | -b misc/uxn32.bmp "$(GAME_TITLE);$(GAME_SUBTITLE1);$(GAME_SUBTITLE2)" \ 47 | $(_ADDFILES) 48 | 49 | $(TARGET)_debug.nds : $(NITRO_FILES) arm7/$(TARGET).elf arm9/$(TARGET)_debug.elf misc/uxn32.bmp 50 | ndstool -c $(TARGET)_debug.nds -7 arm7/$(TARGET).elf -9 arm9/$(TARGET)_debug.elf \ 51 | -b misc/uxn32.bmp "$(GAME_TITLE) (debug);$(GAME_SUBTITLE1);$(GAME_SUBTITLE2)" \ 52 | $(_ADDFILES) 53 | 54 | $(TARGET)_profile.nds : $(NITRO_FILES) arm7/$(TARGET).elf arm9/$(TARGET)_profile.elf misc/uxn32.bmp 55 | ndstool -c $(TARGET)_profile.nds -7 arm7/$(TARGET).elf -9 arm9/$(TARGET)_profile.elf \ 56 | -b misc/uxn32.bmp "$(GAME_TITLE) (profile);$(GAME_SUBTITLE1);$(GAME_SUBTITLE2)" \ 57 | $(_ADDFILES) 58 | 59 | #--------------------------------------------------------------------------------- 60 | arm7/$(TARGET).elf: 61 | $(MAKE) -C arm7 62 | 63 | #--------------------------------------------------------------------------------- 64 | arm9/$(TARGET).elf: 65 | $(MAKE) -C arm9 DEBUG=false PROFILE=false 66 | 67 | arm9/$(TARGET)_debug.elf: 68 | $(MAKE) -C arm9 DEBUG=true PROFILE=false 69 | 70 | arm9/$(TARGET)_profile.elf: 71 | $(MAKE) -C arm9 DEBUG=false PROFILE=true 72 | 73 | #--------------------------------------------------------------------------------- 74 | clean: 75 | $(MAKE) -C arm9 clean 76 | $(MAKE) -C arm7 clean 77 | rm -f $(TARGET).nds $(TARGET).arm7 $(TARGET).arm9 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uxnds 2 | 3 | Quick and simple port of the [uxn virtual machine](https://wiki.xxiivv.com/site/uxn.html) to the 4 | NDS and 3DS consoles. 5 | 6 | You can download the latest version and source code [here](https://github.com/asiekierka/uxnds/releases). 7 | 8 | ## Usage 9 | 10 | By default, uxnds will run /uxn/boot.rom or /uxn/launcher.rom. It also supports reading files from within /uxn. 11 | 12 | On start, a keyboard is presented on the bottom screen, and the uxn display - on the top screen. 13 | Use the L or R buttons to swap them - in this configuration, mouse input is approximated via 14 | touchscreen. 15 | 16 | You can use the system button in the lower-left corner to reset the uxn virtual machine. 17 | 18 | ## Installation 19 | 20 | Two ports are provided: the 3DS port (compatible with 3DS consoles) and the NDS port (compatible with DS, DSi and 3DS consoles). 21 | 22 | ### 3DS port 23 | 24 | There is one binary provided: uxnds.3dsx. 25 | 26 | To compile uxnds for 3DS, you may use: 27 | 28 | * the latest devkitARM toolchain from the devkitPro organization to compile. After [installing](https://devkitpro.org/wiki/Getting_Started), simply run `make -f Makefile.3ds`. 29 | 30 | ### NDS port 31 | 32 | When using a real DS, DSi or 3DS console, it is recommended to launch this program via 33 | [nds-hb-menu](https://github.com/devkitPro/nds-hb-menu) - though, as it currently doesn't use argc/argv, 34 | it doesn't really change much. 35 | 36 | There are three binaries provided: 37 | 38 | * uxnds.nds - faster, but best used only with known-good software, 39 | * uxnds_debug.nds - slower, but provides debugging information, profiling information and performs CPU stack bounds checks. 40 | * uxnds_profile.nds - almost as fast as uxnds.nds - with debugging/profiling information, no CPU stack bounds checks. 41 | 42 | To compile uxnds for NDS, you may use: 43 | 44 | * the [BlocksDS toolchain](https://github.com/blocksds/sdk) - run `make -f Makefile.blocksds`; 45 | * the latest devkitARM toolchain from the devkitPro organization to compile. After [installing](https://devkitpro.org/wiki/Getting_Started), simply run `make -f Makefile.nds`. 46 | -------------------------------------------------------------------------------- /arm7/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | ifeq ($(strip $(DEVKITARM)),) 5 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 6 | endif 7 | 8 | include $(DEVKITARM)/ds_rules 9 | 10 | #--------------------------------------------------------------------------------- 11 | # BUILD is the directory where object files & intermediate files will be placed 12 | # SOURCES is a list of directories containing source code 13 | # INCLUDES is a list of directories containing extra header files 14 | # DATA is a list of directories containing binary files 15 | # all directories are relative to this makefile 16 | #--------------------------------------------------------------------------------- 17 | BUILD := build 18 | SOURCES := source 19 | INCLUDES := include ../include ../source build 20 | DATA := 21 | 22 | #--------------------------------------------------------------------------------- 23 | # options for code generation 24 | #--------------------------------------------------------------------------------- 25 | ARCH := -mthumb-interwork 26 | 27 | CFLAGS := -g -Wall -O2\ 28 | -mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\ 29 | -ffast-math \ 30 | $(ARCH) 31 | 32 | CFLAGS += $(INCLUDE) -DARM7 33 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -fno-rtti 34 | 35 | 36 | ASFLAGS := -g $(ARCH) 37 | LDFLAGS = -specs=ds_arm7.specs -g $(ARCH) -Wl,--nmagic -Wl,-Map,$(notdir $*).map 38 | 39 | LIBS := -lnds7 40 | 41 | #--------------------------------------------------------------------------------- 42 | # list of directories containing libraries, this must be the top level containing 43 | # include and lib 44 | #--------------------------------------------------------------------------------- 45 | LIBDIRS := $(LIBNDS) 46 | 47 | 48 | #--------------------------------------------------------------------------------- 49 | # no real need to edit anything past this point unless you need to add additional 50 | # rules for different file extensions 51 | #--------------------------------------------------------------------------------- 52 | ifneq ($(BUILD),$(notdir $(CURDIR))) 53 | #--------------------------------------------------------------------------------- 54 | 55 | export ARM7ELF := $(CURDIR)/$(TARGET).elf 56 | export DEPSDIR := $(CURDIR)/$(BUILD) 57 | 58 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) 59 | 60 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 61 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 62 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 63 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 64 | 65 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 66 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 67 | 68 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 69 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 70 | -I$(CURDIR)/$(BUILD) 71 | 72 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 73 | 74 | #--------------------------------------------------------------------------------- 75 | # use CXX for linking C++ projects, CC for standard C 76 | #--------------------------------------------------------------------------------- 77 | ifeq ($(strip $(CPPFILES)),) 78 | #--------------------------------------------------------------------------------- 79 | export LD := $(CC) 80 | #--------------------------------------------------------------------------------- 81 | else 82 | #--------------------------------------------------------------------------------- 83 | export LD := $(CXX) 84 | #--------------------------------------------------------------------------------- 85 | endif 86 | #--------------------------------------------------------------------------------- 87 | 88 | .PHONY: $(BUILD) clean 89 | 90 | #--------------------------------------------------------------------------------- 91 | $(BUILD): 92 | @[ -d $@ ] || mkdir -p $@ 93 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 94 | 95 | #--------------------------------------------------------------------------------- 96 | clean: 97 | @echo clean ... 98 | @rm -fr $(BUILD) *.elf 99 | 100 | 101 | #--------------------------------------------------------------------------------- 102 | else 103 | 104 | DEPENDS := $(OFILES:.o=.d) 105 | 106 | #--------------------------------------------------------------------------------- 107 | # main targets 108 | #--------------------------------------------------------------------------------- 109 | $(ARM7ELF) : $(OFILES) 110 | @echo linking $(notdir $@) 111 | @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ 112 | 113 | 114 | #--------------------------------------------------------------------------------- 115 | # you need a rule like this for each extension you use as binary data 116 | #--------------------------------------------------------------------------------- 117 | %.bin.o : %.bin 118 | #--------------------------------------------------------------------------------- 119 | @echo $(notdir $<) 120 | @$(bin2o) 121 | 122 | -include $(DEPENDS) 123 | 124 | #--------------------------------------------------------------------------------------- 125 | endif 126 | #--------------------------------------------------------------------------------------- 127 | -------------------------------------------------------------------------------- /arm7/source/apu.c: -------------------------------------------------------------------------------- 1 | #include "uxn.h" 2 | #include "nds/apu.h" 3 | 4 | /* 5 | Copyright (c) 2021 Devine Lu Linvega 6 | Copyright (c) 2021 Andrew Alderwick 7 | Copyright (c) 2021 Adrian "asie" Siekierka 8 | 9 | Permission to use, copy, modify, and distribute this software for any 10 | purpose with or without fee is hereby granted, provided that the above 11 | copyright notice and this permission notice appear in all copies. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 | WITH REGARD TO THIS SOFTWARE. 15 | */ 16 | 17 | static Sint16 18 | envelope(NdsApu *c, Uint32 age) 19 | { 20 | if(!c->r) return 0x0888; 21 | if(age < c->a) return 0x0888 * age / c->a; 22 | if(age < c->d) return 0x0444 * (2 * c->d - c->a - age) / (c->d - c->a); 23 | if(age < c->s) return 0x0444; 24 | if(age < c->r) return 0x0444 * (c->r - age) / (c->r - c->s); 25 | c->advance = 0; 26 | return 0x0000; 27 | } 28 | 29 | void 30 | nds_apu_render(NdsApu *c, Sint16 *sample_left, Sint16 *sample_right, int samples) 31 | { 32 | Sint32 s, i; 33 | if(!c->advance || !c->period) return; 34 | for(i = 0; i < samples; i++) { 35 | c->count += c->advance; 36 | c->i += c->count / c->period; 37 | c->count %= c->period; 38 | if(c->i >= c->len) { 39 | if(!c->repeat) { 40 | c->advance = 0; 41 | return; 42 | } 43 | c->i %= c->len; 44 | } 45 | s = (Sint8)(c->addr[c->i] + 0x80) * envelope(c, c->age++); 46 | *sample_left++ += s * c->volume[0] / 0x180; 47 | *sample_right++ += s * c->volume[1] / 0x180; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /arm7/source/main.c: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------- 2 | 3 | Copyright (c) 2021 Adrian "asie" Siekierka 4 | 5 | Permission to use, copy, modify, and distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE. 11 | 12 | Based on the devkitARM/libnds examples' default ARM7 core. 13 | 14 | Copyright (C) 2005 - 2010 15 | Michael Noland (joat) 16 | Jason Rogers (dovoto) 17 | Dave Murphy (WinterMute) 18 | 19 | This software is provided 'as-is', without any express or implied 20 | warranty. In no event will the authors be held liable for any 21 | damages arising from the use of this software. 22 | 23 | Permission is granted to anyone to use this software for any 24 | purpose, including commercial applications, and to alter it and 25 | redistribute it freely, subject to the following restrictions: 26 | 27 | 1. The origin of this software must not be misrepresented; you 28 | must not claim that you wrote the original software. If you use 29 | this software in a product, an acknowledgment in the product 30 | documentation would be appreciated but is not required. 31 | 32 | 2. Altered source versions must be plainly marked as such, and 33 | must not be misrepresented as being the original software. 34 | 35 | 3. This notice may not be removed or altered from any source 36 | distribution. 37 | 38 | ---------------------------------------------------------------------------------*/ 39 | #include 40 | #include 41 | #include 42 | #include "uxn.h" 43 | #include "nds/apu.h" 44 | #include "nds/fifo.h" 45 | 46 | static u16 sampling_freq, sampling_bufsize; 47 | static u16 sampling_timer_freq; 48 | static s16 *sampling_addr; 49 | static u8 sampling_pos; 50 | static NdsApu apus[POLYPHONY]; 51 | 52 | void apu_handler() { 53 | memset(sampling_addr, 0, sampling_bufsize * 2); 54 | memset(sampling_addr + (sampling_bufsize * 2), 0, sampling_bufsize * 2); 55 | 56 | for (int i = 0; i < 4; i++) { 57 | nds_apu_render(&apus[i], sampling_addr, sampling_addr + (sampling_bufsize * 2), sampling_bufsize); 58 | } 59 | 60 | if (sampling_pos) { 61 | sampling_addr -= sampling_bufsize; 62 | sampling_pos = 0; 63 | } else { 64 | sampling_addr += sampling_bufsize; 65 | sampling_pos = 1; 66 | } 67 | } 68 | 69 | void fifo_handler(u32 cmd, void *unused) { 70 | switch (cmd & UXNDS_FIFO_CMD_MASK) { 71 | case UXNDS_FIFO_CMD_SET_RATE: 72 | // init sound hardware 73 | powerOn(POWER_SOUND); 74 | writePowerManagement(PM_CONTROL_REG, ( readPowerManagement(PM_CONTROL_REG) & ~PM_SOUND_MUTE ) | PM_SOUND_AMP ); 75 | REG_SOUNDCNT = SOUND_ENABLE | 127; 76 | sampling_freq = cmd & 0xFFFF; 77 | sampling_timer_freq = (((BUS_CLOCK >> 1) + (sampling_freq >> 1)) / sampling_freq) ^ 0xFFFF; 78 | break; 79 | case UXNDS_FIFO_CMD_SET_ADDR: 80 | sampling_addr = (s16*) (cmd & ~UXNDS_FIFO_CMD_MASK); 81 | sampling_pos = 0; 82 | sampling_bufsize = UXNDS_AUDIO_BUFFER_SIZE; 83 | 84 | SCHANNEL_CR(0) = 0; 85 | SCHANNEL_TIMER(0) = sampling_timer_freq; 86 | SCHANNEL_SOURCE(0) = (vu32) sampling_addr; 87 | SCHANNEL_LENGTH(0) = sampling_bufsize; 88 | 89 | SCHANNEL_CR(0) = SCHANNEL_ENABLE | SOUND_VOL(96) | SOUND_PAN(0) | SOUND_FORMAT_16BIT | SOUND_REPEAT; 90 | 91 | SCHANNEL_CR(1) = 0; 92 | SCHANNEL_TIMER(1) = sampling_timer_freq; 93 | SCHANNEL_SOURCE(1) = (vu32) (sampling_addr + (sampling_bufsize * 2)); 94 | SCHANNEL_LENGTH(1) = sampling_bufsize; 95 | 96 | SCHANNEL_CR(1) = SCHANNEL_ENABLE | SOUND_VOL(96) | SOUND_PAN(127) | SOUND_FORMAT_16BIT | SOUND_REPEAT; 97 | 98 | TIMER_DATA(0) = sampling_timer_freq; 99 | TIMER_CR(0) = TIMER_IRQ_REQ | TIMER_ENABLE | ClockDivider_1024; 100 | break; 101 | case UXNDS_FIFO_CMD_APU0: 102 | case UXNDS_FIFO_CMD_APU1: 103 | case UXNDS_FIFO_CMD_APU2: 104 | case UXNDS_FIFO_CMD_APU3: 105 | NdsApu *apus_remote = (NdsApu*) (cmd & ~UXNDS_FIFO_CMD_MASK); 106 | int oldIME = enterCriticalSection(); 107 | apus[(cmd >> 28) & 0x03] = apus_remote[(cmd >> 28) & 0x03]; 108 | leaveCriticalSection(oldIME); 109 | // fifoSendValue32(UXNDS_FIFO_CHANNEL, 0); 110 | break; 111 | } 112 | } 113 | 114 | void VcountHandler() { 115 | inputGetAndSend(); 116 | } 117 | 118 | volatile bool exitflag = false; 119 | 120 | void powerButtonCB() { 121 | exitflag = true; 122 | } 123 | 124 | int main() { 125 | #ifdef __BLOCKSDS__ 126 | enableSound(); 127 | #else 128 | dmaFillWords(0, (void*)0x04000400, 0x100); 129 | 130 | REG_SOUNDCNT |= SOUND_ENABLE; 131 | writePowerManagement(PM_CONTROL_REG, ( readPowerManagement(PM_CONTROL_REG) & ~PM_SOUND_MUTE ) | PM_SOUND_AMP ); 132 | powerOn(POWER_SOUND); 133 | #endif 134 | 135 | readUserSettings(); 136 | ledBlink(0); 137 | 138 | irqInit(); 139 | fifoInit(); 140 | touchInit(); 141 | 142 | SetYtrigger(80); 143 | 144 | fifoSetValue32Handler(UXNDS_FIFO_CHANNEL, fifo_handler, NULL); 145 | installSystemFIFO(); 146 | 147 | irqSet(IRQ_VBLANK, VcountHandler); 148 | irqSet(IRQ_TIMER0, apu_handler); 149 | 150 | #ifdef __BLOCKSDS__ 151 | initClockIRQTimer(3); 152 | irqEnable(IRQ_VBLANK | IRQ_VCOUNT | IRQ_TIMER0); 153 | #else 154 | initClockIRQ(); 155 | irqEnable(IRQ_VBLANK | IRQ_VCOUNT | IRQ_NETWORK | IRQ_TIMER0); 156 | #endif 157 | 158 | setPowerButtonCB(powerButtonCB); 159 | 160 | while (!exitflag) { 161 | if ( 0 == (REG_KEYINPUT & (KEY_SELECT | KEY_START | KEY_L | KEY_R))) { 162 | exitflag = true; 163 | } 164 | swiWaitForVBlank(); 165 | } 166 | return 0; 167 | } 168 | -------------------------------------------------------------------------------- /arm9/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | ifeq ($(strip $(DEVKITARM)),) 5 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 6 | endif 7 | 8 | include $(DEVKITARM)/ds_rules 9 | 10 | #--------------------------------------------------------------------------------- 11 | # BUILD is the directory where object files & intermediate files will be placed 12 | # SOURCES is a list of directories containing source code 13 | # INCLUDES is a list of directories containing extra header files 14 | # DATA is a list of directories containing binary files 15 | # all directories are relative to this makefile 16 | #--------------------------------------------------------------------------------- 17 | ifeq ($(DEBUG),true) 18 | BUILD := build_debug 19 | else 20 | ifeq ($(PROFILE),true) 21 | BUILD := build_profile 22 | else 23 | BUILD := build 24 | endif 25 | endif 26 | SOURCES := ../source/devices source ../source 27 | INCLUDES := include ../include ../source $(BUILD) 28 | DATA := 29 | GRAPHICS := ../assets/nds 30 | 31 | 32 | #--------------------------------------------------------------------------------- 33 | # options for code generation 34 | #--------------------------------------------------------------------------------- 35 | ARCH := -mthumb -mthumb-interwork 36 | 37 | CFLAGS := -g -Wall -O3 \ 38 | -march=armv5te -mtune=arm946e-s -fomit-frame-pointer \ 39 | -ffast-math \ 40 | $(ARCH) 41 | 42 | CFLAGS += $(INCLUDE) -DARM9 43 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions 44 | 45 | ifeq ($(DEBUG),true) 46 | CFLAGS += -DDEBUG -DCPU_ERROR_CHECKING -DDEBUG_PROFILE 47 | CXXFLAGS += -DDEBUG -DCPU_ERROR_CHECKING -DDEBUG_PROFILE 48 | else 49 | ifeq ($(PROFILE),true) 50 | CFLAGS += -DDEBUG -DDEBUG_PROFILE 51 | CXXFLAGS += -DDEBUG -DDEBUG_PROFILE 52 | endif 53 | endif 54 | 55 | ASFLAGS := -g $(ARCH) -march=armv5te -mtune=arm946e-s 56 | 57 | LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 58 | 59 | #--------------------------------------------------------------------------------- 60 | # any extra libraries we wish to link with the project 61 | #--------------------------------------------------------------------------------- 62 | LIBS := -lfilesystem -lfat -lnds9 63 | 64 | #--------------------------------------------------------------------------------- 65 | # list of directories containing libraries, this must be the top level containing 66 | # include and lib 67 | #--------------------------------------------------------------------------------- 68 | LIBDIRS := $(LIBNDS) 69 | 70 | #--------------------------------------------------------------------------------- 71 | # no real need to edit anything past this point unless you need to add additional 72 | # rules for different file extensions 73 | #--------------------------------------------------------------------------------- 74 | ifneq ($(BUILD),$(notdir $(CURDIR))) 75 | #--------------------------------------------------------------------------------- 76 | 77 | ifeq ($(DEBUG),true) 78 | export ARM9ELF := $(CURDIR)/$(TARGET)_debug.elf 79 | else 80 | ifeq ($(PROFILE),true) 81 | export ARM9ELF := $(CURDIR)/$(TARGET)_profile.elf 82 | else 83 | export ARM9ELF := $(CURDIR)/$(TARGET).elf 84 | endif 85 | endif 86 | 87 | export DEPSDIR := $(CURDIR)/$(BUILD) 88 | 89 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 90 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ 91 | $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) 92 | 93 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 94 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 95 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 96 | PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png))) 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 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 114 | $(PNGFILES:.png=.o) \ 115 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 116 | 117 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 118 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 119 | -I$(CURDIR)/$(BUILD) 120 | 121 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 122 | 123 | .PHONY: $(BUILD) clean 124 | 125 | #--------------------------------------------------------------------------------- 126 | $(BUILD): 127 | @[ -d $@ ] || mkdir -p $@ 128 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 129 | 130 | #--------------------------------------------------------------------------------- 131 | clean: 132 | @echo clean ... 133 | @rm -fr build build_debug build_profile *.elf *.nds* *.bin 134 | 135 | 136 | #--------------------------------------------------------------------------------- 137 | else 138 | 139 | #--------------------------------------------------------------------------------- 140 | # main targets 141 | #--------------------------------------------------------------------------------- 142 | $(ARM9ELF) : $(OFILES) 143 | @echo linking $(notdir $@) 144 | @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ 145 | 146 | #--------------------------------------------------------------------------------- 147 | # you need a rule like this for each extension you use as binary data 148 | #--------------------------------------------------------------------------------- 149 | %.bin.o : %.bin 150 | #--------------------------------------------------------------------------------- 151 | @echo $(notdir $<) 152 | @$(bin2o) 153 | 154 | #--------------------------------------------------------------------------------- 155 | # This rule creates assembly source files using grit 156 | # grit takes an image file and a .grit describing how the file is to be processed 157 | # add additional rules like this for each image extension 158 | # you use in the graphics folders 159 | #--------------------------------------------------------------------------------- 160 | %.s %.h: %.png %.grit 161 | #--------------------------------------------------------------------------------- 162 | grit $< -fts -o$* 163 | 164 | -include $(DEPSDIR)/*.d 165 | 166 | #--------------------------------------------------------------------------------------- 167 | endif 168 | #--------------------------------------------------------------------------------------- 169 | -------------------------------------------------------------------------------- /arm9/source/emulator.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "uxn.h" 7 | #include "util.h" 8 | #include "nds/apu.h" 9 | #include "nds/fifo.h" 10 | #include "nds_keyboard.h" 11 | #include "nds_ppu.h" 12 | #include "devices/datetime.h" 13 | #include "devices/file.h" 14 | #include "devices/system.h" 15 | #include "emulator_config.h" 16 | 17 | /* 18 | Copyright (c) 2021 Devine Lu Linvega 19 | Copyright (c) 2021 Adrian "asie" Siekierka 20 | 21 | Permission to use, copy, modify, and distribute this software for any 22 | purpose with or without fee is hereby granted, provided that the above 23 | copyright notice and this permission notice appear in all copies. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 26 | WITH REGARD TO THIS SOFTWARE. 27 | */ 28 | 29 | DTCM_BSS 30 | static NdsPpu ppu; 31 | static NdsApu apu[POLYPHONY]; 32 | static u32 apu_samples[(UXNDS_AUDIO_BUFFER_SIZE * 4) >> 1]; 33 | 34 | Uint8 dispswap; 35 | Uint8 debug = 0; 36 | 37 | static PrintConsole *mainConsole; 38 | #ifdef DEBUG 39 | #ifdef DEBUG_PROFILE 40 | static PrintConsole profileConsole; 41 | #endif 42 | #endif 43 | 44 | #define TICK_RESET_KEYS (KEY_X | KEY_Y) 45 | 46 | int prompt_reset(Uxn *u); 47 | 48 | int 49 | error(char *msg, const char *err) 50 | { 51 | dprintf("Error %s: %s\n", msg, err); 52 | while(1) { 53 | swiWaitForVBlank(); 54 | } 55 | } 56 | 57 | void 58 | quit(void) 59 | { 60 | #ifdef ENABLE_KEYBOARD 61 | keyboard_exit(); 62 | #endif 63 | exit(0); 64 | } 65 | 66 | int 67 | init(void) 68 | { 69 | if(!nds_initppu(&ppu)) 70 | return error("PPU", "Init failure"); 71 | fifoSendValue32(UXNDS_FIFO_CHANNEL, UXNDS_FIFO_CMD_SET_RATE | SAMPLE_FREQUENCY); 72 | fifoSendValue32(UXNDS_FIFO_CHANNEL, UXNDS_FIFO_CMD_SET_ADDR | ((u32) (&apu_samples))); 73 | #ifdef ENABLE_KEYBOARD 74 | keyboard_init(); 75 | #endif 76 | return 1; 77 | } 78 | 79 | DTCM_BSS 80 | Uxn u; 81 | 82 | #pragma mark - Devices 83 | 84 | ITCM_ARM_CODE 85 | static Uint8 86 | screen_dei(Uint8 *d, Uint8 port) 87 | { 88 | switch(port) { 89 | case 0x2: return PPU_PIXELS_WIDTH >> 8; 90 | case 0x3: return PPU_PIXELS_WIDTH & 0xFF; 91 | case 0x4: return PPU_PIXELS_HEIGHT >> 8; 92 | case 0x5: return PPU_PIXELS_HEIGHT & 0xFF; 93 | default: return d[port]; 94 | } 95 | } 96 | 97 | ITCM_ARM_CODE 98 | static void 99 | screen_deo(Uint8 *d, Uint8 port) 100 | { 101 | if(port == 0xe) { 102 | Uint8 ctrl = d[0xe]; 103 | Uint8 color = ctrl & 0x3; 104 | Uint16 x = PEEK2(d + 0x8); 105 | Uint16 y = PEEK2(d + 0xa); 106 | Uint32 *layer = (ctrl & 0x40) ? ppu.fg : ppu.bg; 107 | /* fill mode */ 108 | if(ctrl & 0x80) { 109 | Uint16 x2 = PPU_TILES_WIDTH * 8; 110 | Uint16 y2 = PPU_TILES_HEIGHT * 8; 111 | if(ctrl & 0x10) x2 = x, x = 0; 112 | if(ctrl & 0x20) y2 = y, y = 0; 113 | nds_ppu_fill(&ppu, layer, x, y, x2, y2, color); 114 | } 115 | /* pixel mode */ 116 | else { 117 | Uint16 x = peek16(d, 0x8); 118 | Uint16 y = peek16(d, 0xa); 119 | nds_ppu_pixel(&ppu, layer, x, y, d[0xe] & 0x3); 120 | if(d[0x6] & 0x1) POKE2(d + 0x8, x + 1); /* auto x+1 */ 121 | if(d[0x6] & 0x2) POKE2(d + 0xa, y + 1); /* auto y+1 */ 122 | } 123 | } else if(port == 0xf) { 124 | Uint8 twobpp = d[0xf] & 0x80; 125 | Uint16 x = peek16(d, 0x8); 126 | Uint16 y = peek16(d, 0xa); 127 | Uint32 *layer = d[0xf] & 0x40 ? ppu.fg : ppu.bg; 128 | Uint16 addr = peek16(d, 0xc); 129 | Uint8 n = d[0x6] >> 4; 130 | Uint8 dx = (d[0x6] & 0x01) << 3; 131 | Uint8 dy = (d[0x6] & 0x02) << 2; 132 | int flipx = (d[0xf] & 0x10), fx = flipx ? -1 : 1; 133 | int flipy = (d[0xf] & 0x20), fy = flipy ? -1 : 1; 134 | Uint16 dyx = dy * fx, dxy = dx * fy; 135 | Uint16 len = (n + 1) << (3 + twobpp); 136 | if(addr > (0x10000 - len)) return; 137 | for (Uint8 i = 0; i <= n; i++) { 138 | if (twobpp) { 139 | nds_ppu_2bpp(&ppu, layer, x + dyx * i, y + dxy * i, &u.ram.dat[addr], d[0xf] & 0xf, flipx, flipy); 140 | addr += (d[0x6] & 0x04) << 2; 141 | } else { 142 | nds_ppu_1bpp(&ppu, layer, x + dyx * i, y + dxy * i, &u.ram.dat[addr], d[0xf] & 0xf, flipx, flipy); 143 | addr += (d[0x6] & 0x04) << 1; 144 | } 145 | } 146 | poke16(d, 0x8, x + dx * fx); /* auto x+dx */ 147 | poke16(d, 0xa, y + dy * fy); /* auto y+dy */ 148 | poke16(d, 0xc, addr); /* auto addr */ 149 | } 150 | } 151 | 152 | ITCM_ARM_CODE 153 | static Uint8 154 | audio_dei(int instance_id, Uint8 *d, Uint8 port) 155 | { 156 | NdsApu *instance = &apu[instance_id]; 157 | switch(port) { 158 | case 0x4: return nds_apu_get_vu(instance); 159 | case 0x2: POKDEV(0x2, instance->i); /* fall through */ 160 | default: return d[port]; 161 | } 162 | } 163 | 164 | ITCM_ARM_CODE 165 | static void 166 | audio_deo(int instance_id, Uint8 *d, Uint8 port) 167 | { 168 | NdsApu *instance = memUncached(&apu[instance_id]); 169 | if(port == 0xf) { 170 | Uint16 addr = peek16(d, 0xc); 171 | instance->len = peek16(d, 0xa); 172 | if(instance->len > 0x10000 - addr) 173 | instance->len = 0x10000 - addr; 174 | instance->addr = &u.ram.dat[addr]; 175 | instance->volume[0] = d[0xe] >> 4; 176 | instance->volume[1] = d[0xe] & 0xf; 177 | instance->repeat = !(d[0xf] & 0x80); 178 | Uint8 detune = d[0x5]; 179 | nds_apu_start(instance, peek16(d, 0x8), d[0xf] & 0x7f, detune); 180 | fifoSendValue32(UXNDS_FIFO_CHANNEL, (UXNDS_FIFO_CMD_APU0 + ((instance_id) << 28)) 181 | | ((u32) (&apu))); 182 | // fifoWaitValue32(UXNDS_FIFO_CHANNEL); 183 | // fifoGetValue32(UXNDS_FIFO_CHANNEL); 184 | } 185 | } 186 | 187 | ITCM_ARM_CODE 188 | static Uint8 audio0_dei(Uint8 *d, Uint8 port) { return audio_dei(0, d, port); } 189 | ITCM_ARM_CODE 190 | static Uint8 audio1_dei(Uint8 *d, Uint8 port) { return audio_dei(1, d, port); } 191 | ITCM_ARM_CODE 192 | static Uint8 audio2_dei(Uint8 *d, Uint8 port) { return audio_dei(2, d, port); } 193 | ITCM_ARM_CODE 194 | static Uint8 audio3_dei(Uint8 *d, Uint8 port) { return audio_dei(3, d, port); } 195 | ITCM_ARM_CODE 196 | static void audio0_deo(Uint8 *d, Uint8 port) { audio_deo(0, d, port); } 197 | ITCM_ARM_CODE 198 | static void audio1_deo(Uint8 *d, Uint8 port) { audio_deo(1, d, port); } 199 | ITCM_ARM_CODE 200 | static void audio2_deo(Uint8 *d, Uint8 port) { audio_deo(2, d, port); } 201 | ITCM_ARM_CODE 202 | static void audio3_deo(Uint8 *d, Uint8 port) { audio_deo(3, d, port); } 203 | ITCM_ARM_CODE 204 | static void file0_deo(Uint8 *d, Uint8 port) { file_deo(&u, 0xa0+port); } 205 | ITCM_ARM_CODE 206 | static void file1_deo(Uint8 *d, Uint8 port) { file_deo(&u, 0xb0+port); } 207 | 208 | ITCM_ARM_CODE 209 | static Uint8 nds_system_dei(Uint8 *d, Uint8 port) { return system_dei(&u, port); } 210 | 211 | ITCM_ARM_CODE 212 | static void 213 | nds_system_deo(Uint8 *d, Uint8 port) 214 | { 215 | system_deo(&u, d, port); 216 | if(port > 0x7 && port < 0xe) 217 | nds_putcolors(&ppu, &u.dev[0x8]); 218 | } 219 | 220 | #pragma mark - Generics 221 | 222 | static u8 ctrl_flags = 0; 223 | 224 | void 225 | doctrl(Uxn *u) 226 | { 227 | bool changed = false; 228 | u8 old_flags = ctrl_flags; 229 | #ifdef ENABLE_KEYBOARD 230 | int key = dispswap ? -1 : keyboard_update(); 231 | #else 232 | int key = -1; 233 | #endif 234 | 235 | int pressed = keysDown(); 236 | // pressed or held, clear if repeated 237 | int held = (pressed | keysHeld()) & ~(keysDownRepeat() & ~pressed); 238 | 239 | #ifdef ENABLE_TOUCH 240 | if (pressed & (KEY_L | KEY_R)) { 241 | lcdSwap(); 242 | if (!dispswap) videoBgDisableSub(3); else videoBgEnableSub(3); 243 | dispswap ^= 1; 244 | } 245 | #endif 246 | 247 | ctrl_flags = (held & 0x0F) 248 | #ifdef ENABLE_KEYBOARD 249 | | (keyboard_is_held(K_CTRL) ? 0x01 : 0) 250 | | (keyboard_is_held(K_ALT) ? 0x02 : 0) 251 | | (keyboard_is_held(K_SHIFT) ? 0x04 : 0) 252 | | ((key == K_HOME) ? 0x08 : 0) 253 | #endif 254 | | ((held & 0xC0) >> 2) 255 | | ((held & KEY_RIGHT) ? 0x80 : 0) 256 | | ((held & KEY_LEFT) ? 0x40 : 0); 257 | 258 | if (key > 0 && key < 128) { 259 | u->dev[0x83] = key; 260 | changed = true; 261 | } 262 | if (old_flags != ctrl_flags) { 263 | changed = true; 264 | } 265 | if (changed) { 266 | // clear only changed bits 267 | u->dev[0x82] = (u->dev[0x82] & ~(old_flags & (~ctrl_flags))) | (ctrl_flags & (~old_flags)); 268 | uxn_eval(u, GETVEC(u->dev + 0x80)); 269 | if (key > 0 && key < 128) { 270 | u->dev[0x83] = 0; 271 | } 272 | } 273 | 274 | if (key == K_SYSTEM) { 275 | prompt_reset(u); 276 | } 277 | } 278 | 279 | static bool istouching = false; 280 | 281 | void 282 | domouse(Uxn *u) 283 | { 284 | bool changed = false; 285 | 286 | if (dispswap && (keysHeld() & KEY_TOUCH)) { 287 | if (!istouching) { 288 | u->dev[0x96] = 0x01; 289 | istouching = true; 290 | changed = true; 291 | } 292 | 293 | touchPosition tpos; 294 | touchRead(&tpos); 295 | if (peek16(u->dev + 0x90, 0x2) != tpos.px 296 | || peek16(u->dev + 0x90, 0x4) != tpos.py) 297 | { 298 | poke16(u->dev + 0x90, 0x2, tpos.px); 299 | poke16(u->dev + 0x90, 0x4, tpos.py); 300 | changed = true; 301 | } 302 | } else if (istouching) { 303 | u->dev[0x96] = 0x00; 304 | istouching = false; 305 | changed = true; 306 | } 307 | 308 | if (changed) { 309 | uxn_eval(u, GETVEC(u->dev + 0x90)); 310 | } 311 | } 312 | 313 | #define timer_ticks(tid) (TIMER_DATA((tid)) | (TIMER_DATA((tid)+1) << 16)) 314 | 315 | #ifdef DEBUG_PROFILE 316 | static Uint32 tticks_peak[3]; 317 | 318 | void 319 | profiler_ticks(Uint32 tticks, int pos, const char *name) 320 | { 321 | if (tticks >= 0x80000000) 322 | tticks = ~tticks; 323 | if (tticks_peak[pos] < tticks) 324 | tticks_peak[pos] = tticks; 325 | consoleSelect(&profileConsole); 326 | iprintf("\x1b[%d;0H\x1b[0K%s: %d, peak %d\n", pos, name, tticks, tticks_peak[pos]); 327 | consoleSelect(mainConsole); 328 | } 329 | #endif 330 | 331 | static int 332 | uxn_load_boot(Uxn *u) 333 | { 334 | if(system_load(u, "nitro:/boot.rom")) { 335 | chdir("nitro:/"); 336 | return 1; 337 | } 338 | if(system_load(u, "boot.rom")) { 339 | return 1; 340 | } 341 | if(system_load(u, "./uxn/boot.rom")) { 342 | chdir("./uxn"); 343 | return 1; 344 | } 345 | if(system_load(u, "./uxn/launcher.rom")) { 346 | chdir("/uxn"); 347 | return 1; 348 | } 349 | if(system_load(u, "/uxn/boot.rom")) { 350 | chdir("/uxn"); 351 | return 1; 352 | } 353 | if(system_load(u, "/uxn/launcher.rom")) { 354 | chdir("/uxn"); 355 | return 1; 356 | } 357 | return 0; 358 | } 359 | 360 | int 361 | prompt_reset(Uxn *u) 362 | { 363 | consoleClear(); 364 | iprintf("\n\n\n\n\n Would you like to reset?\n\n [A] - Yes\n [B] - No\n"); 365 | while(1) { 366 | swiWaitForVBlank(); 367 | scanKeys(); 368 | int allHeld = keysDown() | keysHeld(); 369 | if (allHeld & KEY_A) { 370 | consoleClear(); 371 | break; 372 | } else if (allHeld & KEY_B) { 373 | consoleClear(); 374 | return 0; 375 | } 376 | } 377 | 378 | iprintf("Resetting...\n"); 379 | 380 | if(!resetuxn()) 381 | return error("Resetting", "Failed"); 382 | if(!uxn_load_boot(u)) 383 | return error("Load", "Failed"); 384 | if(!nds_initppu(&ppu)) 385 | return error("PPU", "Init failure"); 386 | #ifdef ENABLE_KEYBOARD 387 | keyboard_clear(); 388 | #endif 389 | while(1) { 390 | swiWaitForVBlank(); 391 | scanKeys(); 392 | if ((keysDown() | keysHeld()) == 0) break; 393 | } 394 | ctrl_flags = 0; 395 | uxn_eval(u, 0x0100); 396 | 397 | consoleClear(); 398 | return 0; 399 | } 400 | 401 | DTCM_BSS 402 | u32 vbl_counter = 0; 403 | 404 | static void vblankHandler(void) 405 | { 406 | vbl_counter++; 407 | } 408 | 409 | int 410 | start(Uxn *u) 411 | { 412 | #ifdef DEBUG_PROFILE 413 | u32 tticks; 414 | #endif 415 | u32 last_vbl_counter = 0; 416 | 417 | irqSet(IRQ_VBLANK, vblankHandler); 418 | irqEnable(IRQ_VBLANK); 419 | 420 | uxn_eval(u, 0x0100); 421 | while(1) { 422 | if(u->dev[0x0f]) break; // Run ended. 423 | scanKeys(); 424 | #ifdef DEBUG_PROFILE 425 | int allHeld = keysDown() | keysHeld(); 426 | // X+Y in debugger mode resets tticks_peak 427 | if ((keysDown() & TICK_RESET_KEYS) && ((allHeld & TICK_RESET_KEYS) == TICK_RESET_KEYS)) 428 | memset(tticks_peak, 0, sizeof(tticks_peak)); 429 | tticks = timer_ticks(0); 430 | #endif 431 | doctrl(u); 432 | domouse(u); 433 | #ifdef DEBUG_PROFILE 434 | profiler_ticks(timer_ticks(0) - tticks, 1, "ctrl"); 435 | tticks = timer_ticks(0); 436 | #endif 437 | uxn_eval(u, GETVEC(u->dev + 0x20)); 438 | #ifdef DEBUG_PROFILE 439 | profiler_ticks(timer_ticks(0) - tticks, 0, "main"); 440 | #endif 441 | bool req_wait_vblank; 442 | { 443 | int oldIME = enterCriticalSection(); 444 | req_wait_vblank = vbl_counter == last_vbl_counter; 445 | last_vbl_counter = vbl_counter; 446 | leaveCriticalSection(oldIME); 447 | } 448 | if (req_wait_vblank) { 449 | swiWaitForVBlank(); 450 | last_vbl_counter++; 451 | } 452 | #ifdef DEBUG_PROFILE 453 | tticks = timer_ticks(0); 454 | #endif 455 | nds_copyppu(&ppu); 456 | #ifdef DEBUG_PROFILE 457 | profiler_ticks(timer_ticks(0) - tticks, 2, "flip"); 458 | #endif 459 | } 460 | 461 | irqDisable(IRQ_VBLANK); 462 | irqClear(IRQ_VBLANK); 463 | 464 | return 1; 465 | } 466 | 467 | int 468 | main(int argc, char **argv) 469 | { 470 | defaultExceptionHandler(); 471 | 472 | powerOn(POWER_ALL_2D); 473 | videoSetModeSub(MODE_0_2D); 474 | vramSetBankC(VRAM_C_SUB_BG); 475 | 476 | keysSetRepeat(27, 4); 477 | 478 | #ifdef USE_BOTTOM_SCREEN_DEFAULT 479 | dispswap = 1; 480 | lcdSwap(); 481 | #else 482 | dispswap = 0; 483 | #endif 484 | 485 | mainConsole = consoleDemoInit(); 486 | #ifdef DEBUG 487 | #ifdef DEBUG_PROFILE 488 | // Timers 0-1 - profiling timers 489 | TIMER0_DATA = 0; 490 | TIMER1_DATA = 0; 491 | TIMER0_CR = TIMER_ENABLE | TIMER_DIV_1; 492 | TIMER1_CR = TIMER_ENABLE | TIMER_CASCADE; 493 | 494 | consoleSetWindow(mainConsole, 0, 0, 32, 11); 495 | 496 | profileConsole = *mainConsole; 497 | consoleSetWindow(&profileConsole, 0, 11, 32, 4); 498 | #else 499 | consoleSetWindow(mainConsole, 0, 0, 32, 14); 500 | #endif 501 | #endif 502 | consoleSelect(mainConsole); 503 | 504 | uxn_register_device(0x0, nds_system_dei, nds_system_deo); 505 | uxn_register_device(0x1, NULL, console_deo); 506 | uxn_register_device(0x2, screen_dei, screen_deo); 507 | uxn_register_device(0x3, audio0_dei, audio0_deo); 508 | uxn_register_device(0x4, audio1_dei, audio1_deo); 509 | uxn_register_device(0x5, audio2_dei, audio2_deo); 510 | uxn_register_device(0x6, audio3_dei, audio3_deo); 511 | uxn_register_device(0xa, NULL, file0_deo); 512 | uxn_register_device(0xb, NULL, file1_deo); 513 | uxn_register_device(0xc, datetime_dei, NULL); 514 | 515 | if(!uxn_boot()) 516 | return error("Boot", "Failed"); 517 | if(!fatInitDefault()) 518 | return error("FAT init", "Failed"); 519 | nitroFSInit(NULL); // no big deal if this one fails 520 | if(!uxn_load_boot(&u)) { 521 | dprintf("Halted: Missing input rom.\n"); 522 | return error("Load", "Failed"); 523 | } 524 | if(!init()) 525 | return error("Init", "Failed"); 526 | 527 | /* Write screen size to dev/screen */ 528 | poke16(u.dev + 0x20, 2, PPU_PIXELS_WIDTH); 529 | poke16(u.dev + 0x20, 4, PPU_PIXELS_HEIGHT); 530 | 531 | start(&u); 532 | #ifdef DEBUG 533 | scanKeys(); 534 | while(keysHeld()) scanKeys(); 535 | printf("Press any button to exit"); 536 | while(!keysHeld()) scanKeys(); 537 | #endif 538 | quit(); 539 | return 0; 540 | } 541 | -------------------------------------------------------------------------------- /arm9/source/lut_expand_8_32.inc: -------------------------------------------------------------------------------- 1 | 0x00000000, 2 | 0x00000001, 3 | 0x00000010, 4 | 0x00000011, 5 | 0x00000100, 6 | 0x00000101, 7 | 0x00000110, 8 | 0x00000111, 9 | 0x00001000, 10 | 0x00001001, 11 | 0x00001010, 12 | 0x00001011, 13 | 0x00001100, 14 | 0x00001101, 15 | 0x00001110, 16 | 0x00001111, 17 | 0x00010000, 18 | 0x00010001, 19 | 0x00010010, 20 | 0x00010011, 21 | 0x00010100, 22 | 0x00010101, 23 | 0x00010110, 24 | 0x00010111, 25 | 0x00011000, 26 | 0x00011001, 27 | 0x00011010, 28 | 0x00011011, 29 | 0x00011100, 30 | 0x00011101, 31 | 0x00011110, 32 | 0x00011111, 33 | 0x00100000, 34 | 0x00100001, 35 | 0x00100010, 36 | 0x00100011, 37 | 0x00100100, 38 | 0x00100101, 39 | 0x00100110, 40 | 0x00100111, 41 | 0x00101000, 42 | 0x00101001, 43 | 0x00101010, 44 | 0x00101011, 45 | 0x00101100, 46 | 0x00101101, 47 | 0x00101110, 48 | 0x00101111, 49 | 0x00110000, 50 | 0x00110001, 51 | 0x00110010, 52 | 0x00110011, 53 | 0x00110100, 54 | 0x00110101, 55 | 0x00110110, 56 | 0x00110111, 57 | 0x00111000, 58 | 0x00111001, 59 | 0x00111010, 60 | 0x00111011, 61 | 0x00111100, 62 | 0x00111101, 63 | 0x00111110, 64 | 0x00111111, 65 | 0x01000000, 66 | 0x01000001, 67 | 0x01000010, 68 | 0x01000011, 69 | 0x01000100, 70 | 0x01000101, 71 | 0x01000110, 72 | 0x01000111, 73 | 0x01001000, 74 | 0x01001001, 75 | 0x01001010, 76 | 0x01001011, 77 | 0x01001100, 78 | 0x01001101, 79 | 0x01001110, 80 | 0x01001111, 81 | 0x01010000, 82 | 0x01010001, 83 | 0x01010010, 84 | 0x01010011, 85 | 0x01010100, 86 | 0x01010101, 87 | 0x01010110, 88 | 0x01010111, 89 | 0x01011000, 90 | 0x01011001, 91 | 0x01011010, 92 | 0x01011011, 93 | 0x01011100, 94 | 0x01011101, 95 | 0x01011110, 96 | 0x01011111, 97 | 0x01100000, 98 | 0x01100001, 99 | 0x01100010, 100 | 0x01100011, 101 | 0x01100100, 102 | 0x01100101, 103 | 0x01100110, 104 | 0x01100111, 105 | 0x01101000, 106 | 0x01101001, 107 | 0x01101010, 108 | 0x01101011, 109 | 0x01101100, 110 | 0x01101101, 111 | 0x01101110, 112 | 0x01101111, 113 | 0x01110000, 114 | 0x01110001, 115 | 0x01110010, 116 | 0x01110011, 117 | 0x01110100, 118 | 0x01110101, 119 | 0x01110110, 120 | 0x01110111, 121 | 0x01111000, 122 | 0x01111001, 123 | 0x01111010, 124 | 0x01111011, 125 | 0x01111100, 126 | 0x01111101, 127 | 0x01111110, 128 | 0x01111111, 129 | 0x10000000, 130 | 0x10000001, 131 | 0x10000010, 132 | 0x10000011, 133 | 0x10000100, 134 | 0x10000101, 135 | 0x10000110, 136 | 0x10000111, 137 | 0x10001000, 138 | 0x10001001, 139 | 0x10001010, 140 | 0x10001011, 141 | 0x10001100, 142 | 0x10001101, 143 | 0x10001110, 144 | 0x10001111, 145 | 0x10010000, 146 | 0x10010001, 147 | 0x10010010, 148 | 0x10010011, 149 | 0x10010100, 150 | 0x10010101, 151 | 0x10010110, 152 | 0x10010111, 153 | 0x10011000, 154 | 0x10011001, 155 | 0x10011010, 156 | 0x10011011, 157 | 0x10011100, 158 | 0x10011101, 159 | 0x10011110, 160 | 0x10011111, 161 | 0x10100000, 162 | 0x10100001, 163 | 0x10100010, 164 | 0x10100011, 165 | 0x10100100, 166 | 0x10100101, 167 | 0x10100110, 168 | 0x10100111, 169 | 0x10101000, 170 | 0x10101001, 171 | 0x10101010, 172 | 0x10101011, 173 | 0x10101100, 174 | 0x10101101, 175 | 0x10101110, 176 | 0x10101111, 177 | 0x10110000, 178 | 0x10110001, 179 | 0x10110010, 180 | 0x10110011, 181 | 0x10110100, 182 | 0x10110101, 183 | 0x10110110, 184 | 0x10110111, 185 | 0x10111000, 186 | 0x10111001, 187 | 0x10111010, 188 | 0x10111011, 189 | 0x10111100, 190 | 0x10111101, 191 | 0x10111110, 192 | 0x10111111, 193 | 0x11000000, 194 | 0x11000001, 195 | 0x11000010, 196 | 0x11000011, 197 | 0x11000100, 198 | 0x11000101, 199 | 0x11000110, 200 | 0x11000111, 201 | 0x11001000, 202 | 0x11001001, 203 | 0x11001010, 204 | 0x11001011, 205 | 0x11001100, 206 | 0x11001101, 207 | 0x11001110, 208 | 0x11001111, 209 | 0x11010000, 210 | 0x11010001, 211 | 0x11010010, 212 | 0x11010011, 213 | 0x11010100, 214 | 0x11010101, 215 | 0x11010110, 216 | 0x11010111, 217 | 0x11011000, 218 | 0x11011001, 219 | 0x11011010, 220 | 0x11011011, 221 | 0x11011100, 222 | 0x11011101, 223 | 0x11011110, 224 | 0x11011111, 225 | 0x11100000, 226 | 0x11100001, 227 | 0x11100010, 228 | 0x11100011, 229 | 0x11100100, 230 | 0x11100101, 231 | 0x11100110, 232 | 0x11100111, 233 | 0x11101000, 234 | 0x11101001, 235 | 0x11101010, 236 | 0x11101011, 237 | 0x11101100, 238 | 0x11101101, 239 | 0x11101110, 240 | 0x11101111, 241 | 0x11110000, 242 | 0x11110001, 243 | 0x11110010, 244 | 0x11110011, 245 | 0x11110100, 246 | 0x11110101, 247 | 0x11110110, 248 | 0x11110111, 249 | 0x11111000, 250 | 0x11111001, 251 | 0x11111010, 252 | 0x11111011, 253 | 0x11111100, 254 | 0x11111101, 255 | 0x11111110, 256 | 0x11111111 257 | -------------------------------------------------------------------------------- /arm9/source/lut_expand_8_32_f.inc: -------------------------------------------------------------------------------- 1 | 0x00000000, 2 | 0x0000000f, 3 | 0x000000f0, 4 | 0x000000ff, 5 | 0x00000f00, 6 | 0x00000f0f, 7 | 0x00000ff0, 8 | 0x00000fff, 9 | 0x0000f000, 10 | 0x0000f00f, 11 | 0x0000f0f0, 12 | 0x0000f0ff, 13 | 0x0000ff00, 14 | 0x0000ff0f, 15 | 0x0000fff0, 16 | 0x0000ffff, 17 | 0x000f0000, 18 | 0x000f000f, 19 | 0x000f00f0, 20 | 0x000f00ff, 21 | 0x000f0f00, 22 | 0x000f0f0f, 23 | 0x000f0ff0, 24 | 0x000f0fff, 25 | 0x000ff000, 26 | 0x000ff00f, 27 | 0x000ff0f0, 28 | 0x000ff0ff, 29 | 0x000fff00, 30 | 0x000fff0f, 31 | 0x000ffff0, 32 | 0x000fffff, 33 | 0x00f00000, 34 | 0x00f0000f, 35 | 0x00f000f0, 36 | 0x00f000ff, 37 | 0x00f00f00, 38 | 0x00f00f0f, 39 | 0x00f00ff0, 40 | 0x00f00fff, 41 | 0x00f0f000, 42 | 0x00f0f00f, 43 | 0x00f0f0f0, 44 | 0x00f0f0ff, 45 | 0x00f0ff00, 46 | 0x00f0ff0f, 47 | 0x00f0fff0, 48 | 0x00f0ffff, 49 | 0x00ff0000, 50 | 0x00ff000f, 51 | 0x00ff00f0, 52 | 0x00ff00ff, 53 | 0x00ff0f00, 54 | 0x00ff0f0f, 55 | 0x00ff0ff0, 56 | 0x00ff0fff, 57 | 0x00fff000, 58 | 0x00fff00f, 59 | 0x00fff0f0, 60 | 0x00fff0ff, 61 | 0x00ffff00, 62 | 0x00ffff0f, 63 | 0x00fffff0, 64 | 0x00ffffff, 65 | 0x0f000000, 66 | 0x0f00000f, 67 | 0x0f0000f0, 68 | 0x0f0000ff, 69 | 0x0f000f00, 70 | 0x0f000f0f, 71 | 0x0f000ff0, 72 | 0x0f000fff, 73 | 0x0f00f000, 74 | 0x0f00f00f, 75 | 0x0f00f0f0, 76 | 0x0f00f0ff, 77 | 0x0f00ff00, 78 | 0x0f00ff0f, 79 | 0x0f00fff0, 80 | 0x0f00ffff, 81 | 0x0f0f0000, 82 | 0x0f0f000f, 83 | 0x0f0f00f0, 84 | 0x0f0f00ff, 85 | 0x0f0f0f00, 86 | 0x0f0f0f0f, 87 | 0x0f0f0ff0, 88 | 0x0f0f0fff, 89 | 0x0f0ff000, 90 | 0x0f0ff00f, 91 | 0x0f0ff0f0, 92 | 0x0f0ff0ff, 93 | 0x0f0fff00, 94 | 0x0f0fff0f, 95 | 0x0f0ffff0, 96 | 0x0f0fffff, 97 | 0x0ff00000, 98 | 0x0ff0000f, 99 | 0x0ff000f0, 100 | 0x0ff000ff, 101 | 0x0ff00f00, 102 | 0x0ff00f0f, 103 | 0x0ff00ff0, 104 | 0x0ff00fff, 105 | 0x0ff0f000, 106 | 0x0ff0f00f, 107 | 0x0ff0f0f0, 108 | 0x0ff0f0ff, 109 | 0x0ff0ff00, 110 | 0x0ff0ff0f, 111 | 0x0ff0fff0, 112 | 0x0ff0ffff, 113 | 0x0fff0000, 114 | 0x0fff000f, 115 | 0x0fff00f0, 116 | 0x0fff00ff, 117 | 0x0fff0f00, 118 | 0x0fff0f0f, 119 | 0x0fff0ff0, 120 | 0x0fff0fff, 121 | 0x0ffff000, 122 | 0x0ffff00f, 123 | 0x0ffff0f0, 124 | 0x0ffff0ff, 125 | 0x0fffff00, 126 | 0x0fffff0f, 127 | 0x0ffffff0, 128 | 0x0fffffff, 129 | 0xf0000000, 130 | 0xf000000f, 131 | 0xf00000f0, 132 | 0xf00000ff, 133 | 0xf0000f00, 134 | 0xf0000f0f, 135 | 0xf0000ff0, 136 | 0xf0000fff, 137 | 0xf000f000, 138 | 0xf000f00f, 139 | 0xf000f0f0, 140 | 0xf000f0ff, 141 | 0xf000ff00, 142 | 0xf000ff0f, 143 | 0xf000fff0, 144 | 0xf000ffff, 145 | 0xf00f0000, 146 | 0xf00f000f, 147 | 0xf00f00f0, 148 | 0xf00f00ff, 149 | 0xf00f0f00, 150 | 0xf00f0f0f, 151 | 0xf00f0ff0, 152 | 0xf00f0fff, 153 | 0xf00ff000, 154 | 0xf00ff00f, 155 | 0xf00ff0f0, 156 | 0xf00ff0ff, 157 | 0xf00fff00, 158 | 0xf00fff0f, 159 | 0xf00ffff0, 160 | 0xf00fffff, 161 | 0xf0f00000, 162 | 0xf0f0000f, 163 | 0xf0f000f0, 164 | 0xf0f000ff, 165 | 0xf0f00f00, 166 | 0xf0f00f0f, 167 | 0xf0f00ff0, 168 | 0xf0f00fff, 169 | 0xf0f0f000, 170 | 0xf0f0f00f, 171 | 0xf0f0f0f0, 172 | 0xf0f0f0ff, 173 | 0xf0f0ff00, 174 | 0xf0f0ff0f, 175 | 0xf0f0fff0, 176 | 0xf0f0ffff, 177 | 0xf0ff0000, 178 | 0xf0ff000f, 179 | 0xf0ff00f0, 180 | 0xf0ff00ff, 181 | 0xf0ff0f00, 182 | 0xf0ff0f0f, 183 | 0xf0ff0ff0, 184 | 0xf0ff0fff, 185 | 0xf0fff000, 186 | 0xf0fff00f, 187 | 0xf0fff0f0, 188 | 0xf0fff0ff, 189 | 0xf0ffff00, 190 | 0xf0ffff0f, 191 | 0xf0fffff0, 192 | 0xf0ffffff, 193 | 0xff000000, 194 | 0xff00000f, 195 | 0xff0000f0, 196 | 0xff0000ff, 197 | 0xff000f00, 198 | 0xff000f0f, 199 | 0xff000ff0, 200 | 0xff000fff, 201 | 0xff00f000, 202 | 0xff00f00f, 203 | 0xff00f0f0, 204 | 0xff00f0ff, 205 | 0xff00ff00, 206 | 0xff00ff0f, 207 | 0xff00fff0, 208 | 0xff00ffff, 209 | 0xff0f0000, 210 | 0xff0f000f, 211 | 0xff0f00f0, 212 | 0xff0f00ff, 213 | 0xff0f0f00, 214 | 0xff0f0f0f, 215 | 0xff0f0ff0, 216 | 0xff0f0fff, 217 | 0xff0ff000, 218 | 0xff0ff00f, 219 | 0xff0ff0f0, 220 | 0xff0ff0ff, 221 | 0xff0fff00, 222 | 0xff0fff0f, 223 | 0xff0ffff0, 224 | 0xff0fffff, 225 | 0xfff00000, 226 | 0xfff0000f, 227 | 0xfff000f0, 228 | 0xfff000ff, 229 | 0xfff00f00, 230 | 0xfff00f0f, 231 | 0xfff00ff0, 232 | 0xfff00fff, 233 | 0xfff0f000, 234 | 0xfff0f00f, 235 | 0xfff0f0f0, 236 | 0xfff0f0ff, 237 | 0xfff0ff00, 238 | 0xfff0ff0f, 239 | 0xfff0fff0, 240 | 0xfff0ffff, 241 | 0xffff0000, 242 | 0xffff000f, 243 | 0xffff00f0, 244 | 0xffff00ff, 245 | 0xffff0f00, 246 | 0xffff0f0f, 247 | 0xffff0ff0, 248 | 0xffff0fff, 249 | 0xfffff000, 250 | 0xfffff00f, 251 | 0xfffff0f0, 252 | 0xfffff0ff, 253 | 0xffffff00, 254 | 0xffffff0f, 255 | 0xfffffff0, 256 | 0xffffffff 257 | -------------------------------------------------------------------------------- /arm9/source/lut_expand_8_32_f_flipx.inc: -------------------------------------------------------------------------------- 1 | 0x00000000, 2 | 0xf0000000, 3 | 0x0f000000, 4 | 0xff000000, 5 | 0x00f00000, 6 | 0xf0f00000, 7 | 0x0ff00000, 8 | 0xfff00000, 9 | 0x000f0000, 10 | 0xf00f0000, 11 | 0x0f0f0000, 12 | 0xff0f0000, 13 | 0x00ff0000, 14 | 0xf0ff0000, 15 | 0x0fff0000, 16 | 0xffff0000, 17 | 0x0000f000, 18 | 0xf000f000, 19 | 0x0f00f000, 20 | 0xff00f000, 21 | 0x00f0f000, 22 | 0xf0f0f000, 23 | 0x0ff0f000, 24 | 0xfff0f000, 25 | 0x000ff000, 26 | 0xf00ff000, 27 | 0x0f0ff000, 28 | 0xff0ff000, 29 | 0x00fff000, 30 | 0xf0fff000, 31 | 0x0ffff000, 32 | 0xfffff000, 33 | 0x00000f00, 34 | 0xf0000f00, 35 | 0x0f000f00, 36 | 0xff000f00, 37 | 0x00f00f00, 38 | 0xf0f00f00, 39 | 0x0ff00f00, 40 | 0xfff00f00, 41 | 0x000f0f00, 42 | 0xf00f0f00, 43 | 0x0f0f0f00, 44 | 0xff0f0f00, 45 | 0x00ff0f00, 46 | 0xf0ff0f00, 47 | 0x0fff0f00, 48 | 0xffff0f00, 49 | 0x0000ff00, 50 | 0xf000ff00, 51 | 0x0f00ff00, 52 | 0xff00ff00, 53 | 0x00f0ff00, 54 | 0xf0f0ff00, 55 | 0x0ff0ff00, 56 | 0xfff0ff00, 57 | 0x000fff00, 58 | 0xf00fff00, 59 | 0x0f0fff00, 60 | 0xff0fff00, 61 | 0x00ffff00, 62 | 0xf0ffff00, 63 | 0x0fffff00, 64 | 0xffffff00, 65 | 0x000000f0, 66 | 0xf00000f0, 67 | 0x0f0000f0, 68 | 0xff0000f0, 69 | 0x00f000f0, 70 | 0xf0f000f0, 71 | 0x0ff000f0, 72 | 0xfff000f0, 73 | 0x000f00f0, 74 | 0xf00f00f0, 75 | 0x0f0f00f0, 76 | 0xff0f00f0, 77 | 0x00ff00f0, 78 | 0xf0ff00f0, 79 | 0x0fff00f0, 80 | 0xffff00f0, 81 | 0x0000f0f0, 82 | 0xf000f0f0, 83 | 0x0f00f0f0, 84 | 0xff00f0f0, 85 | 0x00f0f0f0, 86 | 0xf0f0f0f0, 87 | 0x0ff0f0f0, 88 | 0xfff0f0f0, 89 | 0x000ff0f0, 90 | 0xf00ff0f0, 91 | 0x0f0ff0f0, 92 | 0xff0ff0f0, 93 | 0x00fff0f0, 94 | 0xf0fff0f0, 95 | 0x0ffff0f0, 96 | 0xfffff0f0, 97 | 0x00000ff0, 98 | 0xf0000ff0, 99 | 0x0f000ff0, 100 | 0xff000ff0, 101 | 0x00f00ff0, 102 | 0xf0f00ff0, 103 | 0x0ff00ff0, 104 | 0xfff00ff0, 105 | 0x000f0ff0, 106 | 0xf00f0ff0, 107 | 0x0f0f0ff0, 108 | 0xff0f0ff0, 109 | 0x00ff0ff0, 110 | 0xf0ff0ff0, 111 | 0x0fff0ff0, 112 | 0xffff0ff0, 113 | 0x0000fff0, 114 | 0xf000fff0, 115 | 0x0f00fff0, 116 | 0xff00fff0, 117 | 0x00f0fff0, 118 | 0xf0f0fff0, 119 | 0x0ff0fff0, 120 | 0xfff0fff0, 121 | 0x000ffff0, 122 | 0xf00ffff0, 123 | 0x0f0ffff0, 124 | 0xff0ffff0, 125 | 0x00fffff0, 126 | 0xf0fffff0, 127 | 0x0ffffff0, 128 | 0xfffffff0, 129 | 0x0000000f, 130 | 0xf000000f, 131 | 0x0f00000f, 132 | 0xff00000f, 133 | 0x00f0000f, 134 | 0xf0f0000f, 135 | 0x0ff0000f, 136 | 0xfff0000f, 137 | 0x000f000f, 138 | 0xf00f000f, 139 | 0x0f0f000f, 140 | 0xff0f000f, 141 | 0x00ff000f, 142 | 0xf0ff000f, 143 | 0x0fff000f, 144 | 0xffff000f, 145 | 0x0000f00f, 146 | 0xf000f00f, 147 | 0x0f00f00f, 148 | 0xff00f00f, 149 | 0x00f0f00f, 150 | 0xf0f0f00f, 151 | 0x0ff0f00f, 152 | 0xfff0f00f, 153 | 0x000ff00f, 154 | 0xf00ff00f, 155 | 0x0f0ff00f, 156 | 0xff0ff00f, 157 | 0x00fff00f, 158 | 0xf0fff00f, 159 | 0x0ffff00f, 160 | 0xfffff00f, 161 | 0x00000f0f, 162 | 0xf0000f0f, 163 | 0x0f000f0f, 164 | 0xff000f0f, 165 | 0x00f00f0f, 166 | 0xf0f00f0f, 167 | 0x0ff00f0f, 168 | 0xfff00f0f, 169 | 0x000f0f0f, 170 | 0xf00f0f0f, 171 | 0x0f0f0f0f, 172 | 0xff0f0f0f, 173 | 0x00ff0f0f, 174 | 0xf0ff0f0f, 175 | 0x0fff0f0f, 176 | 0xffff0f0f, 177 | 0x0000ff0f, 178 | 0xf000ff0f, 179 | 0x0f00ff0f, 180 | 0xff00ff0f, 181 | 0x00f0ff0f, 182 | 0xf0f0ff0f, 183 | 0x0ff0ff0f, 184 | 0xfff0ff0f, 185 | 0x000fff0f, 186 | 0xf00fff0f, 187 | 0x0f0fff0f, 188 | 0xff0fff0f, 189 | 0x00ffff0f, 190 | 0xf0ffff0f, 191 | 0x0fffff0f, 192 | 0xffffff0f, 193 | 0x000000ff, 194 | 0xf00000ff, 195 | 0x0f0000ff, 196 | 0xff0000ff, 197 | 0x00f000ff, 198 | 0xf0f000ff, 199 | 0x0ff000ff, 200 | 0xfff000ff, 201 | 0x000f00ff, 202 | 0xf00f00ff, 203 | 0x0f0f00ff, 204 | 0xff0f00ff, 205 | 0x00ff00ff, 206 | 0xf0ff00ff, 207 | 0x0fff00ff, 208 | 0xffff00ff, 209 | 0x0000f0ff, 210 | 0xf000f0ff, 211 | 0x0f00f0ff, 212 | 0xff00f0ff, 213 | 0x00f0f0ff, 214 | 0xf0f0f0ff, 215 | 0x0ff0f0ff, 216 | 0xfff0f0ff, 217 | 0x000ff0ff, 218 | 0xf00ff0ff, 219 | 0x0f0ff0ff, 220 | 0xff0ff0ff, 221 | 0x00fff0ff, 222 | 0xf0fff0ff, 223 | 0x0ffff0ff, 224 | 0xfffff0ff, 225 | 0x00000fff, 226 | 0xf0000fff, 227 | 0x0f000fff, 228 | 0xff000fff, 229 | 0x00f00fff, 230 | 0xf0f00fff, 231 | 0x0ff00fff, 232 | 0xfff00fff, 233 | 0x000f0fff, 234 | 0xf00f0fff, 235 | 0x0f0f0fff, 236 | 0xff0f0fff, 237 | 0x00ff0fff, 238 | 0xf0ff0fff, 239 | 0x0fff0fff, 240 | 0xffff0fff, 241 | 0x0000ffff, 242 | 0xf000ffff, 243 | 0x0f00ffff, 244 | 0xff00ffff, 245 | 0x00f0ffff, 246 | 0xf0f0ffff, 247 | 0x0ff0ffff, 248 | 0xfff0ffff, 249 | 0x000fffff, 250 | 0xf00fffff, 251 | 0x0f0fffff, 252 | 0xff0fffff, 253 | 0x00ffffff, 254 | 0xf0ffffff, 255 | 0x0fffffff, 256 | 0xffffffff 257 | -------------------------------------------------------------------------------- /arm9/source/lut_expand_8_32_flipx.inc: -------------------------------------------------------------------------------- 1 | 0x00000000, 2 | 0x10000000, 3 | 0x01000000, 4 | 0x11000000, 5 | 0x00100000, 6 | 0x10100000, 7 | 0x01100000, 8 | 0x11100000, 9 | 0x00010000, 10 | 0x10010000, 11 | 0x01010000, 12 | 0x11010000, 13 | 0x00110000, 14 | 0x10110000, 15 | 0x01110000, 16 | 0x11110000, 17 | 0x00001000, 18 | 0x10001000, 19 | 0x01001000, 20 | 0x11001000, 21 | 0x00101000, 22 | 0x10101000, 23 | 0x01101000, 24 | 0x11101000, 25 | 0x00011000, 26 | 0x10011000, 27 | 0x01011000, 28 | 0x11011000, 29 | 0x00111000, 30 | 0x10111000, 31 | 0x01111000, 32 | 0x11111000, 33 | 0x00000100, 34 | 0x10000100, 35 | 0x01000100, 36 | 0x11000100, 37 | 0x00100100, 38 | 0x10100100, 39 | 0x01100100, 40 | 0x11100100, 41 | 0x00010100, 42 | 0x10010100, 43 | 0x01010100, 44 | 0x11010100, 45 | 0x00110100, 46 | 0x10110100, 47 | 0x01110100, 48 | 0x11110100, 49 | 0x00001100, 50 | 0x10001100, 51 | 0x01001100, 52 | 0x11001100, 53 | 0x00101100, 54 | 0x10101100, 55 | 0x01101100, 56 | 0x11101100, 57 | 0x00011100, 58 | 0x10011100, 59 | 0x01011100, 60 | 0x11011100, 61 | 0x00111100, 62 | 0x10111100, 63 | 0x01111100, 64 | 0x11111100, 65 | 0x00000010, 66 | 0x10000010, 67 | 0x01000010, 68 | 0x11000010, 69 | 0x00100010, 70 | 0x10100010, 71 | 0x01100010, 72 | 0x11100010, 73 | 0x00010010, 74 | 0x10010010, 75 | 0x01010010, 76 | 0x11010010, 77 | 0x00110010, 78 | 0x10110010, 79 | 0x01110010, 80 | 0x11110010, 81 | 0x00001010, 82 | 0x10001010, 83 | 0x01001010, 84 | 0x11001010, 85 | 0x00101010, 86 | 0x10101010, 87 | 0x01101010, 88 | 0x11101010, 89 | 0x00011010, 90 | 0x10011010, 91 | 0x01011010, 92 | 0x11011010, 93 | 0x00111010, 94 | 0x10111010, 95 | 0x01111010, 96 | 0x11111010, 97 | 0x00000110, 98 | 0x10000110, 99 | 0x01000110, 100 | 0x11000110, 101 | 0x00100110, 102 | 0x10100110, 103 | 0x01100110, 104 | 0x11100110, 105 | 0x00010110, 106 | 0x10010110, 107 | 0x01010110, 108 | 0x11010110, 109 | 0x00110110, 110 | 0x10110110, 111 | 0x01110110, 112 | 0x11110110, 113 | 0x00001110, 114 | 0x10001110, 115 | 0x01001110, 116 | 0x11001110, 117 | 0x00101110, 118 | 0x10101110, 119 | 0x01101110, 120 | 0x11101110, 121 | 0x00011110, 122 | 0x10011110, 123 | 0x01011110, 124 | 0x11011110, 125 | 0x00111110, 126 | 0x10111110, 127 | 0x01111110, 128 | 0x11111110, 129 | 0x00000001, 130 | 0x10000001, 131 | 0x01000001, 132 | 0x11000001, 133 | 0x00100001, 134 | 0x10100001, 135 | 0x01100001, 136 | 0x11100001, 137 | 0x00010001, 138 | 0x10010001, 139 | 0x01010001, 140 | 0x11010001, 141 | 0x00110001, 142 | 0x10110001, 143 | 0x01110001, 144 | 0x11110001, 145 | 0x00001001, 146 | 0x10001001, 147 | 0x01001001, 148 | 0x11001001, 149 | 0x00101001, 150 | 0x10101001, 151 | 0x01101001, 152 | 0x11101001, 153 | 0x00011001, 154 | 0x10011001, 155 | 0x01011001, 156 | 0x11011001, 157 | 0x00111001, 158 | 0x10111001, 159 | 0x01111001, 160 | 0x11111001, 161 | 0x00000101, 162 | 0x10000101, 163 | 0x01000101, 164 | 0x11000101, 165 | 0x00100101, 166 | 0x10100101, 167 | 0x01100101, 168 | 0x11100101, 169 | 0x00010101, 170 | 0x10010101, 171 | 0x01010101, 172 | 0x11010101, 173 | 0x00110101, 174 | 0x10110101, 175 | 0x01110101, 176 | 0x11110101, 177 | 0x00001101, 178 | 0x10001101, 179 | 0x01001101, 180 | 0x11001101, 181 | 0x00101101, 182 | 0x10101101, 183 | 0x01101101, 184 | 0x11101101, 185 | 0x00011101, 186 | 0x10011101, 187 | 0x01011101, 188 | 0x11011101, 189 | 0x00111101, 190 | 0x10111101, 191 | 0x01111101, 192 | 0x11111101, 193 | 0x00000011, 194 | 0x10000011, 195 | 0x01000011, 196 | 0x11000011, 197 | 0x00100011, 198 | 0x10100011, 199 | 0x01100011, 200 | 0x11100011, 201 | 0x00010011, 202 | 0x10010011, 203 | 0x01010011, 204 | 0x11010011, 205 | 0x00110011, 206 | 0x10110011, 207 | 0x01110011, 208 | 0x11110011, 209 | 0x00001011, 210 | 0x10001011, 211 | 0x01001011, 212 | 0x11001011, 213 | 0x00101011, 214 | 0x10101011, 215 | 0x01101011, 216 | 0x11101011, 217 | 0x00011011, 218 | 0x10011011, 219 | 0x01011011, 220 | 0x11011011, 221 | 0x00111011, 222 | 0x10111011, 223 | 0x01111011, 224 | 0x11111011, 225 | 0x00000111, 226 | 0x10000111, 227 | 0x01000111, 228 | 0x11000111, 229 | 0x00100111, 230 | 0x10100111, 231 | 0x01100111, 232 | 0x11100111, 233 | 0x00010111, 234 | 0x10010111, 235 | 0x01010111, 236 | 0x11010111, 237 | 0x00110111, 238 | 0x10110111, 239 | 0x01110111, 240 | 0x11110111, 241 | 0x00001111, 242 | 0x10001111, 243 | 0x01001111, 244 | 0x11001111, 245 | 0x00101111, 246 | 0x10101111, 247 | 0x01101111, 248 | 0x11101111, 249 | 0x00011111, 250 | 0x10011111, 251 | 0x01011111, 252 | 0x11011111, 253 | 0x00111111, 254 | 0x10111111, 255 | 0x01111111, 256 | 0x11111111 257 | -------------------------------------------------------------------------------- /arm9/source/nds_apu.c: -------------------------------------------------------------------------------- 1 | #include "uxn.h" 2 | #include "nds/apu.h" 3 | 4 | /* 5 | Copyright (c) 2021 Devine Lu Linvega 6 | Copyright (c) 2021 Andrew Alderwick 7 | Copyright (c) 2021 Adrian "asie" Siekierka 8 | 9 | Permission to use, copy, modify, and distribute this software for any 10 | purpose with or without fee is hereby granted, provided that the above 11 | copyright notice and this permission notice appear in all copies. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 | WITH REGARD TO THIS SOFTWARE. 15 | */ 16 | 17 | #define NOTE_PERIOD (SAMPLE_FREQUENCY * 0x4000 / 11025) 18 | #define ADSR_STEP (SAMPLE_FREQUENCY / 0xf) 19 | 20 | /* clang-format off */ 21 | 22 | static Uint32 advances[12] = { 23 | 0x80000, 0x879c8, 0x8facd, 0x9837f, 0xa1451, 0xaadc1, 24 | 0xb504f, 0xbfc88, 0xcb2ff, 0xd7450, 0xe411f, 0xf1a1c 25 | }; 26 | 27 | static float detunes[256] = { 28 | 1.0000000000000000f, 1.0002256593050698f, 1.0004513695322617f, 29 | 1.0006771306930664f, 1.0009029427989777f, 1.0011288058614922f, 30 | 1.0013547198921082f, 1.0015806849023274f, 1.0018067009036538f, 31 | 1.002032767907594f, 1.0022588859256572f, 1.0024850549693551f, 32 | 1.0027112750502025f, 1.0029375461797159f, 1.0031638683694153f, 33 | 1.0033902416308227f, 1.0036166659754628f, 1.0038431414148634f, 34 | 1.004069667960554f, 1.0042962456240678f, 1.0045228744169397f, 35 | 1.0047495543507072f, 1.004976285436911f, 1.0052030676870944f, 36 | 1.0054299011128027f, 1.0056567857255843f, 1.0058837215369900f, 37 | 1.006110708558573f, 1.0063377468018897f, 1.0065648362784985f, 38 | 1.0067919769999607f, 1.0070191689778405f, 1.007246412223704f, 39 | 1.0074737067491204f, 1.0077010525656616f, 1.0079284496849015f, 40 | 1.0081558981184175f, 1.008383397877789f, 1.008610948974598f, 41 | 1.0088385514204294f, 1.0090662052268706f, 1.0092939104055114f, 42 | 1.0095216669679448f, 1.0097494749257656f, 1.009977334290572f, 43 | 1.0102052450739643f, 1.0104332072875455f, 1.0106612209429215f, 44 | 1.0108892860517005f, 1.0111174026254934f, 1.0113455706759138f, 45 | 1.011573790214578f, 1.0118020612531047f, 1.0120303838031153f, 46 | 1.0122587578762337f, 1.012487183484087f, 1.012715660638304f, 47 | 1.0129441893505169f, 1.0131727696323602f, 1.0134014014954713f, 48 | 1.0136300849514894f, 1.0138588200120575f, 1.0140876066888203f, 49 | 1.0143164449934257f, 1.0145453349375237f, 1.0147742765327674f, 50 | 1.0150032697908125f, 1.015232314723317f, 1.015461411341942f, 51 | 1.0156905596583505f, 1.0159197596842091f, 1.0161490114311862f, 52 | 1.016378314910953f, 1.0166076701351838f, 1.0168370771155553f, 53 | 1.0170665358637463f, 1.0172960463914391f, 1.017525608710318f, 54 | 1.0177552228320703f, 1.0179848887683858f, 1.0182146065309567f, 55 | 1.0184443761314785f, 1.0186741975816487f, 1.0189040708931674f, 56 | 1.019133996077738f, 1.0193639731470658f, 1.0195940021128593f, 57 | 1.0198240829868295f, 1.0200542157806898f, 1.0202844005061564f, 58 | 1.0205146371749483f, 1.0207449257987866f, 1.0209752663893958f, 59 | 1.0212056589585028f, 1.0214361035178368f, 1.0216666000791297f, 60 | 1.0218971486541166f, 1.0221277492545349f, 1.0223584018921241f, 61 | 1.0225891065786274f, 1.02281986332579f, 1.0230506721453596f, 62 | 1.023281533049087f, 1.0235124460487257f, 1.0237434111560313f, 63 | 1.0239744283827625f, 1.0242054977406807f, 1.0244366192415495f, 64 | 1.0246677928971357f, 1.0248990187192082f, 1.025130296719539f, 65 | 1.0253616269099028f, 1.0255930093020766f, 1.0258244439078401f, 66 | 1.026055930738976f, 1.0262874698072693f, 1.0265190611245079f, 67 | 1.0267507047024822f, 1.0269824005529853f, 1.027214148687813f, 68 | 1.0274459491187637f, 1.0276778018576387f, 1.0279097069162415f, 69 | 1.0281416643063788f, 1.0283736740398595f, 1.0286057361284953f, 70 | 1.028837850584101f, 1.0290700174184932f, 1.029302236643492f, 71 | 1.0295345082709197f, 1.0297668323126017f, 1.029999208780365f, 72 | 1.030231637686041f, 1.030464119041462f, 1.0306966528584645f, 73 | 1.0309292391488862f, 1.0311618779245688f, 1.0313945691973556f, 74 | 1.0316273129790936f, 1.0318601092816313f, 1.0320929581168212f, 75 | 1.0323258594965172f, 1.0325588134325767f, 1.0327918199368598f, 76 | 1.0330248790212284f, 1.033257990697548f, 1.0334911549776868f, 77 | 1.033724371873515f, 1.0339576413969056f, 1.0341909635597348f, 78 | 1.0344243383738811f, 1.034657765851226f, 1.034891246003653f, 79 | 1.0351247788430489f, 1.0353583643813031f, 1.0355920026303078f, 80 | 1.0358256936019572f, 1.0360594373081489f, 1.0362932337607829f, 81 | 1.0365270829717617f, 1.0367609849529913f, 1.0369949397163791f, 82 | 1.0372289472738365f, 1.0374630076372766f, 1.0376971208186156f, 83 | 1.0379312868297725f, 1.0381655056826686f, 1.0383997773892284f, 84 | 1.0386341019613787f, 1.0388684794110492f, 1.039102909750172f, 85 | 1.0393373929906822f, 1.0395719291445176f, 1.0398065182236185f, 86 | 1.0400411602399278f, 1.0402758552053915f, 1.0405106031319582f, 87 | 1.0407454040315787f, 1.040980257916207f, 1.0412151647977996f, 88 | 1.041450124688316f, 1.0416851375997183f, 1.0419202035439705f, 89 | 1.0421553225330404f, 1.042390494578898f, 1.042625719693516f, 90 | 1.0428609978888699f, 1.043096329176938f, 1.043331713569701f, 91 | 1.0435671510791424f, 1.0438026417172486f, 1.0440381854960086f, 92 | 1.0442737824274138f, 1.044509432523459f, 1.044745135796141f, 93 | 1.04498089225746f, 1.045216701919418f, 1.0454525647940205f, 94 | 1.0456884808932754f, 1.0459244502291931f, 1.0461604728137874f, 95 | 1.046396548659074f, 1.046632677777072f, 1.0468688601798024f, 96 | 1.0471050958792898f, 1.047341384887561f, 1.0475777272166455f, 97 | 1.047814122878576f, 1.048050571885387f, 1.0482870742491166f, 98 | 1.0485236299818055f, 1.0487602390954964f, 1.0489969016022356f, 99 | 1.0492336175140715f, 1.0494703868430555f, 1.0497072096012419f, 100 | 1.0499440858006872f, 1.0501810154534512f, 1.050417998571596f, 101 | 1.0506550351671864f, 1.0508921252522903f, 1.0511292688389782f, 102 | 1.051366465939323f, 1.0516037165654004f, 1.0518410207292894f, 103 | 1.052078378443071f, 1.0523157897188296f, 1.0525532545686513f, 104 | 1.0527907730046264f, 1.0530283450388465f, 1.0532659706834067f, 105 | 1.053503649950405f, 1.053741382851941f, 1.0539791694001188f, 106 | 1.0542170096070436f, 1.0544549034848243f, 1.0546928510455722f, 107 | 1.0549308523014012f, 1.0551689072644284f, 1.0554070159467728f, 108 | 1.0556451783605572f, 1.0558833945179062f, 1.056121664430948f, 109 | 1.0563599881118126f, 1.0565983655726334f, 1.0568367968255465f, 110 | 1.0570752818826903f, 1.0573138207562065f, 1.057552413458239f, 111 | 1.0577910600009348f, 1.0580297603964437f, 1.058268514656918f, 112 | 1.0585073227945128f, 1.0587461848213857f, 1.058985100749698f, 113 | 1.0592240705916123f, 114 | }; 115 | 116 | /* clang-format on */ 117 | 118 | static Sint32 119 | envelope(NdsApu *c, Uint32 age) 120 | { 121 | if(!c->r) return 0x0888; 122 | if(age < c->a) return 0x0888 * age / c->a; 123 | if(age < c->d) return 0x0444 * (2 * c->d - c->a - age) / (c->d - c->a); 124 | if(age < c->s) return 0x0444; 125 | if(age < c->r) return 0x0444 * (c->r - age) / (c->r - c->s); 126 | c->advance = 0; 127 | return 0x0000; 128 | } 129 | 130 | void 131 | nds_apu_start(NdsApu *c, Uint16 adsr, Uint8 pitch, Uint8 detune) 132 | { 133 | if(pitch < 108 && c->len) 134 | c->advance = (Uint32)((float)(advances[pitch % 12]) * detunes[detune]) >> (8 - pitch / 12); 135 | else { 136 | c->advance = 0; 137 | return; 138 | } 139 | c->a = ADSR_STEP * (adsr >> 12); 140 | c->d = ADSR_STEP * (adsr >> 8 & 0xf) + c->a; 141 | c->s = ADSR_STEP * (adsr >> 4 & 0xf) + c->d; 142 | c->r = ADSR_STEP * (adsr >> 0 & 0xf) + c->s; 143 | c->age = 0; 144 | c->i = 0; 145 | if(c->len <= 0x100) /* single cycle mode */ 146 | c->period = NOTE_PERIOD * 337 / 2 / c->len; 147 | else /* sample repeat mode */ 148 | c->period = NOTE_PERIOD; 149 | } 150 | 151 | Uint8 152 | nds_apu_get_vu(NdsApu *c) 153 | { 154 | int i; 155 | Sint32 sum[2] = {0, 0}; 156 | if(!c->advance || !c->period) return 0; 157 | for(i = 0; i < 2; i++) { 158 | if(!c->volume[i]) continue; 159 | sum[i] = 1 + envelope(c, c->age) * c->volume[i] / 0x800; 160 | if(sum[i] > 0xf) sum[i] = 0xf; 161 | } 162 | return (sum[0] << 4) | sum[1]; 163 | } 164 | -------------------------------------------------------------------------------- /arm9/source/nds_keyboard.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "util.h" 8 | #include "gfx_keyboard.h" 9 | #include "nds_keyboard.h" 10 | 11 | /* 12 | Copyright (c) 2018, 2023 Adrian "asie" Siekierka 13 | 14 | Permission to use, copy, modify, and distribute this software for any 15 | purpose with or without fee is hereby granted, provided that the above 16 | copyright notice and this permission notice appear in all copies. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 19 | WITH REGARD TO THIS SOFTWARE. 20 | */ 21 | 22 | typedef uint8_t keycode_t; 23 | 24 | typedef struct { 25 | u16 x, y, w, h; 26 | keycode_t keycode, keycode_shifted; 27 | uint8_t flags; 28 | } key_area_t; 29 | 30 | #define KEYS_HELD_MAX_COUNT 6 31 | #define KEY_SIZE 8 32 | #define KEY_YOFS (192 - (KEY_SIZE*2)*5) 33 | #define KEY_POS(ix, iy, iw) ((int)(ix*2)*(KEY_SIZE)+1), (KEY_YOFS)+((int)(iy*2)*(KEY_SIZE)+1), ((int)(iw*2)*(KEY_SIZE)-2), ((KEY_SIZE)*2-2) 34 | 35 | #define KF_MODIFIER 0x01 36 | #define KF_HELD 0x02 37 | 38 | #define KF_RUNTIME_HELD 0x40 39 | #define KF_RUNTIME_HOVERED 0x80 40 | #define KF_RUNTIME_ANY_HIGHLIGHT 0xC0 41 | #define KF_RUNTIME_ALL 0xC0 42 | 43 | static key_area_t keyboard_areas[] = { 44 | { KEY_POS(0, 0, 1), K_ESC, K_ESC, 0 }, 45 | { KEY_POS(1.5, 0, 1), '1', '!', 0 }, 46 | { KEY_POS(2.5, 0, 1), '2', '@', 0 }, 47 | { KEY_POS(3.5, 0, 1), '3', '#', 0 }, 48 | { KEY_POS(4.5, 0, 1), '4', '$', 0 }, 49 | { KEY_POS(5.5, 0, 1), '5', '%', 0 }, 50 | { KEY_POS(6.5, 0, 1), '6', '^', 0 }, 51 | { KEY_POS(7.5, 0, 1), '7', '&', 0 }, 52 | { KEY_POS(8.5, 0, 1), '8', '*', 0 }, 53 | { KEY_POS(9.5, 0, 1), '9', '(', 0 }, 54 | { KEY_POS(10.5, 0, 1), '0', ')', 0 }, 55 | { KEY_POS(11.5, 0, 1), '-', '_', 0 }, 56 | { KEY_POS(12.5, 0, 1), '=', '+', 0 }, 57 | { KEY_POS(13.5, 0, 1), '\\', '|', 0 }, 58 | { KEY_POS(14.5, 0, 1), '`', '~', 0 }, 59 | 60 | { KEY_POS(0, 1, 2), K_TAB, K_TAB, 0 }, 61 | { KEY_POS(2, 1, 1), 'q', 'Q', 0 }, 62 | { KEY_POS(3, 1, 1), 'w', 'W', 0 }, 63 | { KEY_POS(4, 1, 1), 'e', 'E', 0 }, 64 | { KEY_POS(5, 1, 1), 'r', 'R', 0 }, 65 | { KEY_POS(6, 1, 1), 't', 'T', 0 }, 66 | { KEY_POS(7, 1, 1), 'y', 'Y', 0 }, 67 | { KEY_POS(8, 1, 1), 'u', 'U', 0 }, 68 | { KEY_POS(9, 1, 1), 'i', 'I', 0 }, 69 | { KEY_POS(10, 1, 1), 'o', 'O', 0 }, 70 | { KEY_POS(11, 1, 1), 'p', 'P', 0 }, 71 | { KEY_POS(12, 1, 1), '[', '{', 0 }, 72 | { KEY_POS(13, 1, 1), ']', '}', 0 }, 73 | { KEY_POS(14, 1, 2), K_BKSP, K_BKSP, 0 }, 74 | 75 | { KEY_POS(0, 2, 2.5), K_CTRL, K_CTRL, KF_MODIFIER }, 76 | { KEY_POS(2.5, 2, 1), 'a', 'A', 0 }, 77 | { KEY_POS(3.5, 2, 1), 's', 'S', 0 }, 78 | { KEY_POS(4.5, 2, 1), 'd', 'D', 0 }, 79 | { KEY_POS(5.5, 2, 1), 'f', 'F', 0 }, 80 | { KEY_POS(6.5, 2, 1), 'g', 'G', 0 }, 81 | { KEY_POS(7.5, 2, 1), 'h', 'H', 0 }, 82 | { KEY_POS(8.5, 2, 1), 'j', 'J', 0 }, 83 | { KEY_POS(9.5, 2, 1), 'k', 'K', 0 }, 84 | { KEY_POS(10.5, 2, 1), 'l', 'L', 0 }, 85 | { KEY_POS(11.5, 2, 1), ';', ':', 0 }, 86 | { KEY_POS(12.5, 2, 1), '\'', '"', 0 }, 87 | { KEY_POS(13.5, 2, 2.5), K_RET, K_RET, 0 }, 88 | 89 | { KEY_POS(0, 3, 3), K_SHIFT, K_SHIFT, KF_MODIFIER }, 90 | { KEY_POS(3, 3, 1), 'z', 'Z', 0 }, 91 | { KEY_POS(4, 3, 1), 'x', 'X', 0 }, 92 | { KEY_POS(5, 3, 1), 'c', 'C', 0 }, 93 | { KEY_POS(6, 3, 1), 'v', 'V', 0 }, 94 | { KEY_POS(7, 3, 1), 'b', 'B', 0 }, 95 | { KEY_POS(8, 3, 1), 'n', 'N', 0 }, 96 | { KEY_POS(9, 3, 1), 'm', 'M', 0 }, 97 | { KEY_POS(10, 3, 1), ',', '<', 0 }, 98 | { KEY_POS(11, 3, 1), '.', '>', 0 }, 99 | { KEY_POS(12, 3, 1), '/', '?', 0 }, 100 | { KEY_POS(13, 3, 3), K_SHIFT, K_SHIFT, KF_MODIFIER }, 101 | 102 | { KEY_POS(0, 4, 1), K_SYSTEM, K_SYSTEM, 0 }, 103 | { KEY_POS(1.5, 4, 2), K_ALT, K_ALT, KF_MODIFIER }, 104 | { KEY_POS(3.5, 4, 8), ' ', ' ', 0 }, 105 | { KEY_POS(11.5, 4, 2), K_ALT, K_ALT, KF_MODIFIER }, 106 | { KEY_POS(13.5, 4, 1), K_HOME, K_HOME, KF_HELD }, 107 | { KEY_POS(15, 4, 1), K_DEL, K_DEL, 0 } 108 | }; 109 | #define KEYBOARD_AREAS_COUNT (sizeof(keyboard_areas) / sizeof(key_area_t)) 110 | 111 | #define KR_CLEAR_HELD 0x02 112 | #define KR_CAPS_LOCK 0x04 113 | 114 | #define KBD_MAP_BASE 20 115 | #define KBD_TILE_BASE 2 116 | 117 | static keycode_t keys_held[KEYS_HELD_MAX_COUNT]; 118 | static uint8_t keys_held_count; 119 | static uint8_t keyboard_reqs; 120 | 121 | static bool key_area_touched(touchPosition* pos, const key_area_t* area) { 122 | return pos->px >= area->x && pos->py >= area->y 123 | && pos->px < (area->x + area->w) 124 | && pos->py < (area->y + area->h); 125 | } 126 | 127 | bool keyboard_is_held(int key) { 128 | for (int i = 0; i < keys_held_count; i++) { 129 | if (keys_held[i] == key) 130 | return true; 131 | } 132 | return false; 133 | } 134 | 135 | static void keyboard_unhold(int key) { 136 | for (int i = 0; i < keys_held_count; i++) { 137 | if (keys_held[i] == key) { 138 | for (int j = i; j < keys_held_count - 1; j++) { 139 | keys_held[j] = keys_held[j + 1]; 140 | } 141 | i--; 142 | keys_held_count--; 143 | } 144 | } 145 | } 146 | 147 | static bool keyboard_is_shifted(void) { 148 | return (keyboard_reqs & KR_CAPS_LOCK) || keyboard_is_held(K_SHIFT); 149 | } 150 | 151 | static void keyboard_update_map(void) { 152 | bool shifted = keyboard_is_shifted(); 153 | uint32_t *src = (uint32_t*) (gfx_keyboardMap + (shifted ? 32*10 : 0)); 154 | uint32_t *dst = (uint32_t*) (BG_MAP_RAM_SUB(KBD_MAP_BASE) + (32*14)); 155 | 156 | for (int i = 0; i < 32*10/2; i++, src++, dst++) { 157 | *dst = (*dst & 0xF000F000) | (*src & 0x0FFF0FFF); 158 | } 159 | } 160 | 161 | static void keyboard_update_palette(key_area_t *area) { 162 | uint16_t palette_idx = 0; 163 | bool shifted = keyboard_is_shifted(); 164 | keycode_t keycode = shifted ? area->keycode_shifted : area->keycode; 165 | 166 | if (keycode == K_SHIFT && (keyboard_reqs & KR_CAPS_LOCK)) { 167 | palette_idx = 2 << 12; 168 | } else if (area->flags & KF_RUNTIME_ANY_HIGHLIGHT) { 169 | palette_idx = 1 << 12; 170 | } 171 | 172 | for (int ty = (area->y >> 3); ty <= ((area->y + area->h - 1) >> 3); ty++) { 173 | for (int tx = (area->x >> 3); tx <= ((area->x + area->w - 1) >> 3); tx++) { 174 | uint16_t tile = BG_MAP_RAM_SUB(KBD_MAP_BASE)[(ty << 5) | tx]; 175 | BG_MAP_RAM_SUB(KBD_MAP_BASE)[(ty << 5) | tx] = (tile & 0xFFF) | palette_idx; 176 | } 177 | } 178 | } 179 | 180 | static void keyboard_update_palettes(int target_keycode) { 181 | bool shifted = keyboard_is_shifted(); 182 | for (int i = 0; i < KEYBOARD_AREAS_COUNT; i++) { 183 | key_area_t* area = &keyboard_areas[i]; 184 | keycode_t keycode = shifted ? area->keycode_shifted : area->keycode; 185 | if (target_keycode == -1 || target_keycode == keycode) { 186 | keyboard_update_palette(area); 187 | } 188 | } 189 | } 190 | 191 | __attribute__((optimize("-Os"))) 192 | static void keyboard_set_palette_colors(uint16_t *pal, uint8_t mul /* 0-15 */) { 193 | for (int i = 0; i < 14; i++) { 194 | uint16_t col = gfx_keyboardPal[i]; 195 | pal[i] = 196 | ((col & 0x1F) * mul / 15) 197 | | ((((col >> 5) & 0x1F) * mul / 15) << 5) 198 | | ((((col >> 10) & 0x1F) * mul / 15) << 10) 199 | | (col & 0x8000); 200 | } 201 | } 202 | 203 | __attribute__((optimize("-Os"))) 204 | bool keyboard_init(void) { 205 | decompress(gfx_keyboardTiles, BG_TILE_RAM_SUB(KBD_TILE_BASE), LZ77Vram); 206 | memset(BG_MAP_RAM_SUB(KBD_MAP_BASE), 0, 2*(32*14)); 207 | 208 | memcpy(BG_PALETTE_SUB, gfx_keyboardPal, 2*14); 209 | keyboard_set_palette_colors(BG_PALETTE_SUB + 16, 12); 210 | keyboard_set_palette_colors(BG_PALETTE_SUB + 32, 10); 211 | 212 | keyboard_clear(); 213 | swiWaitForVBlank(); 214 | REG_BG3CNT_SUB = BG_32x32 | BG_COLOR_16 | BG_PRIORITY_3 | BG_TILE_BASE(KBD_TILE_BASE) | BG_MAP_BASE(KBD_MAP_BASE); 215 | REG_BG3HOFS_SUB = 0; 216 | REG_BG3VOFS_SUB = 0; 217 | videoBgEnableSub(3); 218 | return true; 219 | } 220 | 221 | void keyboard_exit(void) { 222 | 223 | } 224 | 225 | void keyboard_clear(void) { 226 | for (int i = 0; i < KEYBOARD_AREAS_COUNT; i++) { 227 | key_area_t* area = &keyboard_areas[i]; 228 | area->flags &= ~KF_RUNTIME_ALL; 229 | } 230 | memcpy(BG_MAP_RAM_SUB(KBD_MAP_BASE) + (32*14), gfx_keyboardMap, 2*(32*10)); 231 | keys_held_count = 0; 232 | } 233 | 234 | int keyboard_update(void) { 235 | u32 kDown = keysDown(); 236 | u32 kHeld = keysHeld(); 237 | u32 kUp = keysUp(); 238 | touchPosition pos; 239 | int retval = -1; 240 | 241 | if (keyboard_reqs & KR_CLEAR_HELD) { 242 | bool was_shifted = keyboard_is_shifted(); 243 | // no keypress ongoing - pop all held keys 244 | keys_held_count = 0; 245 | for (int i = 0; i < KEYBOARD_AREAS_COUNT; i++) { 246 | key_area_t* area = &keyboard_areas[i]; 247 | if (area->flags & KF_RUNTIME_HELD) { 248 | area->flags &= ~KF_RUNTIME_HELD; 249 | keyboard_update_palette(area); 250 | } 251 | } 252 | keyboard_reqs &= ~KR_CLEAR_HELD; 253 | if (was_shifted) { 254 | keyboard_update_map(); 255 | } 256 | } 257 | 258 | bool touching = ((kDown | kHeld) & KEY_TOUCH) != 0; 259 | bool shifted = keyboard_is_shifted(); 260 | if (touching) { 261 | touchRead(&pos); 262 | 263 | // handle key hovering 264 | for (int i = 0; i < KEYBOARD_AREAS_COUNT; i++) { 265 | key_area_t* area = &keyboard_areas[i]; 266 | if (key_area_touched(&pos, area)) { 267 | keycode_t keycode = shifted ? area->keycode_shifted : area->keycode; 268 | 269 | if (!(area->flags & KF_RUNTIME_HOVERED)) { 270 | area->flags |= KF_RUNTIME_HOVERED; 271 | keyboard_update_palette(area); 272 | } 273 | if (area->flags & KF_HELD) { 274 | retval = keycode; 275 | } 276 | } else { 277 | if (area->flags & KF_RUNTIME_HOVERED) { 278 | area->flags &= ~KF_RUNTIME_HOVERED; 279 | keyboard_update_palette(area); 280 | } 281 | } 282 | } 283 | } else if (kUp & KEY_TOUCH) { 284 | // actually press key 285 | for (int i = 0; i < KEYBOARD_AREAS_COUNT; i++) { 286 | key_area_t* area = &keyboard_areas[i]; 287 | if (area->flags & KF_RUNTIME_HOVERED) { 288 | area->flags &= ~KF_RUNTIME_HOVERED; 289 | keycode_t keycode = shifted ? area->keycode_shifted : area->keycode; 290 | 291 | if ((keyboard_reqs & KR_CAPS_LOCK) && area->keycode_shifted == area->keycode) { 292 | keyboard_reqs &= ~KR_CAPS_LOCK; 293 | keyboard_reqs |= KR_CLEAR_HELD; 294 | } else if ((area->flags & KF_RUNTIME_HELD) && keycode == K_SHIFT) { 295 | keyboard_reqs |= KR_CAPS_LOCK; 296 | } else if (area->flags & KF_MODIFIER) { 297 | if (area->flags & KF_RUNTIME_HELD) { 298 | keyboard_unhold(keycode); 299 | area->flags &= ~KF_RUNTIME_HELD; 300 | } else if (keys_held_count < KEYS_HELD_MAX_COUNT && !keyboard_is_held(keycode)) { 301 | keys_held[keys_held_count++] = keycode; 302 | area->flags |= KF_RUNTIME_HELD; 303 | } 304 | } else { 305 | if (!(area->flags & KF_HELD)) { 306 | retval = keycode; 307 | } 308 | keyboard_reqs |= KR_CLEAR_HELD; 309 | } 310 | 311 | if (keycode == K_SHIFT) { 312 | keyboard_update_map(); 313 | } 314 | if (area->flags & KF_MODIFIER) { 315 | keyboard_update_palettes(keycode); 316 | } else { 317 | keyboard_update_palette(area); 318 | } 319 | } 320 | } 321 | } 322 | 323 | return retval; 324 | } 325 | -------------------------------------------------------------------------------- /arm9/source/nds_keyboard.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | Copyright (c) 2018, 2023 Adrian "asie" Siekierka 5 | 6 | Permission to use, copy, modify, and distribute this software for any 7 | purpose with or without fee is hereby granted, provided that the above 8 | copyright notice and this permission notice appear in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | WITH REGARD TO THIS SOFTWARE. 12 | */ 13 | 14 | #define K_BKSP 8 15 | #define K_TAB 9 16 | #define K_RET 13 17 | #define K_ESC 27 18 | #define K_DEL 127 19 | #define K_CTRL 128 20 | #define K_ALT 129 21 | #define K_SHIFT 130 22 | #define K_HOME 131 23 | #define K_SYSTEM 132 24 | 25 | bool keyboard_is_held(int key); 26 | bool keyboard_init(void); 27 | void keyboard_exit(void); 28 | void keyboard_clear(void); 29 | // Returns pressed keycode, or -1 if no key pressed. 30 | int keyboard_update(void); 31 | -------------------------------------------------------------------------------- /arm9/source/nds_ppu.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* 6 | Copyright (c) 2021 Devine Lu Linvega 7 | Copyright (c) 2021 Andrew Alderwick 8 | Copyright (c) 2021 Adrian "asie" Siekierka 9 | 10 | Permission to use, copy, modify, and distribute this software for any 11 | purpose with or without fee is hereby granted, provided that the above 12 | copyright notice and this permission notice appear in all copies. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 15 | WITH REGARD TO THIS SOFTWARE. 16 | */ 17 | 18 | #define PPU_TILES_WIDTH 32 19 | #define PPU_TILES_HEIGHT 24 20 | #define PPU_PIXELS_WIDTH (PPU_TILES_WIDTH * 8) 21 | #define PPU_PIXELS_HEIGHT (PPU_TILES_HEIGHT * 8) 22 | 23 | typedef unsigned char Uint8; 24 | typedef unsigned short Uint16; 25 | typedef unsigned int Uint32; 26 | 27 | typedef struct NdsPpu { 28 | Uint32 *bg, *fg; 29 | } NdsPpu; 30 | 31 | int nds_initppu(NdsPpu *p); 32 | void nds_putcolors(NdsPpu *p, Uint8 *addr); 33 | void nds_ppu_pixel(NdsPpu *p, Uint32 *layer, Uint16 x, Uint16 y, Uint8 color); 34 | void nds_ppu_fill(NdsPpu *p, Uint32 *layer, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2, Uint8 color); 35 | void nds_ppu_2bpp(NdsPpu *p, Uint32 *layer, int16_t x, int16_t y, Uint8 *sprite, Uint8 color, Uint8 flipx, Uint8 flipy); 36 | void nds_ppu_1bpp(NdsPpu *p, Uint32 *layer, int16_t x, int16_t y, Uint8 *sprite, Uint8 color, Uint8 flipx, Uint8 flipy); 37 | void nds_copyppu(NdsPpu *p); 38 | -------------------------------------------------------------------------------- /arm9/source/stubs.c: -------------------------------------------------------------------------------- 1 | // Admittedly, this was more useful to reduce filesize before "datetime" device support. 2 | #if 0 3 | #ifdef __BLOCKSDS__ 4 | #include 5 | 6 | uint32_t get_fattime(void) { 7 | return 0; 8 | } 9 | #else 10 | #include 11 | #include 12 | 13 | // Stubbing out unused functionality, in order to optimize the uxnds filesize. 14 | 15 | // libfat source/filetime.c 16 | 17 | uint16_t _FAT_filetime_getTimeFromRTC (void) { 18 | return 0; 19 | } 20 | 21 | uint16_t _FAT_filetime_getDateFromRTC (void) { 22 | return 0; 23 | 24 | } 25 | time_t _FAT_filetime_to_time_t (uint16_t t, uint16_t d) { 26 | return 0; 27 | } 28 | #endif 29 | #endif 30 | -------------------------------------------------------------------------------- /assets/3ds/gfx_keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/uxnds/56a2027c3d6ca96f45202e1594eaac8ac0ad8152/assets/3ds/gfx_keyboard.png -------------------------------------------------------------------------------- /assets/3ds/gfx_keyboard.t3s: -------------------------------------------------------------------------------- 1 | -f rgba8888 -z auto 2 | gfx_keyboard.png 3 | -------------------------------------------------------------------------------- /assets/nds/gfx_keyboard.grit: -------------------------------------------------------------------------------- 1 | -gt 2 | -gB4 3 | -gzl 4 | -m 5 | -p 6 | -ps0 7 | -pn16 8 | -------------------------------------------------------------------------------- /assets/nds/gfx_keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/uxnds/56a2027c3d6ca96f45202e1594eaac8ac0ad8152/assets/nds/gfx_keyboard.png -------------------------------------------------------------------------------- /include/emulator_config.h: -------------------------------------------------------------------------------- 1 | #define ENABLE_CTR_3D 2 | #define ENABLE_KEYBOARD 3 | #define ENABLE_TOUCH 4 | // #define USE_BOTTOM_SCREEN_DEFAULT 5 | -------------------------------------------------------------------------------- /include/nds/apu.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2021 Devine Lu Linvega 3 | Copyright (c) 2021 Andrew Alderwick 4 | Copyright (c) 2021 Adrian "asie" Siekierka 5 | 6 | Permission to use, copy, modify, and distribute this software for any 7 | purpose with or without fee is hereby granted, provided that the above 8 | copyright notice and this permission notice appear in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | WITH REGARD TO THIS SOFTWARE. 12 | */ 13 | 14 | typedef unsigned int Uint32; 15 | typedef signed int Sint32; 16 | 17 | #define SAMPLE_FREQUENCY 22050 18 | #define POLYPHONY 4 19 | 20 | typedef struct { 21 | Uint8 *addr; 22 | Uint32 count, advance, period, age, a, d, s, r; 23 | Uint16 i, len; 24 | Sint8 volume[2]; 25 | Uint8 pitch, repeat; 26 | } NdsApu; 27 | 28 | void nds_apu_render(NdsApu *c, Sint16 *sample_left, Sint16 *sample_right, int samples); /* ARM7 */ 29 | void nds_apu_start(NdsApu *c, Uint16 adsr, Uint8 pitch, Uint8 detune); /* ARM9 */ 30 | Uint8 nds_apu_get_vu(NdsApu *c); /* ARM9 */ 31 | -------------------------------------------------------------------------------- /include/nds/fifo.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2021 Adrian "asie" Siekierka 3 | 4 | Permission to use, copy, modify, and distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE. 10 | */ 11 | 12 | #include 13 | 14 | #define UXNDS_AUDIO_BUFFER_SIZE 512 15 | #define UXNDS_FIFO_CHANNEL FIFO_USER_01 16 | #define UXNDS_FIFO_CMD_SET_RATE 0x10000000 17 | #define UXNDS_FIFO_CMD_SET_ADDR 0x20000000 18 | #define UXNDS_FIFO_CMD_APU0 0x80000000 19 | #define UXNDS_FIFO_CMD_APU1 0x90000000 20 | #define UXNDS_FIFO_CMD_APU2 0xA0000000 21 | #define UXNDS_FIFO_CMD_APU3 0xB0000000 22 | #define UXNDS_FIFO_CMD_MASK 0xF0000000 23 | 24 | -------------------------------------------------------------------------------- /misc/uxn32.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/uxnds/56a2027c3d6ca96f45202e1594eaac8ac0ad8152/misc/uxn32.bmp -------------------------------------------------------------------------------- /misc/uxn32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/uxnds/56a2027c3d6ca96f45202e1594eaac8ac0ad8152/misc/uxn32.png -------------------------------------------------------------------------------- /misc/uxn48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/uxnds/56a2027c3d6ca96f45202e1594eaac8ac0ad8152/misc/uxn48.png -------------------------------------------------------------------------------- /source/3ds/ctr_keyboard.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include <3ds.h> 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../util.h" 11 | #include "ctr_util.h" 12 | #include "ctr_keyboard.h" 13 | 14 | /* 15 | Copyright (c) 2018, 2023 Adrian "asie" Siekierka 16 | 17 | Permission to use, copy, modify, and distribute this software for any 18 | purpose with or without fee is hereby granted, provided that the above 19 | copyright notice and this permission notice appear in all copies. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 22 | WITH REGARD TO THIS SOFTWARE. 23 | */ 24 | 25 | typedef uint8_t keycode_t; 26 | 27 | typedef struct { 28 | u16 x, y, w, h; 29 | keycode_t keycode, keycode_shifted; 30 | uint8_t flags; 31 | } key_area_t; 32 | 33 | #define KEYS_HELD_MAX_COUNT 6 34 | #define KEY_SIZE 10 35 | #define KEY_YOFS (240 - (KEY_SIZE*2)*5) 36 | #define KEY_POS(ix, iy, iw) ((int)(ix*2)*(KEY_SIZE)+1), (KEY_YOFS)+((int)(iy*2)*(KEY_SIZE)+1), ((int)(iw*2)*(KEY_SIZE)-2), ((KEY_SIZE)*2-2) 37 | 38 | #define KF_MODIFIER 0x01 39 | #define KF_HELD 0x02 40 | 41 | #define KF_RUNTIME_HELD 0x40 42 | #define KF_RUNTIME_HOVERED 0x80 43 | #define KF_RUNTIME_ANY_HIGHLIGHT 0xC0 44 | #define KF_RUNTIME_ALL 0xC0 45 | 46 | static key_area_t keyboard_areas[] = { 47 | { KEY_POS(0, 0, 1), K_ESC, K_ESC, 0 }, 48 | { KEY_POS(1.5, 0, 1), '1', '!', 0 }, 49 | { KEY_POS(2.5, 0, 1), '2', '@', 0 }, 50 | { KEY_POS(3.5, 0, 1), '3', '#', 0 }, 51 | { KEY_POS(4.5, 0, 1), '4', '$', 0 }, 52 | { KEY_POS(5.5, 0, 1), '5', '%', 0 }, 53 | { KEY_POS(6.5, 0, 1), '6', '^', 0 }, 54 | { KEY_POS(7.5, 0, 1), '7', '&', 0 }, 55 | { KEY_POS(8.5, 0, 1), '8', '*', 0 }, 56 | { KEY_POS(9.5, 0, 1), '9', '(', 0 }, 57 | { KEY_POS(10.5, 0, 1), '0', ')', 0 }, 58 | { KEY_POS(11.5, 0, 1), '-', '_', 0 }, 59 | { KEY_POS(12.5, 0, 1), '=', '+', 0 }, 60 | { KEY_POS(13.5, 0, 1), '\\', '|', 0 }, 61 | { KEY_POS(14.5, 0, 1), '`', '~', 0 }, 62 | 63 | { KEY_POS(0, 1, 2), K_TAB, K_TAB, 0 }, 64 | { KEY_POS(2, 1, 1), 'q', 'Q', 0 }, 65 | { KEY_POS(3, 1, 1), 'w', 'W', 0 }, 66 | { KEY_POS(4, 1, 1), 'e', 'E', 0 }, 67 | { KEY_POS(5, 1, 1), 'r', 'R', 0 }, 68 | { KEY_POS(6, 1, 1), 't', 'T', 0 }, 69 | { KEY_POS(7, 1, 1), 'y', 'Y', 0 }, 70 | { KEY_POS(8, 1, 1), 'u', 'U', 0 }, 71 | { KEY_POS(9, 1, 1), 'i', 'I', 0 }, 72 | { KEY_POS(10, 1, 1), 'o', 'O', 0 }, 73 | { KEY_POS(11, 1, 1), 'p', 'P', 0 }, 74 | { KEY_POS(12, 1, 1), '[', '{', 0 }, 75 | { KEY_POS(13, 1, 1), ']', '}', 0 }, 76 | { KEY_POS(14, 1, 2), K_BKSP, K_BKSP, 0 }, 77 | 78 | { KEY_POS(0, 2, 2.5), K_CTRL, K_CTRL, KF_MODIFIER }, 79 | { KEY_POS(2.5, 2, 1), 'a', 'A', 0 }, 80 | { KEY_POS(3.5, 2, 1), 's', 'S', 0 }, 81 | { KEY_POS(4.5, 2, 1), 'd', 'D', 0 }, 82 | { KEY_POS(5.5, 2, 1), 'f', 'F', 0 }, 83 | { KEY_POS(6.5, 2, 1), 'g', 'G', 0 }, 84 | { KEY_POS(7.5, 2, 1), 'h', 'H', 0 }, 85 | { KEY_POS(8.5, 2, 1), 'j', 'J', 0 }, 86 | { KEY_POS(9.5, 2, 1), 'k', 'K', 0 }, 87 | { KEY_POS(10.5, 2, 1), 'l', 'L', 0 }, 88 | { KEY_POS(11.5, 2, 1), ';', ':', 0 }, 89 | { KEY_POS(12.5, 2, 1), '\'', '"', 0 }, 90 | { KEY_POS(13.5, 2, 2.5), K_RET, K_RET, 0 }, 91 | 92 | { KEY_POS(0, 3, 3), K_SHIFT, K_SHIFT, KF_MODIFIER }, 93 | { KEY_POS(3, 3, 1), 'z', 'Z', 0 }, 94 | { KEY_POS(4, 3, 1), 'x', 'X', 0 }, 95 | { KEY_POS(5, 3, 1), 'c', 'C', 0 }, 96 | { KEY_POS(6, 3, 1), 'v', 'V', 0 }, 97 | { KEY_POS(7, 3, 1), 'b', 'B', 0 }, 98 | { KEY_POS(8, 3, 1), 'n', 'N', 0 }, 99 | { KEY_POS(9, 3, 1), 'm', 'M', 0 }, 100 | { KEY_POS(10, 3, 1), ',', '<', 0 }, 101 | { KEY_POS(11, 3, 1), '.', '>', 0 }, 102 | { KEY_POS(12, 3, 1), '/', '?', 0 }, 103 | { KEY_POS(13, 3, 3), K_SHIFT, K_SHIFT, KF_MODIFIER }, 104 | 105 | { KEY_POS(0, 4, 1), K_SYSTEM, K_SYSTEM, 0 }, 106 | { KEY_POS(1.5, 4, 2), K_ALT, K_ALT, KF_MODIFIER }, 107 | { KEY_POS(3.5, 4, 8), ' ', ' ', 0 }, 108 | { KEY_POS(11.5, 4, 2), K_ALT, K_ALT, KF_MODIFIER }, 109 | { KEY_POS(13.5, 4, 1), K_HOME, K_HOME, KF_HELD }, 110 | { KEY_POS(15, 4, 1), K_DEL, K_DEL, 0 } 111 | }; 112 | #define KEYBOARD_AREAS_COUNT (sizeof(keyboard_areas) / sizeof(key_area_t)) 113 | 114 | #define KR_REDRAW 0x01 115 | #define KR_CLEAR_HELD 0x02 116 | #define KR_CAPS_LOCK 0x04 117 | 118 | static keycode_t keys_held[KEYS_HELD_MAX_COUNT]; 119 | static uint8_t keys_held_count; 120 | static uint8_t keyboard_reqs; 121 | static C3D_Tex gfx_keyboard_tex; 122 | 123 | static bool key_area_touched(touchPosition* pos, const key_area_t* area) { 124 | return pos->px >= area->x && pos->py >= area->y 125 | && pos->px < (area->x + area->w) 126 | && pos->py < (area->y + area->h); 127 | } 128 | 129 | bool keyboard_is_held(int key) { 130 | for (int i = 0; i < keys_held_count; i++) { 131 | if (keys_held[i] == key) 132 | return true; 133 | } 134 | return false; 135 | } 136 | 137 | static void keyboard_unhold(int key) { 138 | for (int i = 0; i < keys_held_count; i++) { 139 | if (keys_held[i] == key) { 140 | for (int j = i; j < keys_held_count - 1; j++) { 141 | keys_held[j] = keys_held[j + 1]; 142 | } 143 | i--; 144 | keys_held_count--; 145 | } 146 | } 147 | } 148 | 149 | static bool keyboard_is_shifted(void) { 150 | return (keyboard_reqs & KR_CAPS_LOCK) || keyboard_is_held(K_SHIFT); 151 | } 152 | 153 | bool keyboard_init(void) { 154 | if (!ctr_load_t3x(&gfx_keyboard_tex, "romfs:/gfx_keyboard.t3x", TEXTURE_TARGET_VRAM)) { 155 | return false; 156 | } 157 | 158 | keyboard_clear(); 159 | return true; 160 | } 161 | 162 | void keyboard_exit(void) { 163 | C3D_TexDelete(&gfx_keyboard_tex); 164 | } 165 | 166 | void keyboard_draw(void) { 167 | bool shifted = keyboard_is_shifted(); 168 | 169 | C2D_Image kbd_image; 170 | Tex3DS_SubTexture kbd_subtex; 171 | 172 | kbd_subtex.width = 320; 173 | kbd_subtex.height = 100; 174 | kbd_subtex.left = 0.0f; 175 | kbd_subtex.top = 1.0f - ((shifted ? 100.0f : 200.0f) / 256.0f); 176 | kbd_subtex.right = 320.0f / 512.0f; 177 | kbd_subtex.bottom = kbd_subtex.top + (100.0f / 256.0f); 178 | kbd_image.tex = &gfx_keyboard_tex; 179 | kbd_image.subtex = &kbd_subtex; 180 | 181 | C2D_DrawImageAt(kbd_image, 0, KEY_YOFS, 0.0f, NULL, 1.0f, 1.0f); 182 | 183 | for (int i = 0; i < KEYBOARD_AREAS_COUNT; i++) { 184 | const key_area_t* area = &keyboard_areas[i]; 185 | keycode_t keycode = shifted ? area->keycode_shifted : area->keycode; 186 | if (keycode == K_SHIFT && (keyboard_reqs & KR_CAPS_LOCK)) { 187 | C2D_DrawRectSolid(area->x, area->y, 0.0f, area->w, area->h, 0x70000000); 188 | } else if (area->flags & KF_RUNTIME_ANY_HIGHLIGHT) { 189 | C2D_DrawRectSolid(area->x, area->y, 1.0f, area->w, area->h, 0x38000000); 190 | } 191 | } 192 | 193 | keyboard_reqs &= ~KR_REDRAW; 194 | } 195 | 196 | bool keyboard_needs_draw(void) { 197 | return keyboard_reqs & KR_REDRAW; 198 | } 199 | 200 | void keyboard_clear(void) { 201 | for (int i = 0; i < KEYBOARD_AREAS_COUNT; i++) { 202 | key_area_t* area = &keyboard_areas[i]; 203 | area->flags &= ~KF_RUNTIME_ALL; 204 | } 205 | keys_held_count = 0; 206 | keyboard_reqs = KR_REDRAW; 207 | } 208 | 209 | int keyboard_update(void) { 210 | u32 kDown = hidKeysDown(); 211 | u32 kHeld = hidKeysHeld(); 212 | u32 kUp = hidKeysUp(); 213 | touchPosition pos; 214 | int retval = -1; 215 | 216 | if (keyboard_reqs & KR_CLEAR_HELD) { 217 | // no keypress ongoing - pop all held keys 218 | if (keys_held_count > 0) { 219 | keys_held_count = 0; 220 | keyboard_reqs |= KR_REDRAW; 221 | } 222 | for (int i = 0; i < KEYBOARD_AREAS_COUNT; i++) { 223 | key_area_t* area = &keyboard_areas[i]; 224 | area->flags &= ~KF_RUNTIME_HELD; 225 | } 226 | keyboard_reqs &= ~KR_CLEAR_HELD; 227 | } 228 | 229 | bool touching = ((kDown | kHeld) & KEY_TOUCH) != 0; 230 | bool shifted = keyboard_is_shifted(); 231 | if (touching) { 232 | hidTouchRead(&pos); 233 | 234 | // handle key hovering 235 | for (int i = 0; i < KEYBOARD_AREAS_COUNT; i++) { 236 | key_area_t* area = &keyboard_areas[i]; 237 | if (key_area_touched(&pos, area)) { 238 | if (!(area->flags & KF_RUNTIME_HOVERED)) 239 | keyboard_reqs |= KR_REDRAW; 240 | keycode_t keycode = shifted ? area->keycode_shifted : area->keycode; 241 | 242 | area->flags |= KF_RUNTIME_HOVERED; 243 | if (area->flags & KF_HELD) { 244 | retval = keycode; 245 | } 246 | } else { 247 | area->flags &= ~KF_RUNTIME_HOVERED; 248 | } 249 | } 250 | } else if (kUp & KEY_TOUCH) { 251 | // actually press key 252 | for (int i = 0; i < KEYBOARD_AREAS_COUNT; i++) { 253 | key_area_t* area = &keyboard_areas[i]; 254 | if (area->flags & KF_RUNTIME_HOVERED) { 255 | keyboard_reqs |= KR_REDRAW; 256 | area->flags &= ~KF_RUNTIME_HOVERED; 257 | keycode_t keycode = shifted ? area->keycode_shifted : area->keycode; 258 | 259 | if ((keyboard_reqs & KR_CAPS_LOCK) && area->keycode_shifted == area->keycode) { 260 | keyboard_reqs &= ~KR_CAPS_LOCK; 261 | keyboard_reqs |= KR_CLEAR_HELD; 262 | } else if ((area->flags & KF_RUNTIME_HELD) && keycode == K_SHIFT) { 263 | keyboard_reqs |= KR_CAPS_LOCK; 264 | } else if (area->flags & KF_MODIFIER) { 265 | if (area->flags & KF_RUNTIME_HELD) { 266 | keyboard_unhold(keycode); 267 | area->flags &= ~KF_RUNTIME_HELD; 268 | } else if (keys_held_count < KEYS_HELD_MAX_COUNT && !keyboard_is_held(keycode)) { 269 | keys_held[keys_held_count++] = keycode; 270 | area->flags |= KF_RUNTIME_HELD; 271 | } 272 | } else { 273 | if (!(area->flags & KF_HELD)) { 274 | retval = keycode; 275 | } 276 | keyboard_reqs |= KR_CLEAR_HELD; 277 | } 278 | } 279 | } 280 | } 281 | 282 | return retval; 283 | } 284 | -------------------------------------------------------------------------------- /source/3ds/ctr_keyboard.h: -------------------------------------------------------------------------------- 1 | #include <3ds.h> 2 | #include 3 | #include 4 | #include 5 | 6 | /* 7 | Copyright (c) 2018, 2023 Adrian "asie" Siekierka 8 | 9 | Permission to use, copy, modify, and distribute this software for any 10 | purpose with or without fee is hereby granted, provided that the above 11 | copyright notice and this permission notice appear in all copies. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 | WITH REGARD TO THIS SOFTWARE. 15 | */ 16 | 17 | #define K_BKSP 8 18 | #define K_TAB 9 19 | #define K_RET 13 20 | #define K_ESC 27 21 | #define K_DEL 127 22 | #define K_CTRL 128 23 | #define K_ALT 129 24 | #define K_SHIFT 130 25 | #define K_HOME 131 26 | #define K_SYSTEM 132 27 | 28 | bool keyboard_is_held(int key); 29 | bool keyboard_init(void); 30 | void keyboard_exit(void); 31 | void keyboard_clear(void); 32 | void keyboard_draw(void); 33 | bool keyboard_needs_draw(void); 34 | // Returns pressed keycode, or -1 if no key pressed. 35 | int keyboard_update(void); 36 | -------------------------------------------------------------------------------- /source/3ds/ctr_screen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include <3ds.h> 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../uxn.h" 11 | #include "../util.h" 12 | #include "ctr_screen.h" 13 | 14 | /* 15 | Copyright (c) 2021-2023 Devine Lu Linvega, Andrew Alderwick 16 | Copyright (c) 2023 Adrian "asie" Siekierka 17 | 18 | Permission to use, copy, modify, and distribute this software for any 19 | purpose with or without fee is hereby granted, provided that the above 20 | copyright notice and this permission notice appear in all copies. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 23 | WITH REGARD TO THIS SOFTWARE. 24 | */ 25 | 26 | UxnCtrScreen uxn_ctr_screen; 27 | 28 | /* c = !ch ? (color % 5 ? color >> 2 : 0) : color % 4 + ch == 1 ? 0 : (ch - 2 + (color & 3)) % 3 + 1; */ 29 | 30 | static Uint8 blending[4][16] = { 31 | {0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0}, 32 | {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}, 33 | {1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1}, 34 | {2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2}}; 35 | 36 | static inline void 37 | screen_change(UxnCtrScreen *scr, Layer *s, Uint16 x1, Uint16 y1, Uint16 x2, Uint16 y2) 38 | { 39 | if(y1 > scr->height && y2 > y1) return; 40 | if(y1 > y2) y1 = 0; 41 | if(y1 >= scr->height) s->y1 = 0; 42 | else if(y1 < s->y1) s->y1 = y1; 43 | if(y2 > scr->height) s->y2 = scr->height; 44 | else if(y2 > s->y2) s->y2 = y2; 45 | } 46 | 47 | __attribute__((optimize("-O3"))) 48 | static void 49 | screen_fill(UxnCtrScreen *s, Uint8 *pixels, int x1, int y1, int x2, int y2, Uint8 color) 50 | { 51 | int y, width = s->width, height = s->height; 52 | if (x2 > width) x2 = width; 53 | if (y2 > height) y2 = height; 54 | //iprintf("fill %d %d %d %d\n", x1, y1, x2, y2); 55 | for(y = y1; y < y2; y++) 56 | memset(pixels + (y * width) + x1, color, x2-x1); 57 | } 58 | 59 | __attribute__((optimize("-O3"))) 60 | static void 61 | screen_blit(UxnCtrScreen *s, Uint8 *pixels, Uint16 x1, Uint16 y1, Uint8 *ram, Uint16 addr, Uint8 color, Uint8 flipx, Uint8 flipy, Uint8 twobpp) 62 | { 63 | int v, h, width = s->width, height = s->height, opaque = (color % 5); 64 | if ((color == 1 || color == 5) && !flipy && !flipx && x1 <= width-8 && y1 <= height-8) { 65 | // fast path 66 | for(v = 0; v < 8; v++) { 67 | Uint16 c = ram[(addr + v) & 0xffff] | (twobpp ? (ram[(addr + v + 8) & 0xffff] << 8) : 0); 68 | Uint16 y = y1 + v; 69 | if(opaque) { 70 | for(h = 7; h >= 0; --h, c >>= 1) { 71 | Uint8 ch = (c & 1) | ((c >> 7) & 2); 72 | Uint16 x = x1 + h; 73 | pixels[x + y * width] = ch; 74 | } 75 | } else { 76 | for(h = 7; h >= 0; --h, c >>= 1) { 77 | Uint8 ch = (c & 1) | ((c >> 7) & 2); 78 | if(ch) { 79 | Uint16 x = x1 + h; 80 | pixels[x + y * width] = ch; 81 | } 82 | } 83 | } 84 | } 85 | } else { 86 | if (flipx) flipx = 7; 87 | if (flipy) flipy = 7; 88 | if (x1 <= width-8) { 89 | for(v = 0; v < 8; v++) { 90 | Uint16 c = ram[(addr + v) & 0xffff] | (twobpp ? (ram[(addr + v + 8) & 0xffff] << 8) : 0); 91 | Uint16 y = y1 + (v ^ flipy); 92 | if (y >= height) continue; 93 | if(opaque) { 94 | for(h = 7; h >= 0; --h, c >>= 1) { 95 | Uint8 ch = (c & 1) | ((c >> 7) & 2); 96 | Uint16 x = x1 + (h ^ flipx); 97 | pixels[x + y * width] = blending[ch][color]; 98 | } 99 | } else { 100 | for(h = 7; h >= 0; --h, c >>= 1) { 101 | Uint8 ch = (c & 1) | ((c >> 7) & 2); 102 | if(ch) { 103 | Uint16 x = x1 + (h ^ flipx); 104 | pixels[x + y * width] = blending[ch][color]; 105 | } 106 | } 107 | } 108 | } 109 | } else { 110 | for(v = 0; v < 8; v++) { 111 | Uint16 c = ram[(addr + v) & 0xffff] | (twobpp ? (ram[(addr + v + 8) & 0xffff] << 8) : 0); 112 | Uint16 y = y1 + (v ^ flipy); 113 | if (y >= height) continue; 114 | if(opaque) { 115 | for(h = 7; h >= 0; --h, c >>= 1) { 116 | Uint8 ch = (c & 1) | ((c >> 7) & 2); 117 | Uint16 x = x1 + (h ^ flipx); 118 | if (x < width) pixels[x + y * width] = blending[ch][color]; 119 | } 120 | } else { 121 | for(h = 7; h >= 0; --h, c >>= 1) { 122 | Uint8 ch = (c & 1) | ((c >> 7) & 2); 123 | if(ch) { 124 | Uint16 x = x1 + (h ^ flipx); 125 | if (x < width) pixels[x + y * width] = blending[ch][color]; 126 | } 127 | } 128 | } 129 | } 130 | } 131 | } 132 | } 133 | 134 | void 135 | ctr_screen_palette(UxnCtrScreen *p, Uint8 *addr) 136 | { 137 | int i, shift; 138 | for(i = 0, shift = 4; i < 4; ++i, shift ^= 4) { 139 | Uint8 140 | r = (addr[0 + i / 2] >> shift) & 0xf, 141 | g = (addr[2 + i / 2] >> shift) & 0xf, 142 | b = (addr[4 + i / 2] >> shift) & 0xf; 143 | Uint32 color = 0x0f | r << 24 | g << 16 | b << 8; 144 | color |= color << 4; 145 | if (color != p->bg.palette[i]) { 146 | p->bg.palette[i] = color; 147 | screen_change(p, &p->bg, 0, 0, p->width, p->height); 148 | if (i > 0) { 149 | p->fg.palette[i] = color; 150 | screen_change(p, &p->fg, 0, 0, p->width, p->height); 151 | } 152 | } 153 | } 154 | p->fg.palette[0] = 0; 155 | } 156 | 157 | static void 158 | ctr_screen_clear_layer(UxnCtrScreen *p, Layer *layer) 159 | { 160 | memset(layer->pixels, 0, p->width * p->height); 161 | screen_change(p, layer, 0, 0, p->width, p->height); 162 | } 163 | 164 | void 165 | ctr_screen_free(UxnCtrScreen *p) 166 | { 167 | linearFree(p->bg.gpuPixels); 168 | linearFree(p->fg.gpuPixels); 169 | free(p->bg.pixels); 170 | free(p->fg.pixels); 171 | C3D_TexDelete(&p->bg.gpuTexture); 172 | C3D_TexDelete(&p->fg.gpuTexture); 173 | } 174 | 175 | void 176 | ctr_screen_init(UxnCtrScreen *p, int width, int height) 177 | { 178 | int 179 | pitch = next_power_of_two(width), 180 | texHeight = next_power_of_two(height); 181 | Uint8 182 | *bg = malloc(width * height), 183 | *fg = malloc(width * height); 184 | Uint32 185 | *bgPixels = linearAlloc(pitch * height * sizeof(Uint32)), 186 | *fgPixels = linearAlloc(pitch * height * sizeof(Uint32)); 187 | if(bg) p->bg.pixels = bg; 188 | if(fg) p->fg.pixels = fg; 189 | if(bgPixels) p->bg.gpuPixels = bgPixels; 190 | if(fgPixels) p->fg.gpuPixels = fgPixels; 191 | if(bg && fg && bgPixels && fgPixels) { 192 | p->width = width; 193 | p->height = height; 194 | p->pitch = pitch; 195 | ctr_screen_clear_layer(p, &p->bg); 196 | ctr_screen_clear_layer(p, &p->fg); 197 | 198 | C3D_TexInitVRAM(&p->bg.gpuTexture, p->pitch, texHeight, GPU_RGBA8); 199 | C3D_TexInitVRAM(&p->fg.gpuTexture, p->pitch, texHeight, GPU_RGBA8); 200 | 201 | p->bg.gpuImage.tex = &p->bg.gpuTexture; 202 | p->bg.gpuImage.subtex = &p->gpuSubTexture; 203 | p->fg.gpuImage.tex = &p->fg.gpuTexture; 204 | p->fg.gpuImage.subtex = &p->gpuSubTexture; 205 | 206 | p->gpuSubTexture.width = width; 207 | p->gpuSubTexture.height = height; 208 | p->gpuSubTexture.left = 0.0f; 209 | p->gpuSubTexture.top = 1.0f; 210 | p->gpuSubTexture.right = width / (float) pitch; 211 | p->gpuSubTexture.bottom = 1.0f - (height / (float) texHeight); 212 | } 213 | } 214 | 215 | static void 216 | ctr_screen_redraw_layer(UxnCtrScreen *p, Layer *layer) 217 | { 218 | Uint32 x, y, width = p->width, height = p->height, pitch_step = p->pitch - p->width, *dest = layer->gpuPixels; 219 | Uint8 *src = layer->pixels; 220 | 221 | int y1 = layer->y1; 222 | int y2 = layer->y2; 223 | // done in screen_change 224 | // if (y1 < 0) y1 = 0; 225 | // if (y2 > height) y2 = height; 226 | int yh = y2 - y1; 227 | 228 | src += y1 * p->width; 229 | dest += y1 * p->pitch; 230 | Uint32 *destStart = dest; 231 | 232 | GSPGPU_InvalidateDataCache(destStart, p->pitch * yh * 4); 233 | for(y = y1; y < y2; y++, dest += pitch_step) { 234 | for (x = 0; x < width; x++) { 235 | *(dest++) = layer->palette[*(src++)]; 236 | } 237 | } 238 | GSPGPU_FlushDataCache(destStart, p->pitch * yh * 4); 239 | 240 | int transferY = y1 & (~7); 241 | int transferHeight = ((y2 + 7) & (~7)) - transferY; 242 | 243 | C3D_SyncDisplayTransfer( 244 | (u32*) (layer->gpuPixels + (transferY * p->pitch)), GX_BUFFER_DIM(p->pitch, transferHeight), 245 | (u32*) (((Uint32*) layer->gpuTexture.data) + (transferY * p->pitch)), GX_BUFFER_DIM(p->pitch, transferHeight), 246 | (GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(1) | 247 | GX_TRANSFER_RAW_COPY(0) | GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO) | 248 | GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | 249 | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGBA8)) 250 | ); 251 | 252 | layer->y1 = height; 253 | layer->y2 = 0; 254 | } 255 | 256 | void 257 | ctr_screen_redraw(UxnCtrScreen *p) 258 | { 259 | if (p->bg.y2 > p->bg.y1) ctr_screen_redraw_layer(p, &p->bg); 260 | if (p->fg.y2 > p->fg.y1) ctr_screen_redraw_layer(p, &p->fg); 261 | } 262 | 263 | Uint8 264 | ctr_screen_dei(Uint8 *d, Uint8 addr) 265 | { 266 | switch(addr) { 267 | case 0x2: return uxn_ctr_screen.width >> 8; 268 | case 0x3: return uxn_ctr_screen.width; 269 | case 0x4: return uxn_ctr_screen.height >> 8; 270 | case 0x5: return uxn_ctr_screen.height; 271 | default: return d[addr]; 272 | } 273 | } 274 | 275 | void 276 | ctr_screen_deo(Uint8 *d, Uint8 port) 277 | { 278 | switch(port) { 279 | case 0x3: 280 | // ctr_screen_resize(&uxn_ctr_screen, PEEK2(d + 2), uxn_ctr_screen.height); 281 | ctr_screen_clear_layer(&uxn_ctr_screen, &uxn_ctr_screen.bg); 282 | ctr_screen_clear_layer(&uxn_ctr_screen, &uxn_ctr_screen.fg); 283 | break; 284 | case 0x5: 285 | // ctr_screen_resize(&uxn_ctr_screen, uxn_ctr_screen.width, PEEK2(d + 4)); 286 | ctr_screen_clear_layer(&uxn_ctr_screen, &uxn_ctr_screen.bg); 287 | ctr_screen_clear_layer(&uxn_ctr_screen, &uxn_ctr_screen.fg); 288 | break; 289 | case 0xe: { 290 | Uint8 ctrl = d[0xe]; 291 | Uint8 color = ctrl & 0x3; 292 | Uint16 x = PEEK2(d + 0x8); 293 | Uint16 y = PEEK2(d + 0xa); 294 | Layer *layer = (ctrl & 0x40) ? &uxn_ctr_screen.fg : &uxn_ctr_screen.bg; 295 | /* fill mode */ 296 | if(ctrl & 0x80) { 297 | Uint16 x2 = uxn_ctr_screen.width; 298 | Uint16 y2 = uxn_ctr_screen.height; 299 | if(ctrl & 0x10) x2 = x, x = 0; 300 | if(ctrl & 0x20) y2 = y, y = 0; 301 | screen_fill(&uxn_ctr_screen, layer->pixels, x, y, x2, y2, color); 302 | screen_change(&uxn_ctr_screen, layer, x, y, x2, y2); 303 | } 304 | /* pixel mode */ 305 | else { 306 | Uint16 width = uxn_ctr_screen.width; 307 | Uint16 height = uxn_ctr_screen.height; 308 | if(x < width && y < height) 309 | layer->pixels[x + y * width] = color; 310 | screen_change(&uxn_ctr_screen, layer, x, y, x + 1, y + 1); 311 | if(d[0x6] & 0x1) POKE2(d + 0x8, x + 1); /* auto x+1 */ 312 | if(d[0x6] & 0x2) POKE2(d + 0xa, y + 1); /* auto y+1 */ 313 | } 314 | break; 315 | } 316 | case 0xf: { 317 | Uint8 i; 318 | Uint8 ctrl = d[0xf]; 319 | Uint8 move = d[0x6]; 320 | Uint8 length = move >> 4; 321 | Uint8 twobpp = !!(ctrl & 0x80); 322 | Layer *layer = (ctrl & 0x40) ? &uxn_ctr_screen.fg : &uxn_ctr_screen.bg; 323 | Uint8 color = ctrl & 0xf; 324 | Uint16 x = PEEK2(d + 0x8), dx = (move & 0x1) << 3; 325 | Uint16 y = PEEK2(d + 0xa), dy = (move & 0x2) << 2; 326 | Uint16 addr = PEEK2(d + 0xc), addr_incr = (move & 0x4) << (1 + twobpp); 327 | int flipx = (ctrl & 0x10), fx = flipx ? -1 : 1; 328 | int flipy = (ctrl & 0x20), fy = flipy ? -1 : 1; 329 | Uint16 dyx = dy * fx, dxy = dx * fy; 330 | for(i = 0; i <= length; i++) { 331 | screen_blit(&uxn_ctr_screen, layer->pixels, x + dyx * i, y + dxy * i, u.ram.dat, addr, color, flipx, flipy, twobpp); 332 | addr += addr_incr; 333 | } 334 | screen_change(&uxn_ctr_screen, layer, x, y, x + dyx * length + 8, y + dxy * length + 8); 335 | if(move & 0x1) POKE2(d + 0x8, x + dx * fx); /* auto x+8 */ 336 | if(move & 0x2) POKE2(d + 0xa, y + dy * fy); /* auto y+8 */ 337 | if(move & 0x4) POKE2(d + 0xc, addr); /* auto addr+length */ 338 | break; 339 | } 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /source/3ds/ctr_screen.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2021 Devine Lu Linvega 3 | Copyright (c) 2021 Andrew Alderwick 4 | 5 | Permission to use, copy, modify, and distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE. 11 | */ 12 | 13 | #include <3ds.h> 14 | #include 15 | #include 16 | #include 17 | 18 | typedef struct Layer { 19 | int y1, y2; 20 | Uint32 palette[4]; 21 | Uint32 *gpuPixels; 22 | C2D_Image gpuImage; 23 | C3D_Tex gpuTexture; 24 | Uint8 *pixels, changed; 25 | } Layer; 26 | 27 | typedef struct UxnCtrScreen { 28 | int width, height, pitch; 29 | Layer fg, bg; 30 | Tex3DS_SubTexture gpuSubTexture; 31 | } UxnCtrScreen; 32 | 33 | extern UxnCtrScreen uxn_ctr_screen; 34 | 35 | void ctr_screen_palette(UxnCtrScreen *p, Uint8 *addr); 36 | void ctr_screen_free(UxnCtrScreen *p); 37 | void ctr_screen_init(UxnCtrScreen *p, int width, int height); 38 | void ctr_screen_redraw(UxnCtrScreen *p); 39 | 40 | Uint8 ctr_screen_dei(Uint8 *d, Uint8 addr); 41 | void ctr_screen_deo(Uint8 *d, Uint8 port); 42 | -------------------------------------------------------------------------------- /source/3ds/ctr_util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include <3ds.h> 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../util.h" 11 | #include "ctr_util.h" 12 | 13 | /* 14 | Copyright (c) 2018, 2023 Adrian "asie" Siekierka 15 | 16 | Permission to use, copy, modify, and distribute this software for any 17 | purpose with or without fee is hereby granted, provided that the above 18 | copyright notice and this permission notice appear in all copies. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 21 | WITH REGARD TO THIS SOFTWARE. 22 | */ 23 | 24 | bool ctr_load_t3x(C3D_Tex* tex, const char* name, ctr_texture_target_t loc) { 25 | FILE *file; 26 | file = fopen(name, "rb"); 27 | if (file == NULL) return false; 28 | Tex3DS_TextureImportStdio(file, tex, NULL, loc == TEXTURE_TARGET_VRAM); 29 | fclose(file); 30 | return true; 31 | } 32 | -------------------------------------------------------------------------------- /source/3ds/ctr_util.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include <3ds.h> 4 | #include 5 | #include 6 | #include 7 | 8 | /* 9 | Copyright (c) 2018, 2023 Adrian "asie" Siekierka 10 | 11 | Permission to use, copy, modify, and distribute this software for any 12 | purpose with or without fee is hereby granted, provided that the above 13 | copyright notice and this permission notice appear in all copies. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 | WITH REGARD TO THIS SOFTWARE. 17 | */ 18 | 19 | typedef enum { 20 | TEXTURE_TARGET_RAM, 21 | TEXTURE_TARGET_VRAM 22 | } ctr_texture_target_t; 23 | 24 | bool ctr_load_t3x(C3D_Tex* tex, const char* name, ctr_texture_target_t loc); 25 | -------------------------------------------------------------------------------- /source/3ds/emulator.c: -------------------------------------------------------------------------------- 1 | #include <3ds.h> 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "3ds/gfx.h" 9 | #include "uxn.h" 10 | #include "util.h" 11 | #include "devices/audio.h" 12 | #include "devices/datetime.h" 13 | #include "devices/file.h" 14 | #include "ctr_keyboard.h" 15 | #include "ctr_screen.h" 16 | #include "devices/system.h" 17 | #include "emulator_config.h" 18 | 19 | /* 20 | Copyright (c) 2021 Devine Lu Linvega 21 | Copyright (c) 2021 Adrian "asie" Siekierka 22 | 23 | Permission to use, copy, modify, and distribute this software for any 24 | purpose with or without fee is hereby granted, provided that the above 25 | copyright notice and this permission notice appear in all copies. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 28 | WITH REGARD TO THIS SOFTWARE. 29 | */ 30 | 31 | #define PPU_PIXELS_WIDTH 320 32 | #define PPU_PIXELS_HEIGHT 240 33 | #define AUDIO_BUFFER_SIZE 2048 34 | // #define DEBUG_CONSOLE 35 | 36 | static C3D_RenderTarget *topLeft, *topRight, *bottom; 37 | 38 | static bool soundFillBlock; 39 | static ndspWaveBuf soundBuffer[2]; 40 | static u8 *soundData; 41 | static LightLock soundLock; 42 | 43 | #define PAD 0 44 | 45 | #define REQDRAW_DISPLAY 1 46 | #define REQDRAW_ALL 1 47 | 48 | Uint8 dispswap; 49 | Uint8 reqdraw = 0; 50 | 51 | int prompt_reset(Uxn *u); 52 | 53 | int 54 | error(char *msg, const char *err) 55 | { 56 | #ifndef DEBUG_CONSOLE 57 | consoleInit(GFX_BOTTOM, NULL); 58 | #endif 59 | iprintf("Error %s: %s\n", msg, err); 60 | gfxSwapBuffers(); 61 | while (aptMainLoop()) { 62 | hidScanInput(); 63 | if (hidKeysDown() & KEY_START) break; 64 | gspWaitForVBlank(); 65 | } 66 | exit(0); 67 | return 0; 68 | } 69 | 70 | void 71 | audio_finished_handler(int instance) 72 | { 73 | 74 | } 75 | 76 | static void 77 | audio_callback(void *u) 78 | { 79 | if (soundBuffer[soundFillBlock].status == NDSP_WBUF_DONE) { 80 | int i; 81 | Sint16 *samples = (Sint16 *) soundBuffer[soundFillBlock].data_vaddr; 82 | memset(samples, 0, AUDIO_BUFFER_SIZE * 4); 83 | LightLock_Lock(&soundLock); 84 | for(i = 0; i < POLYPHONY; ++i) 85 | audio_render(i, samples, samples + (AUDIO_BUFFER_SIZE * 2)); 86 | LightLock_Unlock(&soundLock); 87 | DSP_FlushDataCache(samples, AUDIO_BUFFER_SIZE * 4); 88 | ndspChnWaveBufAdd(0, &soundBuffer[soundFillBlock]); 89 | soundFillBlock = !soundFillBlock; 90 | } 91 | } 92 | 93 | static u32 vsync_counter = 0; 94 | 95 | void 96 | redraw(Uxn *u) 97 | { 98 | C2D_DrawParams drawParams; 99 | #ifdef ENABLE_CTR_3D 100 | float slider = osGet3DSliderState(); 101 | int x_offset_bg = (int) (slider * 7.0f); 102 | int x_offset_fg = (slider > 0.0f) ? -1 : 0; 103 | #else 104 | int x_offset_bg = 0; 105 | int x_offset_fg = 0; 106 | #endif 107 | 108 | ctr_screen_redraw(&uxn_ctr_screen); 109 | 110 | u32 curr_frame = C3D_FrameCounter(0); 111 | if (curr_frame > vsync_counter) { 112 | // ticking took >1 frame's worth 113 | C3D_FrameBegin(0); 114 | } else { 115 | C3D_FrameBegin(C3D_FRAME_SYNCDRAW); 116 | } 117 | 118 | memset(&drawParams, 0, sizeof(drawParams)); 119 | drawParams.pos.w = PPU_PIXELS_WIDTH; 120 | drawParams.pos.h = PPU_PIXELS_HEIGHT; 121 | drawParams.pos.y = (240 - PPU_PIXELS_HEIGHT) >> 1; 122 | int x_bg = (400 - PPU_PIXELS_WIDTH) >> 1; 123 | 124 | if (!dispswap) { 125 | C2D_TargetClear(topLeft, C2D_Color32(0, 0, 0, 0)); 126 | C2D_SceneBegin(topLeft); 127 | drawParams.pos.x = x_bg - x_offset_bg; 128 | drawParams.depth = 0.0f; 129 | C2D_DrawImage(uxn_ctr_screen.bg.gpuImage, &drawParams, NULL); 130 | drawParams.pos.x = x_bg - x_offset_fg; 131 | drawParams.depth = 1.0f; 132 | C2D_DrawImage(uxn_ctr_screen.fg.gpuImage, &drawParams, NULL); 133 | 134 | #ifdef ENABLE_CTR_3D 135 | if (slider > 0.0f) { 136 | C2D_TargetClear(topRight, C2D_Color32(0, 0, 0, 0)); 137 | C2D_SceneBegin(topRight); 138 | drawParams.pos.x = x_bg + x_offset_bg; 139 | drawParams.depth = 0.0f; 140 | C2D_DrawImage(uxn_ctr_screen.bg.gpuImage, &drawParams, NULL); 141 | drawParams.pos.x = x_bg + x_offset_fg; 142 | drawParams.depth = 1.0f; 143 | C2D_DrawImage(uxn_ctr_screen.fg.gpuImage, &drawParams, NULL); 144 | } 145 | #endif 146 | 147 | #ifndef DEBUG_CONSOLE 148 | C2D_TargetClear(bottom, C2D_Color32(0, 0, 0, 0)); 149 | C2D_SceneBegin(bottom); 150 | 151 | #ifdef ENABLE_KEYBOARD 152 | keyboard_draw(); 153 | #endif 154 | #endif 155 | } else { 156 | #ifndef DEBUG_CONSOLE 157 | C2D_TargetClear(bottom, C2D_Color32(0, 0, 0, 0)); 158 | C2D_SceneBegin(bottom); 159 | drawParams.pos.x = 0.0f; 160 | drawParams.depth = 0.0f; 161 | C2D_DrawImage(uxn_ctr_screen.bg.gpuImage, &drawParams, NULL); 162 | drawParams.depth = 1.0f; 163 | C2D_DrawImage(uxn_ctr_screen.fg.gpuImage, &drawParams, NULL); 164 | #endif 165 | 166 | C2D_TargetClear(topLeft, C2D_Color32(0, 0, 0, 0)); 167 | C2D_SceneBegin(topLeft); 168 | } 169 | 170 | C3D_FrameEnd(0); 171 | 172 | reqdraw = 0; 173 | vsync_counter = C3D_FrameCounter(0); 174 | } 175 | 176 | void 177 | quit(void) 178 | { 179 | gspWaitForVBlank(); 180 | gspWaitForVBlank(); 181 | 182 | #ifdef ENABLE_KEYBOARD 183 | keyboard_exit(); 184 | #endif 185 | 186 | // APU 187 | ndspExit(); 188 | LightLock_Lock(&soundLock); 189 | linearFree(soundData); 190 | LightLock_Unlock(&soundLock); 191 | 192 | // PPU 193 | ctr_screen_free(&uxn_ctr_screen); 194 | C2D_Fini(); 195 | C3D_Fini(); 196 | gfxExit(); 197 | 198 | romfsExit(); 199 | exit(0); 200 | } 201 | 202 | int 203 | init(void) 204 | { 205 | osSetSpeedupEnable(1); 206 | 207 | // PPU 208 | ctr_screen_init(&uxn_ctr_screen, PPU_PIXELS_WIDTH, PPU_PIXELS_HEIGHT); 209 | gfxInitDefault(); 210 | #ifdef ENABLE_CTR_3D 211 | gfxSet3D(true); 212 | #endif 213 | C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); 214 | C2D_Init(4096); 215 | C2D_Prepare(); 216 | topLeft = C2D_CreateScreenTarget(GFX_TOP, GFX_LEFT); 217 | topRight = C2D_CreateScreenTarget(GFX_TOP, GFX_RIGHT); 218 | bottom = C2D_CreateScreenTarget(GFX_BOTTOM, GFX_LEFT); 219 | 220 | // APU 221 | float soundMix[12]; 222 | memset(soundMix, 0, sizeof(soundMix)); 223 | soundMix[0] = soundMix[1] = 1.0f; 224 | soundData = (u8*) linearAlloc(AUDIO_BUFFER_SIZE * 8); 225 | memset(soundData, 0, AUDIO_BUFFER_SIZE * 8); 226 | LightLock_Init(&soundLock); 227 | ndspInit(); 228 | ndspSetOutputMode(NDSP_OUTPUT_STEREO); 229 | ndspChnReset(0); 230 | ndspChnSetInterp(0, NDSP_INTERP_LINEAR); 231 | ndspChnSetRate(0, SAMPLE_FREQUENCY); 232 | ndspChnSetFormat(0, NDSP_CHANNELS(2) | NDSP_ENCODING(NDSP_ENCODING_PCM16)); 233 | ndspChnSetMix(0, soundMix); 234 | ndspSetOutputCount(1); 235 | ndspSetMasterVol(1.0f); 236 | ndspSetCallback(audio_callback, soundBuffer); 237 | 238 | memset(soundBuffer, 0, sizeof(soundBuffer)); 239 | soundBuffer[0].data_vaddr = &soundData[0]; 240 | soundBuffer[0].nsamples = AUDIO_BUFFER_SIZE; 241 | soundBuffer[1].data_vaddr = &soundData[AUDIO_BUFFER_SIZE * 4]; 242 | soundBuffer[1].nsamples = AUDIO_BUFFER_SIZE; 243 | 244 | DSP_FlushDataCache(soundData, AUDIO_BUFFER_SIZE * 8); 245 | 246 | ndspChnWaveBufAdd(0, &soundBuffer[0]); 247 | ndspChnWaveBufAdd(0, &soundBuffer[1]); 248 | 249 | if (R_FAILED(romfsInit())) { 250 | error("romfsInit", "Failed"); 251 | } 252 | 253 | #ifdef ENABLE_KEYBOARD 254 | if (!keyboard_init()) { 255 | error("keyboard init", "Failed"); 256 | } 257 | #endif 258 | 259 | hidSetRepeatParameters(27, 4); 260 | 261 | return 1; 262 | } 263 | 264 | static u8 ctrl_flags = 0; 265 | 266 | void 267 | doctrl(Uxn *u) 268 | { 269 | bool changed = false; 270 | u8 old_flags = ctrl_flags; 271 | #ifdef ENABLE_KEYBOARD 272 | int key = dispswap ? -1 : keyboard_update(); 273 | #else 274 | int key = -1; 275 | #endif 276 | 277 | int pressed = hidKeysDown(); 278 | int held = (pressed | hidKeysHeld()) & ~(hidKeysDownRepeat() & ~pressed); 279 | 280 | #ifdef ENABLE_TOUCH 281 | if (pressed & (KEY_L | KEY_R)) { 282 | dispswap ^= 1; 283 | } 284 | #endif 285 | 286 | ctrl_flags = (held & 0x0F) 287 | #ifdef ENABLE_KEYBOARD 288 | | (keyboard_is_held(K_CTRL) ? 0x01 : 0) 289 | | (keyboard_is_held(K_ALT) ? 0x02 : 0) 290 | | (keyboard_is_held(K_SHIFT) ? 0x04 : 0) 291 | | ((key == K_HOME) ? 0x08 : 0) 292 | #endif 293 | | ((held & KEY_UP) ? 0x10 : 0) 294 | | ((held & KEY_DOWN) ? 0x20 : 0) 295 | | ((held & KEY_RIGHT) ? 0x80 : 0) 296 | | ((held & KEY_LEFT) ? 0x40 : 0); 297 | 298 | if (key > 0 && key < 128) { 299 | u->dev[0x83] = key; 300 | changed = true; 301 | } 302 | if (old_flags != ctrl_flags) { 303 | changed = true; 304 | } 305 | if (changed) { 306 | // clear only changed bits 307 | u->dev[0x82] = (u->dev[0x82] & ~(old_flags & (~ctrl_flags))) | (ctrl_flags & (~old_flags)); 308 | uxn_eval(u, GETVEC(u->dev + 0x80)); 309 | if (key > 0 && key < 128) { 310 | u->dev[0x83] = 0; 311 | } 312 | } 313 | 314 | if (key == K_SYSTEM) { 315 | prompt_reset(u); 316 | } 317 | } 318 | 319 | static bool istouching = false; 320 | 321 | void 322 | domouse(Uxn *u) 323 | { 324 | bool changed = false; 325 | 326 | if (dispswap && (hidKeysHeld() & KEY_TOUCH)) { 327 | if (!istouching) { 328 | u->dev[0x96] = 0x01; 329 | istouching = true; 330 | changed = true; 331 | } 332 | 333 | touchPosition tpos; 334 | hidTouchRead(&tpos); 335 | if (peek16(u->dev + 0x90, 0x2) != tpos.px 336 | || peek16(u->dev + 0x90, 0x4) != tpos.py) 337 | { 338 | poke16(u->dev + 0x90, 0x2, tpos.px); 339 | poke16(u->dev + 0x90, 0x4, tpos.py); 340 | changed = true; 341 | } 342 | } else if (istouching) { 343 | u->dev[0x96] = 0x00; 344 | istouching = false; 345 | changed = true; 346 | } 347 | 348 | if (changed) { 349 | uxn_eval(u, GETVEC(u->dev + 0x90)); 350 | } 351 | } 352 | 353 | Uxn u; 354 | 355 | #pragma mark - Devices 356 | 357 | static Uint8 358 | audio_dei(int instance, Uint8 *d, Uint8 port) 359 | { 360 | switch(port) { 361 | case 0x4: return audio_get_vu(instance); 362 | case 0x2: POKE2(d + 0x2, audio_get_position(instance)); /* fall through */ 363 | default: return d[port]; 364 | } 365 | } 366 | 367 | static void 368 | audio_deo(int instance, Uint8 *d, Uint8 port) 369 | { 370 | if(port == 0xf) { 371 | audio_start(instance, d, &u); 372 | } 373 | } 374 | 375 | static Uint8 audio0_dei(Uint8 *d, Uint8 port) { return audio_dei(0, d, port); } 376 | static Uint8 audio1_dei(Uint8 *d, Uint8 port) { return audio_dei(1, d, port); } 377 | static Uint8 audio2_dei(Uint8 *d, Uint8 port) { return audio_dei(2, d, port); } 378 | static Uint8 audio3_dei(Uint8 *d, Uint8 port) { return audio_dei(3, d, port); } 379 | static void audio0_deo(Uint8 *d, Uint8 port) { audio_deo(0, d, port); } 380 | static void audio1_deo(Uint8 *d, Uint8 port) { audio_deo(1, d, port); } 381 | static void audio2_deo(Uint8 *d, Uint8 port) { audio_deo(2, d, port); } 382 | static void audio3_deo(Uint8 *d, Uint8 port) { audio_deo(3, d, port); } 383 | static void file0_deo(Uint8 *d, Uint8 port) { file_deo(&u, 0xa0+port); } 384 | static void file1_deo(Uint8 *d, Uint8 port) { file_deo(&u, 0xb0+port); } 385 | 386 | static Uint8 ctr_system_dei(Uint8 *d, Uint8 port) { return system_dei(&u, port); } 387 | static void 388 | ctr_system_deo(Uint8 *d, Uint8 port) 389 | { 390 | system_deo(&u, d, port); 391 | if(port > 0x7 && port < 0xe) 392 | ctr_screen_palette(&uxn_ctr_screen, &u.dev[0x8]); 393 | } 394 | 395 | static int 396 | uxn_load_boot(Uxn *u) 397 | { 398 | if(system_load(u, "romfs:/boot.rom")) { 399 | chdir("romfs:/"); 400 | return 1; 401 | } 402 | if(system_load(u, "boot.rom")) { 403 | return 1; 404 | } 405 | if(system_load(u, "./uxn/boot.rom")) { 406 | chdir("./uxn"); 407 | return 1; 408 | } 409 | if(system_load(u, "./uxn/launcher.rom")) { 410 | chdir("./uxn"); 411 | return 1; 412 | } 413 | if(system_load(u, "/uxn/boot.rom")) { 414 | chdir("/uxn"); 415 | return 1; 416 | } 417 | if(system_load(u, "/uxn/launcher.rom")) { 418 | chdir("/uxn"); 419 | return 1; 420 | } 421 | return 0; 422 | } 423 | 424 | int 425 | prompt_reset(Uxn *u) 426 | { 427 | #ifndef DEBUG_CONSOLE 428 | consoleInit(GFX_BOTTOM, NULL); 429 | #endif 430 | consoleClear(); 431 | iprintf("\n\n\n\n\n\n\n\n\n\n\n\n\n Would you like to reset?\n\n [A] - Yes\n [B] - No\n"); 432 | gfxSwapBuffers(); 433 | while(aptMainLoop()) { 434 | gspWaitForVBlank(); 435 | hidScanInput(); 436 | int allHeld = hidKeysDown() | hidKeysHeld(); 437 | if (allHeld & KEY_A) { 438 | consoleClear(); 439 | break; 440 | } else if (allHeld & KEY_B) { 441 | consoleClear(); 442 | goto restoreGfx; 443 | } 444 | } 445 | 446 | iprintf("Resetting...\n"); 447 | 448 | if(!resetuxn()) 449 | return error("Resetting", "Failed"); 450 | if(!uxn_load_boot(u)) 451 | return error("Load", "Failed"); 452 | ctr_screen_free(&uxn_ctr_screen); 453 | ctr_screen_init(&uxn_ctr_screen, PPU_PIXELS_WIDTH, PPU_PIXELS_HEIGHT); 454 | #ifdef ENABLE_KEYBOARD 455 | keyboard_clear(); 456 | #endif 457 | while(aptMainLoop()) { 458 | gspWaitForVBlank(); 459 | hidScanInput(); 460 | if ((hidKeysDown() | hidKeysHeld()) == 0) break; 461 | } 462 | ctrl_flags = 0; 463 | uxn_eval(u, 0x0100); 464 | 465 | restoreGfx: 466 | #ifndef DEBUG_CONSOLE 467 | gfxInitDefault(); 468 | #ifdef ENABLE_CTR_3D 469 | gfxSet3D(true); 470 | #endif 471 | #endif 472 | return 0; 473 | } 474 | 475 | int 476 | start(Uxn *u) 477 | { 478 | uxn_eval(u, 0x0100); 479 | redraw(u); 480 | while(aptMainLoop()) { 481 | hidScanInput(); 482 | if ((hidKeysHeld() & 483 | (KEY_L | KEY_R | KEY_START | KEY_SELECT)) 484 | == (KEY_L | KEY_R | KEY_START | KEY_SELECT)) { 485 | break; 486 | } 487 | doctrl(u); 488 | domouse(u); 489 | uxn_eval(u, GETVEC(u->dev + 0x20)); 490 | redraw(u); 491 | } 492 | return 1; 493 | } 494 | 495 | int 496 | main(int argc, char **argv) 497 | { 498 | if(!init()) 499 | return error("Init", "Failed"); 500 | 501 | #ifdef USE_BOTTOM_SCREEN_DEFAULT 502 | dispswap = 1; 503 | #else 504 | dispswap = 0; 505 | #endif 506 | 507 | #ifdef DEBUG_CONSOLE 508 | consoleInit(GFX_BOTTOM, NULL); 509 | iprintf("uxn3ds\n"); 510 | #endif 511 | 512 | uxn_register_device(0x0, ctr_system_dei, ctr_system_deo); 513 | uxn_register_device(0x1, NULL, console_deo); 514 | uxn_register_device(0x2, ctr_screen_dei, ctr_screen_deo); 515 | uxn_register_device(0x3, audio0_dei, audio0_deo); 516 | uxn_register_device(0x4, audio1_dei, audio1_deo); 517 | uxn_register_device(0x5, audio2_dei, audio2_deo); 518 | uxn_register_device(0x6, audio3_dei, audio3_deo); 519 | uxn_register_device(0xa, NULL, file0_deo); 520 | uxn_register_device(0xb, NULL, file1_deo); 521 | uxn_register_device(0xc, datetime_dei, NULL); 522 | 523 | if(!uxn_boot()) 524 | return error("Boot", "Failed"); 525 | if(!uxn_load_boot(&u)) { 526 | dprintf("Halted: Missing input rom.\n"); 527 | return error("Load", "Failed"); 528 | } 529 | 530 | start(&u); 531 | quit(); 532 | return 0; 533 | } 534 | -------------------------------------------------------------------------------- /source/devices/audio.c: -------------------------------------------------------------------------------- 1 | #include "../uxn.h" 2 | #include "audio.h" 3 | 4 | /* 5 | Copyright (c) 2021-2023 Devine Lu Linvega, Andrew Alderwick 6 | 7 | Permission to use, copy, modify, and distribute this software for any 8 | purpose with or without fee is hereby granted, provided that the above 9 | copyright notice and this permission notice appear in all copies. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | WITH REGARD TO THIS SOFTWARE. 13 | */ 14 | 15 | #define NOTE_PERIOD (SAMPLE_FREQUENCY * 0x4000 / 11025) 16 | #define ADSR_STEP (SAMPLE_FREQUENCY / 0xf) 17 | 18 | typedef struct { 19 | Uint8 *addr; 20 | Uint32 count, advance, period, age, a, d, s, r; 21 | Uint16 i, len; 22 | Sint8 volume[2]; 23 | Uint8 pitch, repeat; 24 | } UxnAudio; 25 | 26 | /* clang-format off */ 27 | 28 | static Uint32 advances[12] = { 29 | 0x80000, 0x879c8, 0x8facd, 0x9837f, 0xa1451, 0xaadc1, 30 | 0xb504f, 0xbfc88, 0xcb2ff, 0xd7450, 0xe411f, 0xf1a1c 31 | }; 32 | 33 | static double detunes[256] = { 34 | 1.0000000000000000, 1.0002256593050698, 1.0004513695322617, 35 | 1.0006771306930664, 1.0009029427989777, 1.0011288058614922, 36 | 1.0013547198921082, 1.0015806849023274, 1.0018067009036538, 37 | 1.002032767907594 , 1.0022588859256572, 1.0024850549693551, 38 | 1.0027112750502025, 1.0029375461797159, 1.0031638683694153, 39 | 1.0033902416308227, 1.0036166659754628, 1.0038431414148634, 40 | 1.004069667960554 , 1.0042962456240678, 1.0045228744169397, 41 | 1.0047495543507072, 1.004976285436911 , 1.0052030676870944, 42 | 1.0054299011128027, 1.0056567857255843, 1.0058837215369900, 43 | 1.006110708558573 , 1.0063377468018897, 1.0065648362784985, 44 | 1.0067919769999607, 1.0070191689778405, 1.007246412223704 , 45 | 1.0074737067491204, 1.0077010525656616, 1.0079284496849015, 46 | 1.0081558981184175, 1.008383397877789 , 1.008610948974598 , 47 | 1.0088385514204294, 1.0090662052268706, 1.0092939104055114, 48 | 1.0095216669679448, 1.0097494749257656, 1.009977334290572 , 49 | 1.0102052450739643, 1.0104332072875455, 1.0106612209429215, 50 | 1.0108892860517005, 1.0111174026254934, 1.0113455706759138, 51 | 1.011573790214578 , 1.0118020612531047, 1.0120303838031153, 52 | 1.0122587578762337, 1.012487183484087 , 1.012715660638304 , 53 | 1.0129441893505169, 1.0131727696323602, 1.0134014014954713, 54 | 1.0136300849514894, 1.0138588200120575, 1.0140876066888203, 55 | 1.0143164449934257, 1.0145453349375237, 1.0147742765327674, 56 | 1.0150032697908125, 1.015232314723317 , 1.015461411341942 , 57 | 1.0156905596583505, 1.0159197596842091, 1.0161490114311862, 58 | 1.016378314910953 , 1.0166076701351838, 1.0168370771155553, 59 | 1.0170665358637463, 1.0172960463914391, 1.017525608710318 , 60 | 1.0177552228320703, 1.0179848887683858, 1.0182146065309567, 61 | 1.0184443761314785, 1.0186741975816487, 1.0189040708931674, 62 | 1.019133996077738 , 1.0193639731470658, 1.0195940021128593, 63 | 1.0198240829868295, 1.0200542157806898, 1.0202844005061564, 64 | 1.0205146371749483, 1.0207449257987866, 1.0209752663893958, 65 | 1.0212056589585028, 1.0214361035178368, 1.0216666000791297, 66 | 1.0218971486541166, 1.0221277492545349, 1.0223584018921241, 67 | 1.0225891065786274, 1.02281986332579 , 1.0230506721453596, 68 | 1.023281533049087 , 1.0235124460487257, 1.0237434111560313, 69 | 1.0239744283827625, 1.0242054977406807, 1.0244366192415495, 70 | 1.0246677928971357, 1.0248990187192082, 1.025130296719539 , 71 | 1.0253616269099028, 1.0255930093020766, 1.0258244439078401, 72 | 1.026055930738976 , 1.0262874698072693, 1.0265190611245079, 73 | 1.0267507047024822, 1.0269824005529853, 1.027214148687813 , 74 | 1.0274459491187637, 1.0276778018576387, 1.0279097069162415, 75 | 1.0281416643063788, 1.0283736740398595, 1.0286057361284953, 76 | 1.028837850584101 , 1.0290700174184932, 1.029302236643492 , 77 | 1.0295345082709197, 1.0297668323126017, 1.029999208780365 , 78 | 1.030231637686041 , 1.030464119041462 , 1.0306966528584645, 79 | 1.0309292391488862, 1.0311618779245688, 1.0313945691973556, 80 | 1.0316273129790936, 1.0318601092816313, 1.0320929581168212, 81 | 1.0323258594965172, 1.0325588134325767, 1.0327918199368598, 82 | 1.0330248790212284, 1.033257990697548 , 1.0334911549776868, 83 | 1.033724371873515 , 1.0339576413969056, 1.0341909635597348, 84 | 1.0344243383738811, 1.034657765851226 , 1.034891246003653 , 85 | 1.0351247788430489, 1.0353583643813031, 1.0355920026303078, 86 | 1.0358256936019572, 1.0360594373081489, 1.0362932337607829, 87 | 1.0365270829717617, 1.0367609849529913, 1.0369949397163791, 88 | 1.0372289472738365, 1.0374630076372766, 1.0376971208186156, 89 | 1.0379312868297725, 1.0381655056826686, 1.0383997773892284, 90 | 1.0386341019613787, 1.0388684794110492, 1.039102909750172 , 91 | 1.0393373929906822, 1.0395719291445176, 1.0398065182236185, 92 | 1.0400411602399278, 1.0402758552053915, 1.0405106031319582, 93 | 1.0407454040315787, 1.040980257916207 , 1.0412151647977996, 94 | 1.041450124688316 , 1.0416851375997183, 1.0419202035439705, 95 | 1.0421553225330404, 1.042390494578898 , 1.042625719693516 , 96 | 1.0428609978888699, 1.043096329176938 , 1.043331713569701 , 97 | 1.0435671510791424, 1.0438026417172486, 1.0440381854960086, 98 | 1.0442737824274138, 1.044509432523459 , 1.044745135796141 , 99 | 1.04498089225746 , 1.045216701919418 , 1.0454525647940205, 100 | 1.0456884808932754, 1.0459244502291931, 1.0461604728137874, 101 | 1.046396548659074 , 1.046632677777072 , 1.0468688601798024, 102 | 1.0471050958792898, 1.047341384887561 , 1.0475777272166455, 103 | 1.047814122878576 , 1.048050571885387 , 1.0482870742491166, 104 | 1.0485236299818055, 1.0487602390954964, 1.0489969016022356, 105 | 1.0492336175140715, 1.0494703868430555, 1.0497072096012419, 106 | 1.0499440858006872, 1.0501810154534512, 1.050417998571596 , 107 | 1.0506550351671864, 1.0508921252522903, 1.0511292688389782, 108 | 1.051366465939323 , 1.0516037165654004, 1.0518410207292894, 109 | 1.052078378443071 , 1.0523157897188296, 1.0525532545686513, 110 | 1.0527907730046264, 1.0530283450388465, 1.0532659706834067, 111 | 1.053503649950405 , 1.053741382851941 , 1.0539791694001188, 112 | 1.0542170096070436, 1.0544549034848243, 1.0546928510455722, 113 | 1.0549308523014012, 1.0551689072644284, 1.0554070159467728, 114 | 1.0556451783605572, 1.0558833945179062, 1.056121664430948 , 115 | 1.0563599881118126, 1.0565983655726334, 1.0568367968255465, 116 | 1.0570752818826903, 1.0573138207562065, 1.057552413458239 , 117 | 1.0577910600009348, 1.0580297603964437, 1.058268514656918 , 118 | 1.0585073227945128, 1.0587461848213857, 1.058985100749698 , 119 | 1.0592240705916123, 120 | }; 121 | 122 | static UxnAudio uxn_audio[POLYPHONY]; 123 | 124 | /* clang-format on */ 125 | 126 | static Sint32 127 | envelope(UxnAudio *c, Uint32 age) 128 | { 129 | if(!c->r) return 0x0888; 130 | if(age < c->a) return 0x0888 * age / c->a; 131 | if(age < c->d) return 0x0444 * (2 * c->d - c->a - age) / (c->d - c->a); 132 | if(age < c->s) return 0x0444; 133 | if(age < c->r) return 0x0444 * (c->r - age) / (c->r - c->s); 134 | c->advance = 0; 135 | return 0x0000; 136 | } 137 | 138 | int 139 | audio_render(int instance, Sint16 *sample, Sint16 *end) 140 | { 141 | UxnAudio *c = &uxn_audio[instance]; 142 | Sint32 s; 143 | if(!c->advance || !c->period) return 0; 144 | while(sample < end) { 145 | c->count += c->advance; 146 | c->i += c->count / c->period; 147 | c->count %= c->period; 148 | if(c->i >= c->len) { 149 | if(!c->repeat) { 150 | c->advance = 0; 151 | break; 152 | } 153 | c->i %= c->len; 154 | } 155 | s = (Sint8)(c->addr[c->i] + 0x80) * envelope(c, c->age++); 156 | *sample++ += s * c->volume[0] / 0x180; 157 | *sample++ += s * c->volume[1] / 0x180; 158 | } 159 | if(!c->advance) audio_finished_handler(instance); 160 | return 1; 161 | } 162 | 163 | void 164 | audio_start(int instance, Uint8 *d, Uxn *u) 165 | { 166 | UxnAudio *c = &uxn_audio[instance]; 167 | Uint8 pitch = d[0xf] & 0x7f; 168 | Uint8 detune = d[0x5]; 169 | Uint16 addr = PEEK2(d + 0xc), adsr = PEEK2(d + 0x8); 170 | c->len = PEEK2(d + 0xa); 171 | if(c->len > 0x10000 - addr) 172 | c->len = 0x10000 - addr; 173 | c->addr = &u->ram.dat[addr]; 174 | c->volume[0] = d[0xe] >> 4; 175 | c->volume[1] = d[0xe] & 0xf; 176 | c->repeat = !(d[0xf] & 0x80); 177 | if(pitch < 108 && c->len) 178 | c->advance = (Uint32)((double)(advances[pitch % 12]) * detunes[detune]) >> (8 - pitch / 12); 179 | else { 180 | c->advance = 0; 181 | return; 182 | } 183 | c->a = ADSR_STEP * (adsr >> 12); 184 | c->d = ADSR_STEP * (adsr >> 8 & 0xf) + c->a; 185 | c->s = ADSR_STEP * (adsr >> 4 & 0xf) + c->d; 186 | c->r = ADSR_STEP * (adsr >> 0 & 0xf) + c->s; 187 | c->age = 0; 188 | c->i = 0; 189 | if(c->len <= 0x100) /* single cycle mode */ 190 | c->period = NOTE_PERIOD * 337 / 2 / c->len; 191 | else /* sample repeat mode */ 192 | c->period = NOTE_PERIOD; 193 | } 194 | 195 | Uint8 196 | audio_get_vu(int instance) 197 | { 198 | UxnAudio *c = &uxn_audio[instance]; 199 | int i; 200 | Sint32 sum[2] = {0, 0}; 201 | if(!c->advance || !c->period) return 0; 202 | for(i = 0; i < 2; i++) { 203 | if(!c->volume[i]) continue; 204 | sum[i] = 1 + envelope(c, c->age) * c->volume[i] / 0x800; 205 | if(sum[i] > 0xf) sum[i] = 0xf; 206 | } 207 | return (sum[0] << 4) | sum[1]; 208 | } 209 | 210 | Uint16 211 | audio_get_position(int instance) 212 | { 213 | UxnAudio *c = &uxn_audio[instance]; 214 | return c->i; 215 | } 216 | -------------------------------------------------------------------------------- /source/devices/audio.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick 3 | 4 | Permission to use, copy, modify, and distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE. 10 | */ 11 | 12 | typedef signed int Sint32; 13 | 14 | #define AUDIO_VERSION 1 15 | #define AUDIO_DEIMASK 0x0014 16 | #define AUDIO_DEOMASK 0x8000 17 | 18 | #define SAMPLE_FREQUENCY 44100 19 | #define POLYPHONY 4 20 | 21 | Uint8 audio_get_vu(int instance); 22 | Uint16 audio_get_position(int instance); 23 | int audio_render(int instance, Sint16 *sample, Sint16 *end); 24 | void audio_start(int instance, Uint8 *d, Uxn *u); 25 | void audio_finished_handler(int instance); 26 | -------------------------------------------------------------------------------- /source/devices/controller.c: -------------------------------------------------------------------------------- 1 | #include "../uxn.h" 2 | #include "controller.h" 3 | 4 | /* 5 | Copyright (c) 2021-2023 Devine Lu Linvega, Andrew Alderwick 6 | 7 | Permission to use, copy, modify, and distribute this software for any 8 | purpose with or without fee is hereby granted, provided that the above 9 | copyright notice and this permission notice appear in all copies. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | WITH REGARD TO THIS SOFTWARE. 13 | */ 14 | 15 | void 16 | controller_down(Uxn *u, Uint8 *d, Uint8 mask) 17 | { 18 | if(mask) { 19 | d[2] |= mask; 20 | uxn_eval(u, PEEK2(d)); 21 | } 22 | } 23 | 24 | void 25 | controller_up(Uxn *u, Uint8 *d, Uint8 mask) 26 | { 27 | if(mask) { 28 | d[2] &= (~mask); 29 | uxn_eval(u, PEEK2(d)); 30 | } 31 | } 32 | 33 | void 34 | controller_key(Uxn *u, Uint8 *d, Uint8 key) 35 | { 36 | if(key) { 37 | d[3] = key; 38 | uxn_eval(u, PEEK2(d)); 39 | d[3] = 0x00; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /source/devices/controller.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick 3 | 4 | Permission to use, copy, modify, and distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE. 10 | */ 11 | 12 | void controller_down(Uxn *u, Uint8 *d, Uint8 mask); 13 | void controller_up(Uxn *u, Uint8 *d, Uint8 mask); 14 | void controller_key(Uxn *u, Uint8 *d, Uint8 key); 15 | -------------------------------------------------------------------------------- /source/devices/datetime.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../uxn.h" 4 | #include "datetime.h" 5 | 6 | /* 7 | Copyright (c) 2021-2023 Devine Lu Linvega, Andrew Alderwick 8 | 9 | Permission to use, copy, modify, and distribute this software for any 10 | purpose with or without fee is hereby granted, provided that the above 11 | copyright notice and this permission notice appear in all copies. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 | WITH REGARD TO THIS SOFTWARE. 15 | */ 16 | 17 | Uint8 18 | datetime_dei(Uint8 *d, Uint8 addr) 19 | { 20 | time_t seconds = time(NULL); 21 | struct tm zt = {0}; 22 | struct tm *t = localtime(&seconds); 23 | if(t == NULL) 24 | t = &zt; 25 | switch(addr) { 26 | case 0x0: return (t->tm_year + 1900) >> 8; 27 | case 0x1: return (t->tm_year + 1900); 28 | case 0x2: return t->tm_mon; 29 | case 0x3: return t->tm_mday; 30 | case 0x4: return t->tm_hour; 31 | case 0x5: return t->tm_min; 32 | case 0x6: return t->tm_sec; 33 | case 0x7: return t->tm_wday; 34 | case 0x8: return t->tm_yday >> 8; 35 | case 0x9: return t->tm_yday; 36 | case 0xa: return t->tm_isdst; 37 | default: return d[addr]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /source/devices/datetime.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick 3 | 4 | Permission to use, copy, modify, and distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE. 10 | */ 11 | 12 | Uint8 datetime_dei(Uint8 *d, Uint8 addr); 13 | -------------------------------------------------------------------------------- /source/devices/file.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef _WIN32 11 | #include 12 | #include 13 | #define realpath(s, dummy) lrealpath(s) 14 | #define DIR_SEP_CHAR '\\' 15 | #define DIR_SEP_STR "\\" 16 | #define pathcmp(path1, path2, length) strncasecmp(path1, path2, length) /* strncasecmp provided by libiberty */ 17 | #define notdriveroot(file_name) (file_name[0] != DIR_SEP_CHAR && ((strlen(file_name) > 2 && file_name[1] != ':') || strlen(file_name) <= 2)) 18 | #define mkdir(file_name) (_mkdir(file_name) == 0) 19 | #else 20 | #define DIR_SEP_CHAR '/' 21 | #define DIR_SEP_STR "/" 22 | #define pathcmp(path1, path2, length) strncmp(path1, path2, length) 23 | #define notdriveroot(file_name) (file_name[0] != DIR_SEP_CHAR) 24 | #define mkdir(file_name) (mkdir(file_name, 0755) == 0) 25 | #endif 26 | 27 | #ifndef PATH_MAX 28 | #define PATH_MAX 4096 29 | #endif 30 | 31 | #include "../uxn.h" 32 | #include "file.h" 33 | 34 | /* 35 | Copyright (c) 2021-2023 Devine Lu Linvega, Andrew Alderwick 36 | 37 | Permission to use, copy, modify, and distribute this software for any 38 | purpose with or without fee is hereby granted, provided that the above 39 | copyright notice and this permission notice appear in all copies. 40 | 41 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 42 | WITH REGARD TO THIS SOFTWARE. 43 | */ 44 | 45 | typedef struct { 46 | FILE *f; 47 | DIR *dir; 48 | char current_filename[4096]; 49 | struct dirent *de; 50 | enum { IDLE, 51 | FILE_READ, 52 | FILE_WRITE, 53 | DIR_READ, 54 | DIR_WRITE 55 | } state; 56 | int outside_sandbox; 57 | } UxnFile; 58 | 59 | static UxnFile uxn_file[POLYFILEY]; 60 | 61 | static void 62 | reset(UxnFile *c) 63 | { 64 | if(c->f != NULL) { 65 | fclose(c->f); 66 | c->f = NULL; 67 | } 68 | if(c->dir != NULL) { 69 | closedir(c->dir); 70 | c->dir = NULL; 71 | } 72 | c->de = NULL; 73 | c->state = IDLE; 74 | c->outside_sandbox = 0; 75 | } 76 | 77 | static Uint16 78 | get_entry(char *p, Uint16 len, const char *pathname, const char *basename, int fail_nonzero) 79 | { 80 | struct stat st; 81 | if(len < strlen(basename) + 8) 82 | return 0; 83 | if(stat(pathname, &st)) 84 | return fail_nonzero ? sniprintf(p, len, "!!!! %s\n", basename) : 0; 85 | else if(S_ISDIR(st.st_mode)) 86 | return sniprintf(p, len, "---- %s/\n", basename); 87 | else if(st.st_size < 0x10000) 88 | return sniprintf(p, len, "%04x %s\n", (unsigned int)st.st_size, basename); 89 | else 90 | return sniprintf(p, len, "???? %s\n", basename); 91 | } 92 | 93 | static Uint16 94 | file_read_dir(UxnFile *c, char *dest, Uint16 len) 95 | { 96 | static char pathname[4352]; 97 | char *p = dest; 98 | if(c->de == NULL) c->de = readdir(c->dir); 99 | for(; c->de != NULL; c->de = readdir(c->dir)) { 100 | Uint16 n; 101 | if(c->de->d_name[0] == '.' && c->de->d_name[1] == '\0') 102 | continue; 103 | if(strcmp(c->de->d_name, "..") == 0) { 104 | /* hide "sandbox/.." */ 105 | char cwd[PATH_MAX] = {'\0'}, *t; 106 | /* Note there's [currently] no way of chdir()ing from uxn, so $PWD 107 | * is always the sandbox top level. */ 108 | getcwd(cwd, sizeof(cwd)); 109 | /* We already checked that c->current_filename exists so don't need a wrapper. */ 110 | t = realpath(c->current_filename, NULL); 111 | if(strcmp(cwd, t) == 0) { 112 | free(t); 113 | continue; 114 | } 115 | free(t); 116 | } 117 | if(strlen(c->current_filename) + 1 + strlen(c->de->d_name) < sizeof(pathname)) 118 | sniprintf(pathname, sizeof(pathname), "%s/%s", c->current_filename, c->de->d_name); 119 | else 120 | pathname[0] = '\0'; 121 | n = get_entry(p, len, pathname, c->de->d_name, 1); 122 | if(!n) break; 123 | p += n; 124 | len -= n; 125 | } 126 | return p - dest; 127 | } 128 | 129 | static char * 130 | retry_realpath(const char *file_name) 131 | { 132 | char *r, p[PATH_MAX] = {'\0'}, *x; 133 | int fnlen; 134 | if(file_name == NULL) { 135 | errno = EINVAL; 136 | return NULL; 137 | } else if((fnlen = strlen(file_name)) >= PATH_MAX) { 138 | errno = ENAMETOOLONG; 139 | return NULL; 140 | } 141 | if(notdriveroot(file_name)) { 142 | /* TODO: use a macro instead of '/' for absolute path first character so that other systems can work */ 143 | /* if a relative path, prepend cwd */ 144 | getcwd(p, sizeof(p)); 145 | if(strlen(p) + strlen(DIR_SEP_STR) + fnlen >= PATH_MAX) { 146 | errno = ENAMETOOLONG; 147 | return NULL; 148 | } 149 | strcat(p, DIR_SEP_STR); /* TODO: use a macro instead of '/' for the path delimiter */ 150 | } 151 | strcat(p, file_name); 152 | while((r = realpath(p, NULL)) == NULL) { 153 | if(errno != ENOENT) 154 | return NULL; 155 | x = strrchr(p, DIR_SEP_CHAR); /* TODO: path delimiter macro */ 156 | if(x) 157 | *x = '\0'; 158 | else 159 | return NULL; 160 | } 161 | return r; 162 | } 163 | 164 | static void 165 | file_check_sandbox(UxnFile *c) 166 | { 167 | char *x, *rp, cwd[PATH_MAX] = {'\0'}; 168 | x = getcwd(cwd, sizeof(cwd)); 169 | rp = retry_realpath(c->current_filename); 170 | if(rp == NULL || (x && pathcmp(cwd, rp, strlen(cwd)) != 0)) { 171 | c->outside_sandbox = 1; 172 | fiprintf(stderr, "file warning: blocked attempt to access %s outside of sandbox\n", c->current_filename); 173 | } 174 | free(rp); 175 | } 176 | 177 | static Uint16 178 | file_init(UxnFile *c, char *filename, size_t max_len, int override_sandbox) 179 | { 180 | char *p = c->current_filename; 181 | size_t len = sizeof(c->current_filename); 182 | reset(c); 183 | if(len > max_len) len = max_len; 184 | while(len) { 185 | if((*p++ = *filename++) == '\0') { 186 | if(!override_sandbox) /* override sandbox for loading roms */ 187 | file_check_sandbox(c); 188 | return 0; 189 | } 190 | len--; 191 | } 192 | c->current_filename[0] = '\0'; 193 | return 0; 194 | } 195 | 196 | static Uint16 197 | file_read(UxnFile *c, void *dest, int len) 198 | { 199 | if(c->outside_sandbox) return 0; 200 | if(c->state != FILE_READ && c->state != DIR_READ) { 201 | reset(c); 202 | if((c->dir = opendir(c->current_filename)) != NULL) 203 | c->state = DIR_READ; 204 | else if((c->f = fopen(c->current_filename, "rb")) != NULL) 205 | c->state = FILE_READ; 206 | } 207 | if(c->state == FILE_READ) 208 | return fread(dest, 1, len, c->f); 209 | if(c->state == DIR_READ) 210 | return file_read_dir(c, dest, len); 211 | return 0; 212 | } 213 | 214 | static int 215 | is_dir_path(char *p) 216 | { 217 | char c; 218 | int saw_slash = 0; 219 | while((c = *p++)) 220 | saw_slash = c == DIR_SEP_CHAR; 221 | return saw_slash; 222 | } 223 | 224 | int 225 | dir_exists(char *p) 226 | { 227 | struct stat st; 228 | return stat(p, &st) == 0 && S_ISDIR(st.st_mode); 229 | } 230 | 231 | int 232 | ensure_parent_dirs(char *p) 233 | { 234 | int ok = 1; 235 | char c, *s = p; 236 | for(; ok && (c = *p); p++) { 237 | if(c == DIR_SEP_CHAR) { 238 | *p = '\0'; 239 | ok = dir_exists(s) || mkdir(s); 240 | *p = c; 241 | } 242 | } 243 | return ok; 244 | } 245 | 246 | static Uint16 247 | file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags) 248 | { 249 | Uint16 ret = 0; 250 | if(c->outside_sandbox) return 0; 251 | ensure_parent_dirs(c->current_filename); 252 | if(c->state != FILE_WRITE && c->state != DIR_WRITE) { 253 | reset(c); 254 | if(is_dir_path(c->current_filename)) 255 | c->state = DIR_WRITE; 256 | else if((c->f = fopen(c->current_filename, (flags & 0x01) ? "ab" : "wb")) != NULL) 257 | c->state = FILE_WRITE; 258 | } 259 | if(c->state == FILE_WRITE) { 260 | if((ret = fwrite(src, 1, len, c->f)) > 0 && fflush(c->f) != 0) 261 | ret = 0; 262 | } 263 | if(c->state == DIR_WRITE) { 264 | ret = dir_exists(c->current_filename); 265 | } 266 | return ret; 267 | } 268 | 269 | static Uint16 270 | stat_fill(Uint8 *dest, Uint16 len, char c) 271 | { 272 | Uint16 i; 273 | for(i = 0; i < len; i++) 274 | *(dest++) = c; 275 | return len; 276 | } 277 | 278 | static Uint16 279 | stat_size(Uint8 *dest, Uint16 len, off_t size) 280 | { 281 | Uint16 i; 282 | dest += len - 1; 283 | for(i = 0; i < len; i++) { 284 | char c = '0' + (Uint8)(size & 0xf); 285 | if(c > '9') c += 39; 286 | *(dest--) = c; 287 | size = size >> 4; 288 | } 289 | return size == 0 ? len : stat_fill(dest, len, '?'); 290 | } 291 | 292 | static Uint16 293 | file_stat(UxnFile *c, void *dest, Uint16 len) 294 | { 295 | struct stat st; 296 | if(c->outside_sandbox) 297 | return 0; 298 | else if(stat(c->current_filename, &st)) 299 | return stat_fill(dest, len, '!'); 300 | else if(S_ISDIR(st.st_mode)) 301 | return stat_fill(dest, len, '-'); 302 | else 303 | return stat_size(dest, len, st.st_size); 304 | } 305 | 306 | static Uint16 307 | file_delete(UxnFile *c) 308 | { 309 | return c->outside_sandbox ? 0 : unlink(c->current_filename); 310 | } 311 | 312 | /* IO */ 313 | 314 | void 315 | file_deo(Uxn *u, Uint8 port) 316 | { 317 | Uint16 addr, len, res; 318 | switch(port) { 319 | case 0xa5: 320 | addr = PEEK2(&u->dev[0xa4]); 321 | len = PEEK2(&u->dev[0xaa]); 322 | if(len > 0x10000 - addr) 323 | len = 0x10000 - addr; 324 | res = file_stat(&uxn_file[0], &u->ram.dat[addr], len); 325 | POKE2(&u->dev[0xa2], res); 326 | break; 327 | case 0xa6: 328 | res = file_delete(&uxn_file[0]); 329 | POKE2(&u->dev[0xa2], res); 330 | break; 331 | case 0xa9: 332 | addr = PEEK2(&u->dev[0xa8]); 333 | res = file_init(&uxn_file[0], (char *)&u->ram.dat[addr], 0x10000 - addr, 0); 334 | POKE2(&u->dev[0xa2], res); 335 | break; 336 | case 0xad: 337 | addr = PEEK2(&u->dev[0xac]); 338 | len = PEEK2(&u->dev[0xaa]); 339 | if(len > 0x10000 - addr) 340 | len = 0x10000 - addr; 341 | res = file_read(&uxn_file[0], &u->ram.dat[addr], len); 342 | POKE2(&u->dev[0xa2], res); 343 | break; 344 | case 0xaf: 345 | addr = PEEK2(&u->dev[0xae]); 346 | len = PEEK2(&u->dev[0xaa]); 347 | if(len > 0x10000 - addr) 348 | len = 0x10000 - addr; 349 | res = file_write(&uxn_file[0], &u->ram.dat[addr], len, u->dev[0xa7]); 350 | POKE2(&u->dev[0xa2], res); 351 | break; 352 | /* File 2 */ 353 | case 0xb5: 354 | addr = PEEK2(&u->dev[0xb4]); 355 | len = PEEK2(&u->dev[0xba]); 356 | if(len > 0x10000 - addr) 357 | len = 0x10000 - addr; 358 | res = file_stat(&uxn_file[1], &u->ram.dat[addr], len); 359 | POKE2(&u->dev[0xb2], res); 360 | break; 361 | case 0xb6: 362 | res = file_delete(&uxn_file[1]); 363 | POKE2(&u->dev[0xb2], res); 364 | break; 365 | case 0xb9: 366 | addr = PEEK2(&u->dev[0xb8]); 367 | res = file_init(&uxn_file[1], (char *)&u->ram.dat[addr], 0x10000 - addr, 0); 368 | POKE2(&u->dev[0xb2], res); 369 | break; 370 | case 0xbd: 371 | addr = PEEK2(&u->dev[0xbc]); 372 | len = PEEK2(&u->dev[0xba]); 373 | if(len > 0x10000 - addr) 374 | len = 0x10000 - addr; 375 | res = file_read(&uxn_file[1], &u->ram.dat[addr], len); 376 | POKE2(&u->dev[0xb2], res); 377 | break; 378 | case 0xbf: 379 | addr = PEEK2(&u->dev[0xbe]); 380 | len = PEEK2(&u->dev[0xba]); 381 | if(len > 0x10000 - addr) 382 | len = 0x10000 - addr; 383 | res = file_write(&uxn_file[1], &u->ram.dat[addr], len, u->dev[0xb7]); 384 | POKE2(&u->dev[0xb2], res); 385 | break; 386 | } 387 | } 388 | -------------------------------------------------------------------------------- /source/devices/file.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick 3 | 4 | Permission to use, copy, modify, and distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE. 10 | */ 11 | 12 | #define POLYFILEY 2 13 | #define DEV_FILE0 0xa 14 | 15 | void file_deo(Uxn *u, Uint8 port); 16 | -------------------------------------------------------------------------------- /source/devices/mouse.c: -------------------------------------------------------------------------------- 1 | #include "../uxn.h" 2 | #include "mouse.h" 3 | 4 | /* 5 | Copyright (c) 2021-2023 Devine Lu Linvega, Andrew Alderwick 6 | 7 | Permission to use, copy, modify, and distribute this software for any 8 | purpose with or without fee is hereby granted, provided that the above 9 | copyright notice and this permission notice appear in all copies. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | WITH REGARD TO THIS SOFTWARE. 13 | */ 14 | 15 | void 16 | mouse_down(Uxn *u, Uint8 *d, Uint8 mask) 17 | { 18 | d[6] |= mask; 19 | uxn_eval(u, PEEK2(d)); 20 | } 21 | 22 | void 23 | mouse_up(Uxn *u, Uint8 *d, Uint8 mask) 24 | { 25 | d[6] &= (~mask); 26 | uxn_eval(u, PEEK2(d)); 27 | } 28 | 29 | void 30 | mouse_pos(Uxn *u, Uint8 *d, Uint16 x, Uint16 y) 31 | { 32 | POKE2(d + 0x2, x); 33 | POKE2(d + 0x4, y); 34 | uxn_eval(u, PEEK2(d)); 35 | } 36 | 37 | void 38 | mouse_scroll(Uxn *u, Uint8 *d, Uint16 x, Uint16 y) 39 | { 40 | POKE2(d + 0xa, x); 41 | POKE2(d + 0xc, -y); 42 | uxn_eval(u, PEEK2(d)); 43 | POKE2(d + 0xa, 0); 44 | POKE2(d + 0xc, 0); 45 | } 46 | -------------------------------------------------------------------------------- /source/devices/mouse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2021 Devine Lu Linvega, Andrew Alderwick 3 | 4 | Permission to use, copy, modify, and distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE. 10 | */ 11 | 12 | void mouse_down(Uxn *u, Uint8 *d, Uint8 mask); 13 | void mouse_up(Uxn *u, Uint8 *d, Uint8 mask); 14 | void mouse_pos(Uxn *u, Uint8 *d, Uint16 x, Uint16 y); 15 | void mouse_scroll(Uxn *u, Uint8 *d, Uint16 x, Uint16 y); 16 | -------------------------------------------------------------------------------- /source/devices/screen.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../uxn.h" 4 | #include "screen.h" 5 | 6 | /* 7 | Copyright (c) 2021-2023 Devine Lu Linvega, Andrew Alderwick 8 | 9 | Permission to use, copy, modify, and distribute this software for any 10 | purpose with or without fee is hereby granted, provided that the above 11 | copyright notice and this permission notice appear in all copies. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 | WITH REGARD TO THIS SOFTWARE. 15 | */ 16 | 17 | UxnScreen uxn_screen; 18 | 19 | /* c = !ch ? (color % 5 ? color >> 2 : 0) : color % 4 + ch == 1 ? 0 : (ch - 2 + (color & 3)) % 3 + 1; */ 20 | 21 | static Uint8 blending[4][16] = { 22 | {0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0}, 23 | {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}, 24 | {1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1}, 25 | {2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2}}; 26 | 27 | static void 28 | screen_change(int x1, int y1, int x2, int y2) 29 | { 30 | if(x1 < uxn_screen.x1) uxn_screen.x1 = x1; 31 | if(y1 < uxn_screen.y1) uxn_screen.y1 = y1; 32 | if(x2 > uxn_screen.x2) uxn_screen.x2 = x2; 33 | if(y2 > uxn_screen.y2) uxn_screen.y2 = y2; 34 | } 35 | 36 | static void 37 | screen_fill(Uint8 *layer, int x1, int y1, int x2, int y2, int color) 38 | { 39 | int x, y, width = uxn_screen.width, height = uxn_screen.height; 40 | for(y = y1; y < y2 && y < height; y++) 41 | for(x = x1; x < x2 && x < width; x++) 42 | layer[x + y * width] = color; 43 | } 44 | 45 | static void 46 | screen_blit(Uint8 *layer, Uint8 *ram, Uint16 addr, int x1, int y1, int color, int flipx, int flipy, int twobpp) 47 | { 48 | int v, h, width = uxn_screen.width, height = uxn_screen.height, opaque = (color % 5) || !color; 49 | for(v = 0; v < 8; v++) { 50 | Uint16 c = ram[(addr + v) & 0xffff] | (twobpp ? (ram[(addr + v + 8) & 0xffff] << 8) : 0); 51 | Uint16 y = y1 + (flipy ? 7 - v : v); 52 | for(h = 7; h >= 0; --h, c >>= 1) { 53 | Uint8 ch = (c & 1) | ((c >> 7) & 2); 54 | if(opaque || ch) { 55 | Uint16 x = x1 + (flipx ? 7 - h : h); 56 | if(x < width && y < height) 57 | layer[x + y * width] = blending[ch][color]; 58 | } 59 | } 60 | } 61 | } 62 | 63 | void 64 | screen_palette(Uint8 *addr) 65 | { 66 | int i, shift; 67 | for(i = 0, shift = 4; i < 4; ++i, shift ^= 4) { 68 | Uint8 69 | r = (addr[0 + i / 2] >> shift) & 0xf, 70 | g = (addr[2 + i / 2] >> shift) & 0xf, 71 | b = (addr[4 + i / 2] >> shift) & 0xf; 72 | uxn_screen.palette[i] = 0x0f000000 | r << 16 | g << 8 | b; 73 | uxn_screen.palette[i] |= uxn_screen.palette[i] << 4; 74 | } 75 | screen_change(0, 0, uxn_screen.width, uxn_screen.height); 76 | } 77 | 78 | void 79 | screen_resize(Uint16 width, Uint16 height) 80 | { 81 | Uint8 *bg, *fg; 82 | Uint32 *pixels; 83 | if(width < 0x8 || height < 0x8 || width >= 0x400 || height >= 0x400) 84 | return; 85 | bg = realloc(uxn_screen.bg, width * height), 86 | fg = realloc(uxn_screen.fg, width * height); 87 | pixels = realloc(uxn_screen.pixels, width * height * sizeof(Uint32)); 88 | if(!bg || !fg || !pixels) 89 | return; 90 | uxn_screen.bg = bg; 91 | uxn_screen.fg = fg; 92 | uxn_screen.pixels = pixels; 93 | uxn_screen.width = width; 94 | uxn_screen.height = height; 95 | screen_fill(uxn_screen.bg, 0, 0, uxn_screen.width, uxn_screen.height, 0); 96 | screen_fill(uxn_screen.fg, 0, 0, uxn_screen.width, uxn_screen.height, 0); 97 | } 98 | 99 | void 100 | screen_redraw(void) 101 | { 102 | Uint8 *fg = uxn_screen.fg, *bg = uxn_screen.bg; 103 | Uint32 palette[16], *pixels = uxn_screen.pixels; 104 | int i, x, y, w = uxn_screen.width, h = uxn_screen.height; 105 | int x1 = uxn_screen.x1; 106 | int y1 = uxn_screen.y1; 107 | int x2 = uxn_screen.x2 > w ? w : uxn_screen.x2; 108 | int y2 = uxn_screen.y2 > h ? h : uxn_screen.y2; 109 | for(i = 0; i < 16; i++) 110 | palette[i] = uxn_screen.palette[(i >> 2) ? (i >> 2) : (i & 3)]; 111 | for(y = y1; y < y2; y++) 112 | for(x = x1; x < x2; x++) { 113 | i = x + y * w; 114 | pixels[i] = palette[fg[i] << 2 | bg[i]]; 115 | } 116 | uxn_screen.x1 = uxn_screen.y1 = 0xffff; 117 | uxn_screen.x2 = uxn_screen.y2 = 0; 118 | } 119 | 120 | Uint8 121 | screen_dei(Uxn *u, Uint8 addr) 122 | { 123 | switch(addr) { 124 | case 0x22: return uxn_screen.width >> 8; 125 | case 0x23: return uxn_screen.width; 126 | case 0x24: return uxn_screen.height >> 8; 127 | case 0x25: return uxn_screen.height; 128 | default: return u->dev[addr]; 129 | } 130 | } 131 | 132 | void 133 | screen_deo(Uint8 *ram, Uint8 *d, Uint8 port) 134 | { 135 | switch(port) { 136 | case 0x3: 137 | screen_resize(PEEK2(d + 2), uxn_screen.height); 138 | break; 139 | case 0x5: 140 | screen_resize(uxn_screen.width, PEEK2(d + 4)); 141 | break; 142 | case 0xe: { 143 | Uint8 ctrl = d[0xe]; 144 | Uint8 color = ctrl & 0x3; 145 | Uint16 x = PEEK2(d + 0x8); 146 | Uint16 y = PEEK2(d + 0xa); 147 | Uint8 *layer = (ctrl & 0x40) ? uxn_screen.fg : uxn_screen.bg; 148 | /* fill mode */ 149 | if(ctrl & 0x80) { 150 | Uint16 x2 = uxn_screen.width; 151 | Uint16 y2 = uxn_screen.height; 152 | if(ctrl & 0x10) x2 = x, x = 0; 153 | if(ctrl & 0x20) y2 = y, y = 0; 154 | screen_fill(layer, x, y, x2, y2, color); 155 | screen_change(x, y, x2, y2); 156 | } 157 | /* pixel mode */ 158 | else { 159 | Uint16 width = uxn_screen.width; 160 | Uint16 height = uxn_screen.height; 161 | if(x < width && y < height) 162 | layer[x + y * width] = color; 163 | screen_change(x, y, x + 1, y + 1); 164 | if(d[0x6] & 0x1) POKE2(d + 0x8, x + 1); /* auto x+1 */ 165 | if(d[0x6] & 0x2) POKE2(d + 0xa, y + 1); /* auto y+1 */ 166 | } 167 | break; 168 | } 169 | case 0xf: { 170 | Uint8 i; 171 | Uint8 ctrl = d[0xf]; 172 | Uint8 move = d[0x6]; 173 | Uint8 length = move >> 4; 174 | Uint8 twobpp = !!(ctrl & 0x80); 175 | Uint8 *layer = (ctrl & 0x40) ? uxn_screen.fg : uxn_screen.bg; 176 | Uint8 color = ctrl & 0xf; 177 | Uint16 x = PEEK2(d + 0x8), dx = (move & 0x1) << 3; 178 | Uint16 y = PEEK2(d + 0xa), dy = (move & 0x2) << 2; 179 | Uint16 addr = PEEK2(d + 0xc), addr_incr = (move & 0x4) << (1 + twobpp); 180 | int flipx = (ctrl & 0x10), fx = flipx ? -1 : 1; 181 | int flipy = (ctrl & 0x20), fy = flipy ? -1 : 1; 182 | Uint16 dyx = dy * fx, dxy = dx * fy; 183 | for(i = 0; i <= length; i++) { 184 | screen_blit(layer, ram, addr, x + dyx * i, y + dxy * i, color, flipx, flipy, twobpp); 185 | addr += addr_incr; 186 | } 187 | screen_change(x, y, x + dyx * length + 8, y + dxy * length + 8); 188 | if(move & 0x1) POKE2(d + 0x8, x + dx * fx); /* auto x+8 */ 189 | if(move & 0x2) POKE2(d + 0xa, y + dy * fy); /* auto y+8 */ 190 | if(move & 0x4) POKE2(d + 0xc, addr); /* auto addr+length */ 191 | break; 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /source/devices/screen.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2021 Devine Lu Linvega 3 | Copyright (c) 2021 Andrew Alderwick 4 | 5 | Permission to use, copy, modify, and distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE. 11 | */ 12 | 13 | typedef struct UxnScreen { 14 | int width, height, x1, y1, x2, y2; 15 | Uint32 palette[4], *pixels; 16 | Uint8 *fg, *bg; 17 | } UxnScreen; 18 | 19 | extern UxnScreen uxn_screen; 20 | void screen_palette(Uint8 *addr); 21 | void screen_resize(Uint16 width, Uint16 height); 22 | void screen_redraw(void); 23 | Uint8 screen_dei(Uxn *u, Uint8 addr); 24 | void screen_deo(Uint8 *ram, Uint8 *d, Uint8 port); 25 | -------------------------------------------------------------------------------- /source/devices/system.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../uxn.h" 5 | #include "system.h" 6 | 7 | /* 8 | Copyright (c) 2022-2023 Devine Lu Linvega, Andrew Alderwick 9 | 10 | Permission to use, copy, modify, and distribute this software for any 11 | purpose with or without fee is hereby granted, provided that the above 12 | copyright notice and this permission notice appear in all copies. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 15 | WITH REGARD TO THIS SOFTWARE. 16 | */ 17 | 18 | static const char *errors[] = { 19 | "underflow", 20 | "overflow", 21 | "division by zero"}; 22 | 23 | static void 24 | system_print(Stack *s, int ptr, char *name) 25 | { 26 | Uint8 i; 27 | iprintf("<%s>", name); 28 | for(i = 0; i < ptr; i++) 29 | iprintf(" %02x", s->dat[i]); 30 | if(!i) 31 | iprintf(" empty"); 32 | iprintf("\n"); 33 | } 34 | 35 | int 36 | system_error(char *msg, const char *err) 37 | { 38 | iprintf("%s: %s\n", msg, err); 39 | fflush(stderr); 40 | return 0; 41 | } 42 | 43 | int 44 | system_load(Uxn *u, char *filename) 45 | { 46 | int l, i = 0; 47 | FILE *f = fopen(filename, "rb"); 48 | if(!f) 49 | return 0; 50 | l = fread(&u->ram.dat[PAGE_PROGRAM], 0x10000 - PAGE_PROGRAM, 1, f); 51 | while(l && ++i < RAM_PAGES) 52 | l = fread(u->ram.dat + 0x10000 * i, 0x10000, 1, f); 53 | fclose(f); 54 | return 1; 55 | } 56 | 57 | void 58 | system_inspect(Uxn *u) 59 | { 60 | system_print(&u->wst, uxn_get_wst_ptr(), "wst"); 61 | system_print(&u->rst, uxn_get_rst_ptr(), "rst"); 62 | } 63 | 64 | /* IO */ 65 | 66 | Uint8 67 | system_dei(Uxn *u, Uint8 addr) 68 | { 69 | switch(addr) { 70 | case 0x4: return uxn_get_wst_ptr(); 71 | case 0x5: return uxn_get_rst_ptr(); 72 | default: return u->dev[addr]; 73 | } 74 | } 75 | 76 | void 77 | system_deo(Uxn *u, Uint8 *d, Uint8 port) 78 | { 79 | Uint8 *ram; 80 | Uint16 addr; 81 | switch(port) { 82 | case 0x3: 83 | ram = u->ram.dat; 84 | addr = PEEK2(d + 2); 85 | if(ram[addr] == 0x0) { 86 | Uint8 value = ram[addr + 7]; 87 | Uint16 i, length = PEEK2(ram + addr + 1); 88 | Uint16 dst_page = PEEK2(ram + addr + 3), dst_addr = PEEK2(ram + addr + 5); 89 | int dst = (dst_page % RAM_PAGES) * 0x10000; 90 | for(i = 0; i < length; i++) 91 | ram[dst + (Uint16)(dst_addr + i)] = value; 92 | } else if(ram[addr] == 0x1) { 93 | Uint16 i, length = PEEK2(ram + addr + 1); 94 | Uint16 a_page = PEEK2(ram + addr + 3), a_addr = PEEK2(ram + addr + 5); 95 | Uint16 b_page = PEEK2(ram + addr + 7), b_addr = PEEK2(ram + addr + 9); 96 | int src = (a_page % RAM_PAGES) * 0x10000, dst = (b_page % RAM_PAGES) * 0x10000; 97 | for(i = 0; i < length; i++) 98 | ram[dst + (Uint16)(b_addr + i)] = ram[src + (Uint16)(a_addr + i)]; 99 | } else if(ram[addr] == 0x2) { 100 | Uint16 i, length = PEEK2(ram + addr + 1); 101 | Uint16 a_page = PEEK2(ram + addr + 3), a_addr = PEEK2(ram + addr + 5); 102 | Uint16 b_page = PEEK2(ram + addr + 7), b_addr = PEEK2(ram + addr + 9); 103 | int src = (a_page % RAM_PAGES) * 0x10000, dst = (b_page % RAM_PAGES) * 0x10000; 104 | for(i = length - 1; i != 0xffff; i--) 105 | ram[dst + (Uint16)(b_addr + i)] = ram[src + (Uint16)(a_addr + i)]; 106 | } else 107 | fiprintf(stderr, "Unknown Expansion Command 0x%02x\n", ram[addr]); 108 | break; 109 | case 0x4: 110 | uxn_set_wst_ptr(d[4]); 111 | break; 112 | case 0x5: 113 | uxn_set_rst_ptr(d[5]); 114 | break; 115 | case 0xe: 116 | system_inspect(u); 117 | break; 118 | } 119 | } 120 | 121 | /* Errors */ 122 | 123 | int 124 | uxn_halt(Uxn *u, Uint8 instr, Uint8 err, Uint16 addr) 125 | { 126 | Uint8 *d = &u->dev[0]; 127 | Uint16 handler = PEEK2(d); 128 | if(handler) { 129 | uxn_set_wst_ptr(4); 130 | u->wst.dat[0] = addr >> 0x8; 131 | u->wst.dat[1] = addr & 0xff; 132 | u->wst.dat[2] = instr; 133 | u->wst.dat[3] = err; 134 | return uxn_eval(u, handler); 135 | } else { 136 | system_inspect(u); 137 | iprintf("%s %s, by %02x at 0x%04x.\n", (instr & 0x40) ? "Return-stack" : "Working-stack", errors[err - 1], instr, addr); 138 | } 139 | return 0; 140 | } 141 | 142 | /* Console */ 143 | 144 | int 145 | console_input(Uxn *u, char c, int type) 146 | { 147 | Uint8 *d = &u->dev[0x10]; 148 | d[0x2] = c; 149 | d[0x7] = type; 150 | return uxn_eval(u, PEEK2(d)); 151 | } 152 | 153 | void 154 | console_deo(Uint8 *d, Uint8 port) 155 | { 156 | switch(port) { 157 | case 0x8: 158 | case 0x9: 159 | fputc(d[port], stdout); 160 | fflush(stdout); 161 | return; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /source/devices/system.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2022 Devine Lu Linvega, Andrew Alderwick 3 | 4 | Permission to use, copy, modify, and distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE. 10 | */ 11 | 12 | #define CONSOLE_STD 0x1 13 | #define CONSOLE_ARG 0x2 14 | #define CONSOLE_EOA 0x3 15 | #define CONSOLE_END 0x4 16 | 17 | int system_load(Uxn *u, char *filename); 18 | void system_inspect(Uxn *u); 19 | int system_error(char *msg, const char *err); 20 | Uint8 system_dei(Uxn *u, Uint8 addr); 21 | void system_deo(Uxn *u, Uint8 *d, Uint8 port); 22 | int console_input(Uxn *u, char c, int type); 23 | void console_deo(Uint8 *d, Uint8 port); 24 | -------------------------------------------------------------------------------- /source/ndsabi/macros.inc: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (C) 2021-2023 agbabi contributors 4 | // 5 | // ARM assembly support macros 6 | 7 | @ Shift and test upper two bits, clobbering \reg 8 | @ Use mi for first bit, cs for second bit 9 | .macro joaobapt_test_lsl reg shift = #0 10 | movs \reg, \reg, lsl \shift 11 | .endm 12 | 13 | @ Test lowest two bits, clobbering \reg 14 | @ Use mi for low bit, cs for high bit 15 | .macro joaobapt_test reg 16 | joaobapt_test_lsl \reg, #31 17 | .endm 18 | 19 | @ Test lowest two bits of \src, result stored in \dst 20 | @ Use mi for low bit, cs for high bit 21 | .macro joaobapt_test_into dst, src 22 | movs \dst, \src, lsl #31 23 | .endm 24 | 25 | @ Branches depending on lowest two bits, clobbering \reg 26 | @ b_mi = low bit case, b_cs = high bit case 27 | .macro joaobapt_switch reg, b_mi, b_cs 28 | joaobapt_test \reg 29 | bmi \b_mi 30 | bcs \b_cs 31 | .endm 32 | 33 | @ Branches depending on alignment of \a and \b, clobbering \scratch 34 | @ b_byte = off-by-byte case, b_half = off-by-half case 35 | .macro align_switch a, b, scratch, b_byte, b_half 36 | eor \scratch, \a, \b 37 | joaobapt_switch \scratch, \b_byte, \b_half 38 | .endm 39 | -------------------------------------------------------------------------------- /source/ndsabi/memcpy.s: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (C) 2021-2023 agbabi contributors 4 | // 5 | // ABI: 6 | // __aeabi_memcpy, __aeabi_memcpy4, __aeabi_memcpy8 7 | // Standard: 8 | // memcpy 9 | // Support: 10 | // __ndsabi_memcpy2, __ndsabi_memcpy1 11 | 12 | #include "macros.inc" 13 | 14 | .arm 15 | .align 2 16 | 17 | .section .text.__aeabi_memcpy, "ax", %progbits 18 | .global __aeabi_memcpy 19 | .type __aeabi_memcpy, %function 20 | __aeabi_memcpy: 21 | @ >6-bytes is roughly the threshold when byte-by-byte copy is slower 22 | cmp r2, #6 23 | ble __ndsabi_memcpy1 24 | 25 | align_switch r0, r1, r3, __ndsabi_memcpy1, .Lcopy_halves 26 | 27 | @ Check if r0 (or r1) needs word aligning 28 | rsbs r3, r0, #4 29 | joaobapt_test r3 30 | 31 | @ Copy byte head to align 32 | ldrmib r3, [r1], #1 33 | strmib r3, [r0], #1 34 | submi r2, r2, #1 35 | @ r0, r1 are now half aligned 36 | 37 | @ Copy half head to align 38 | ldrcsh r3, [r1], #2 39 | strcsh r3, [r0], #2 40 | subcs r2, r2, #2 41 | @ r0, r1 are now word aligned 42 | 43 | .global __aeabi_memcpy8 44 | .type __aeabi_memcpy8, %function 45 | __aeabi_memcpy8: 46 | .global __aeabi_memcpy4 47 | .type __aeabi_memcpy4, %function 48 | __aeabi_memcpy4: 49 | cmp r2, #32 50 | blt .Lcopy_words 51 | 52 | @ Word aligned, 32-byte copy 53 | push {r4-r10} 54 | .Lloop_32: 55 | subs r2, r2, #32 56 | ldmgeia r1!, {r3-r10} 57 | stmgeia r0!, {r3-r10} 58 | bgt .Lloop_32 59 | pop {r4-r10} 60 | bxeq lr 61 | 62 | @ < 32 bytes remaining to be copied 63 | add r2, r2, #32 64 | 65 | .Lcopy_words: 66 | cmp r2, #4 67 | blt .Lcopy_halves 68 | .Lloop_4: 69 | subs r2, r2, #4 70 | ldrge r3, [r1], #4 71 | strge r3, [r0], #4 72 | bgt .Lloop_4 73 | bxeq lr 74 | 75 | @ Copy byte & half tail 76 | @ This test still works when r2 is negative 77 | joaobapt_test r2 78 | @ Copy half 79 | ldrcsh r3, [r1], #2 80 | strcsh r3, [r0], #2 81 | @ Copy byte 82 | ldrmib r3, [r1] 83 | strmib r3, [r0] 84 | bx lr 85 | 86 | .Lcopy_halves: 87 | @ Copy byte head to align 88 | tst r0, #1 89 | ldrneb r3, [r1], #1 90 | strneb r3, [r0], #1 91 | subne r2, r2, #1 92 | @ r0, r1 are now half aligned 93 | 94 | .global __ndsabi_memcpy2 95 | .type __ndsabi_memcpy2, %function 96 | __ndsabi_memcpy2: 97 | subs r2, r2, #2 98 | ldrgeh r3, [r1], #2 99 | strgeh r3, [r0], #2 100 | bgt __ndsabi_memcpy2 101 | bxeq lr 102 | 103 | @ Copy byte tail 104 | adds r2, r2, #2 105 | ldrneb r3, [r1] 106 | strneb r3, [r0] 107 | bx lr 108 | 109 | .global __ndsabi_memcpy1 110 | .type __ndsabi_memcpy1, %function 111 | __ndsabi_memcpy1: 112 | subs r2, r2, #1 113 | ldrgeb r3, [r1], #1 114 | strgeb r3, [r0], #1 115 | bgt __ndsabi_memcpy1 116 | bx lr 117 | 118 | .section .text.memcpy, "ax", %progbits 119 | .global memcpy 120 | .type memcpy, %function 121 | memcpy: 122 | push {r0, lr} 123 | bl __aeabi_memcpy 124 | pop {r0, lr} 125 | bx lr 126 | -------------------------------------------------------------------------------- /source/ndsabi/memmove.s: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (C) 2021-2023 agbabi contributors 4 | // 5 | // ABI: 6 | // __aeabi_memmove, __aeabi_memmove4, __aeabi_memmove8 7 | // Standard: 8 | // memmove 9 | 10 | .arm 11 | .align 2 12 | 13 | .section .text.__aeabi_memmove, "ax", %progbits 14 | .global __aeabi_memmove 15 | .type __aeabi_memmove, %function 16 | __aeabi_memmove: 17 | cmp r0, r1 18 | .extern __ndsabi_rmemcpy 19 | bgt __ndsabi_rmemcpy 20 | .extern __aeabi_memcpy 21 | b __aeabi_memcpy 22 | 23 | .global __aeabi_memmove8 24 | .type __aeabi_memmove8, %function 25 | __aeabi_memmove8: 26 | .global __aeabi_memmove4 27 | .type __aeabi_memmove4, %function 28 | __aeabi_memmove4: 29 | cmp r0, r1 30 | .extern __ndsabi_rmemcpy 31 | bgt __ndsabi_rmemcpy 32 | .extern __aeabi_memcpy4 33 | b __aeabi_memcpy4 34 | 35 | .global __ndsabi_memmove1 36 | .type __ndsabi_memmove1, %function 37 | __ndsabi_memmove1: 38 | cmp r0, r1 39 | .extern __ndsabi_rmemcpy1 40 | bgt __ndsabi_rmemcpy1 41 | .extern __ndsabi_memcpy1 42 | b __ndsabi_memcpy1 43 | 44 | .section .text.memmove, "ax", %progbits 45 | .global memmove 46 | .type memmove, %function 47 | memmove: 48 | push {r0, lr} 49 | bl __aeabi_memmove 50 | pop {r0, lr} 51 | bx lr 52 | -------------------------------------------------------------------------------- /source/ndsabi/memset.s: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (C) 2021-2023 agbabi contributors 4 | // 5 | // ABI: 6 | // __aeabi_memclr, __aeabi_memclr4, __aeabi_memclr8, 7 | // __aeabi_memset, __aeabi_memset4, __aeabi_memset8 8 | // Standard: 9 | // memset 10 | // Support: 11 | // __ndsabi_wordset4, __ndsabi_lwordset4, __ndsabi_memset1 12 | 13 | #include "macros.inc" 14 | 15 | .arm 16 | .align 2 17 | 18 | .section .text.__aeabi_memclr, "ax", %progbits 19 | .global __aeabi_memclr 20 | .type __aeabi_memclr, %function 21 | __aeabi_memclr: 22 | mov r2, #0 23 | b __aeabi_memset 24 | 25 | .global __aeabi_memclr8 26 | .type __aeabi_memclr8, %function 27 | __aeabi_memclr8: 28 | .global __aeabi_memclr4 29 | .type __aeabi_memclr4, %function 30 | __aeabi_memclr4: 31 | mov r2, #0 32 | b __ndsabi_wordset4 33 | 34 | .section .text.__aeabi_memset, "ax", %progbits 35 | .global __aeabi_memset 36 | .type __aeabi_memset, %function 37 | __aeabi_memset: 38 | @ < 8 bytes probably won't be aligned: go byte-by-byte 39 | cmp r1, #8 40 | blt __ndsabi_memset1 41 | 42 | @ Copy head to align to next word 43 | rsb r3, r0, #4 44 | joaobapt_test r3 45 | strmib r2, [r0], #1 46 | submi r1, r1, #1 47 | strcsb r2, [r0], #1 48 | strcsb r2, [r0], #1 49 | subcs r1, r1, #2 50 | 51 | .global __aeabi_memset8 52 | .type __aeabi_memset8, %function 53 | __aeabi_memset8: 54 | .global __aeabi_memset4 55 | .type __aeabi_memset4, %function 56 | __aeabi_memset4: 57 | lsl r2, r2, #24 58 | orr r2, r2, r2, lsr #8 59 | orr r2, r2, r2, lsr #16 60 | 61 | .global __ndsabi_wordset4 62 | .type __ndsabi_wordset4, %function 63 | __ndsabi_wordset4: 64 | mov r3, r2 65 | 66 | .global __ndsabi_lwordset4 67 | .type __ndsabi_lwordset4, %function 68 | __ndsabi_lwordset4: 69 | @ 16 words is roughly the threshold when lwordset is slower 70 | cmp r1, #64 71 | blt .Lset_2_words 72 | 73 | @ 8 word set 74 | push {r4-r9} 75 | mov r4, r2 76 | mov r5, r3 77 | mov r6, r2 78 | mov r7, r3 79 | mov r8, r2 80 | mov r9, r3 81 | 82 | .Lset_8_words: 83 | subs r1, r1, #32 84 | stmgeia r0!, {r2-r9} 85 | bgt .Lset_8_words 86 | pop {r4-r9} 87 | bxeq lr 88 | 89 | @ Fixup remaining 90 | add r1, r1, #32 91 | .Lset_2_words: 92 | subs r1, r1, #8 93 | stmgeia r0!, {r2-r3} 94 | bgt .Lset_2_words 95 | bxeq lr 96 | 97 | @ Test for remaining word 98 | adds r1, r1, #4 99 | strge r2, [r0], #4 100 | bxeq lr 101 | 102 | @ Set tail 103 | joaobapt_test r1 104 | strcsh r2, [r0], #2 105 | strmib r2, [r0], #1 106 | bx lr 107 | 108 | .global __ndsabi_memset1 109 | .type __ndsabi_memset, %function 110 | __ndsabi_memset1: 111 | subs r1, r1, #1 112 | strgeb r2, [r0], #1 113 | bgt __ndsabi_memset1 114 | bx lr 115 | 116 | .section .text.memset, "ax", %progbits 117 | .global memset 118 | .type memset, %function 119 | memset: 120 | mov r3, r1 121 | mov r1, r2 122 | mov r2, r3 123 | push {r0, lr} 124 | bl __aeabi_memset 125 | pop {r0, lr} 126 | bx lr 127 | -------------------------------------------------------------------------------- /source/ndsabi/rmemcpy.s: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Zlib 2 | // 3 | // Copyright (C) 2021-2023 agbabi contributors 4 | // 5 | // Support: 6 | // __ndsabi_rmemcpy, __ndsabi_rmemcpy1 7 | 8 | #include "macros.inc" 9 | 10 | .arm 11 | .align 2 12 | 13 | .section .text.__ndsabi_rmemcpy, "ax", %progbits 14 | .global __ndsabi_rmemcpy 15 | .type __ndsabi_rmemcpy, %function 16 | __ndsabi_rmemcpy: 17 | @ >6-bytes is roughly the threshold when byte-by-byte copy is slower 18 | cmp r2, #6 19 | ble __ndsabi_rmemcpy1 20 | 21 | align_switch r0, r1, r3, __ndsabi_rmemcpy1, .Lcopy_halves 22 | 23 | @ Check if end needs word aligning 24 | add r3, r0, r2 25 | joaobapt_test r3 26 | 27 | @ Copy byte tail to align 28 | submi r2, r2, #1 29 | ldrmib r3, [r1, r2] 30 | strmib r3, [r0, r2] 31 | @ r2 is now half aligned 32 | 33 | @ Copy half tail to align 34 | subcs r2, r2, #2 35 | ldrcsh r3, [r1, r2] 36 | strcsh r3, [r0, r2] 37 | @ r2 is now word aligned 38 | 39 | cmp r2, #32 40 | blt .Lcopy_words 41 | 42 | @ Word aligned, 32-byte copy 43 | push {r0-r1, r4-r10} 44 | add r0, r0, r2 45 | add r1, r1, r2 46 | .Lloop_32: 47 | subs r2, r2, #32 48 | ldmgedb r1!, {r3-r10} 49 | stmgedb r0!, {r3-r10} 50 | bgt .Lloop_32 51 | pop {r0-r1, r4-r10} 52 | bxeq lr 53 | 54 | @ < 32 bytes remaining to be copied 55 | add r2, r2, #32 56 | 57 | .Lcopy_words: 58 | subs r2, r2, #4 59 | ldrge r3, [r1, r2] 60 | strge r3, [r0, r2] 61 | bgt .Lcopy_words 62 | bxeq lr 63 | 64 | @ Copy byte & half head 65 | joaobapt_test_into r3, r2 66 | @ Copy half 67 | addcs r2, r2, #2 68 | ldrcsh r3, [r1, r2] 69 | strcsh r3, [r0, r2] 70 | @ Copy byte 71 | ldrmib r3, [r1] 72 | strmib r3, [r0] 73 | bx lr 74 | 75 | .Lcopy_halves: 76 | @ Copy byte tail to align 77 | add r3, r0, r2 78 | tst r3, #1 79 | subne r2, r2, #1 80 | ldrneb r3, [r1, r2] 81 | strneb r3, [r0, r2] 82 | @ r2 is now half aligned 83 | 84 | .Lloop_2: 85 | subs r2, r2, #2 86 | ldrgeh r3, [r1, r2] 87 | strgeh r3, [r0, r2] 88 | bgt .Lloop_2 89 | bxeq lr 90 | 91 | @ Copy byte head 92 | ldrb r3, [r1] 93 | strb r3, [r0] 94 | bx lr 95 | 96 | .global __ndsabi_rmemcpy1 97 | .type __ndsabi_rmemcpy1, %function 98 | __ndsabi_rmemcpy1: 99 | subs r2, r2, #1 100 | ldrgeb r3, [r1, r2] 101 | strgeb r3, [r0, r2] 102 | bgt __ndsabi_rmemcpy1 103 | bx lr 104 | -------------------------------------------------------------------------------- /source/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2021 Devine Lu Linvega 3 | Copyright (c) 2021 Andrew Alderwick 4 | 5 | Permission to use, copy, modify, and distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE. 11 | */ 12 | 13 | #include "util.h" 14 | 15 | int 16 | clamp(int val, int min, int max) 17 | { 18 | return (val >= min) ? (val <= max) ? val : max : min; 19 | } 20 | 21 | int 22 | next_power_of_two(int val) 23 | { 24 | val--; 25 | val |= val >> 1; 26 | val |= val >> 2; 27 | val |= val >> 4; 28 | val |= val >> 8; 29 | val |= val >> 16; 30 | return val + 1; 31 | } -------------------------------------------------------------------------------- /source/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2021 Devine Lu Linvega 3 | Copyright (c) 2021 Andrew Alderwick 4 | 5 | Permission to use, copy, modify, and distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE. 11 | */ 12 | 13 | int clamp(int val, int min, int max); 14 | int next_power_of_two(int val); -------------------------------------------------------------------------------- /source/uxn.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* 7 | Copyright (c) 2021 Devine Lu Linvega 8 | Copyright (c) 2021 Adrian "asie" Siekierka 9 | 10 | Permission to use, copy, modify, and distribute this software for any 11 | purpose with or without fee is hereby granted, provided that the above 12 | copyright notice and this permission notice appear in all copies. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 15 | WITH REGARD TO THIS SOFTWARE. 16 | */ 17 | 18 | #ifdef DEBUG 19 | #define dprintf iprintf 20 | #else 21 | #define dprintf(...) 22 | #endif 23 | 24 | #ifdef __BLOCKSDS__ 25 | #define iprintf printf 26 | #define siprintf sprintf 27 | #define sniprintf snprintf 28 | #define fiprintf fprintf 29 | #endif 30 | 31 | #define MAX_PATH 1024 32 | #ifdef __NDS__ 33 | #define ITCM_ARM_CODE __attribute__((section(".itcm"), long_call, target("arm"))) 34 | #else 35 | #define ITCM_ARM_CODE 36 | #endif 37 | 38 | typedef uint8_t Uint8; 39 | typedef int8_t Sint8; 40 | typedef uint16_t Uint16; 41 | typedef int16_t Sint16; 42 | typedef unsigned int Uint32; 43 | 44 | #define PAGE_PROGRAM 0x0100 45 | #define RAM_PAGES 0x0F 46 | 47 | #define GETVEC(d) ((d)[0] << 8 | (d)[1]) 48 | #define POKDEV(x, y) { d[(x)] = (y) >> 8; d[(x) + 1] = (y); } 49 | #define PEKDEV(o, x) { (o) = (d[(x)] << 8) + d[(x) + 1]; } 50 | #define POKE2(d, v) { (d)[0] = (v) >> 8; (d)[1] = (v); } 51 | #define PEEK2(d) ((d)[0] << 8 | (d)[1]) 52 | 53 | static inline void poke8(Uint8 *m, Uint16 a, Uint8 b) { m[a] = b; } 54 | static inline Uint8 peek8(Uint8 *m, Uint16 a) { return m[a]; } 55 | static inline void poke16(Uint8 *m, Uint16 a, Uint16 b) { poke8(m, a, b >> 8); poke8(m, a + 1, b); } 56 | static inline Uint16 peek16(Uint8 *m, Uint16 a) { return (peek8(m, a) << 8) + peek8(m, a + 1); } 57 | 58 | typedef Uint8 (*uxn_dei_t)(Uint8*, Uint8); 59 | typedef void (*uxn_deo_t)(Uint8*, Uint8); 60 | 61 | int uxn_get_wst_ptr(void); 62 | int uxn_get_rst_ptr(void); 63 | void uxn_set_wst_ptr(int value); 64 | void uxn_set_rst_ptr(int value); 65 | void uxn_register_device(int id, uxn_dei_t dei, uxn_deo_t deo); 66 | 67 | int resetuxn(void); 68 | int uxn_boot(void); 69 | 70 | // Legacy API 71 | 72 | typedef struct { 73 | Uint8 *dat; 74 | } Stack; 75 | 76 | typedef struct { 77 | Uint8 *dat; 78 | } Memory; 79 | 80 | typedef struct Uxn { 81 | Stack wst, rst; 82 | Memory ram; 83 | Uint8 *dev; 84 | } Uxn; 85 | 86 | struct Uxn; 87 | 88 | extern Uxn u; 89 | 90 | int uxn_eval(Uxn *u, Uint32 vec); 91 | -------------------------------------------------------------------------------- /source/uxngba-c.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Bad Diode and Devine Lu Linvega 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #ifdef __NDS__ 24 | #include 25 | #else 26 | #include <3ds.h> 27 | #define DTCM_DATA 28 | #define DTCM_BSS 29 | #endif 30 | 31 | #include "uxn.h" 32 | 33 | extern void uxn_eval_asm(Uint32 pc); 34 | 35 | DTCM_BSS u8 wst[256]; 36 | DTCM_BSS u8 rst[256]; 37 | 38 | #ifdef __3DS__ 39 | uintptr_t wst_ptr = (uintptr_t) wst; 40 | uintptr_t rst_ptr = (uintptr_t) rst; 41 | #else 42 | extern uintptr_t wst_ptr; 43 | extern uintptr_t rst_ptr; 44 | #endif 45 | 46 | // JSI and similar depend on a 64K alignment (!) 47 | __attribute__((aligned(65536))) 48 | u8 uxn_ram[64 * 1024 * RAM_PAGES]; 49 | 50 | ITCM_ARM_CODE 51 | void 52 | deo_stub(u8 *dev, u8 port) { 53 | (void)dev; 54 | (void)port; 55 | } 56 | 57 | ITCM_ARM_CODE 58 | void 59 | deo2_wrap(u8 *dev, u8 port, uxn_deo_t deo1) { 60 | deo1(dev,port); 61 | deo1(dev,port+1); 62 | } 63 | 64 | ITCM_ARM_CODE 65 | Uint8 66 | dei_stub(u8 *dev, u8 port) { 67 | return dev[port]; 68 | } 69 | 70 | unsigned int __aeabi_uidiv(unsigned int num, unsigned int den); 71 | 72 | ITCM_ARM_CODE 73 | unsigned int 74 | uxn_uidiv(unsigned int num, unsigned int den) { 75 | return den ? __aeabi_uidiv(num, den) : 0; 76 | } 77 | 78 | DTCM_DATA 79 | uxn_deo_t deo_map[16] = { 80 | deo_stub, deo_stub, deo_stub, deo_stub, 81 | deo_stub, deo_stub, deo_stub, deo_stub, 82 | deo_stub, deo_stub, deo_stub, deo_stub, 83 | deo_stub, deo_stub, deo_stub, deo_stub 84 | }; 85 | 86 | DTCM_DATA 87 | uxn_dei_t dei_map[16] = { 88 | dei_stub, dei_stub, dei_stub, dei_stub, 89 | dei_stub, dei_stub, dei_stub, dei_stub, 90 | dei_stub, dei_stub, dei_stub, dei_stub, 91 | dei_stub, dei_stub, dei_stub, dei_stub 92 | }; 93 | DTCM_BSS u8 device_data[256]; 94 | 95 | int 96 | resetuxn(void) 97 | { 98 | // Reset the stacks 99 | memset(wst, 0, sizeof(wst)); 100 | wst_ptr = (uintptr_t) &wst; 101 | memset(rst, 0, sizeof(rst)); 102 | rst_ptr = (uintptr_t) &rst; 103 | 104 | memset(device_data, 0, 16 * 16); 105 | 106 | // Reset RAM 107 | memset(uxn_ram, 0, sizeof(uxn_ram)); 108 | return 1; 109 | } 110 | 111 | int 112 | uxn_boot(void) 113 | { 114 | // Emulate legacy API 115 | u.wst.dat = wst; 116 | u.rst.dat = rst; 117 | u.dev = device_data; 118 | u.ram.dat = uxn_ram; 119 | 120 | return resetuxn(); 121 | } 122 | 123 | int 124 | uxn_eval(Uxn *u, Uint32 vec) 125 | { 126 | uxn_eval_asm(vec); 127 | return 1; 128 | } 129 | 130 | int 131 | uxn_get_wst_ptr(void) 132 | { 133 | return wst_ptr - ((uintptr_t) &wst); 134 | } 135 | 136 | int 137 | uxn_get_rst_ptr(void) 138 | { 139 | return rst_ptr - ((uintptr_t) &rst); 140 | } 141 | 142 | void 143 | uxn_set_wst_ptr(int value) 144 | { 145 | rst_ptr = ((uintptr_t) &rst) + value; 146 | } 147 | 148 | void 149 | uxn_set_rst_ptr(int value) 150 | { 151 | rst_ptr = ((uintptr_t) &rst) + value; 152 | } 153 | 154 | void 155 | uxn_register_device(int id, uxn_dei_t dei, uxn_deo_t deo) 156 | { 157 | if (dei != NULL) 158 | dei_map[id] = dei; 159 | if (deo != NULL) 160 | deo_map[id] = deo; 161 | } 162 | -------------------------------------------------------------------------------- /uxn/asma.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/uxnds/56a2027c3d6ca96f45202e1594eaac8ac0ad8152/uxn/asma.rom -------------------------------------------------------------------------------- /uxn/calc.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/uxnds/56a2027c3d6ca96f45202e1594eaac8ac0ad8152/uxn/calc.rom -------------------------------------------------------------------------------- /uxn/catclock.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/uxnds/56a2027c3d6ca96f45202e1594eaac8ac0ad8152/uxn/catclock.rom -------------------------------------------------------------------------------- /uxn/dexe.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/uxnds/56a2027c3d6ca96f45202e1594eaac8ac0ad8152/uxn/dexe.rom -------------------------------------------------------------------------------- /uxn/launcher.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/uxnds/56a2027c3d6ca96f45202e1594eaac8ac0ad8152/uxn/launcher.rom -------------------------------------------------------------------------------- /uxn/left.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/uxnds/56a2027c3d6ca96f45202e1594eaac8ac0ad8152/uxn/left.rom -------------------------------------------------------------------------------- /uxn/nasu.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/uxnds/56a2027c3d6ca96f45202e1594eaac8ac0ad8152/uxn/nasu.rom -------------------------------------------------------------------------------- /uxn/noodle.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/uxnds/56a2027c3d6ca96f45202e1594eaac8ac0ad8152/uxn/noodle.rom -------------------------------------------------------------------------------- /uxn/orca.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/uxnds/56a2027c3d6ca96f45202e1594eaac8ac0ad8152/uxn/orca.rom -------------------------------------------------------------------------------- /uxn/piano.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/uxnds/56a2027c3d6ca96f45202e1594eaac8ac0ad8152/uxn/piano.rom -------------------------------------------------------------------------------- /uxn/turye.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asiekierka/uxnds/56a2027c3d6ca96f45202e1594eaac8ac0ad8152/uxn/turye.rom --------------------------------------------------------------------------------