├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile.gc ├── Makefile.wii ├── emgba.cli.example ├── emgba.dcp └── source ├── 3ds.c ├── 3ds.h ├── clock.c ├── clock.h ├── gba.c ├── gba.h ├── gba_mb.c ├── gba_mb.h ├── gba_memory.c ├── gba_video.c ├── gbp.h ├── gbp_sram.c ├── gx.c ├── gx.h ├── gx_cursor.c ├── gx_font.c ├── gx_overlay.c ├── gx_packed.c ├── gx_planar.c ├── gx_prescale.c ├── gx_preview.c ├── gx_solid.c ├── input.c ├── input.h ├── main.c ├── network.c ├── network.h ├── sntp.c ├── sntp.h ├── state.h ├── stub.c ├── stub.h ├── sysconf.h ├── util.h ├── video.c ├── video.h ├── vm ├── dsi_handler.S ├── vm.c └── vm.h ├── wiiload.c └── wiiload.h /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.dol 3 | *.elf 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "mgba"] 2 | path = mgba 3 | url = https://github.com/extremscorner/mgba.git 4 | shallow = true 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /Makefile.gc: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | # Clear the implicit built in rules 3 | #--------------------------------------------------------------------------------- 4 | .SUFFIXES: 5 | #--------------------------------------------------------------------------------- 6 | ifeq ($(strip $(DEVKITRICE)),) 7 | $(error "Please set DEVKITRICE in your environment. export DEVKITRICE=devkitRice") 8 | endif 9 | 10 | include $(DEVKITRICE)/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 | # INCLUDES is a list of directories containing extra header files 17 | #--------------------------------------------------------------------------------- 18 | TARGET := emgba 19 | BUILD := build 20 | SOURCES := source \ 21 | source/vm 22 | DATA := data 23 | INCLUDES := mgba/src \ 24 | mgba/src/arm \ 25 | mgba/include 26 | 27 | #--------------------------------------------------------------------------------- 28 | # options for code generation 29 | #--------------------------------------------------------------------------------- 30 | 31 | CFLAGS = -g -Ofast -std=gnu99 -Wall -Wno-multichar -mogc -flto=jobserver $(INCLUDE) 32 | CXXFLAGS = $(CFLAGS) 33 | 34 | LDFLAGS = $(CFLAGS) -Wl,-Map,$(notdir $@).map 35 | 36 | #--------------------------------------------------------------------------------- 37 | # any extra libraries we wish to link with the project 38 | #--------------------------------------------------------------------------------- 39 | LIBS := -lpng -lz -lfat -lbba -lasnd -static 40 | 41 | #--------------------------------------------------------------------------------- 42 | # list of directories containing libraries, this must be the top level containing 43 | # include and lib 44 | #--------------------------------------------------------------------------------- 45 | LIBDIRS := 46 | 47 | #--------------------------------------------------------------------------------- 48 | # no real need to edit anything past this point unless you need to add additional 49 | # rules for different file extensions 50 | #--------------------------------------------------------------------------------- 51 | ifneq ($(BUILD),$(notdir $(CURDIR))) 52 | #--------------------------------------------------------------------------------- 53 | 54 | export OUTPUT := $(CURDIR)/$(TARGET) 55 | 56 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 57 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 58 | 59 | export DEPSDIR := $(CURDIR)/$(BUILD) 60 | 61 | #--------------------------------------------------------------------------------- 62 | # automatically build a list of object files for our project 63 | #--------------------------------------------------------------------------------- 64 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 65 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 66 | sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 67 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) 68 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 69 | 70 | #--------------------------------------------------------------------------------- 71 | # use CXX for linking C++ projects, CC for standard C 72 | #--------------------------------------------------------------------------------- 73 | ifeq ($(strip $(CPPFILES)),) 74 | export LD := $(CC) 75 | else 76 | export LD := $(CXX) 77 | endif 78 | 79 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 80 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ 81 | $(sFILES:.s=.o) $(SFILES:.S=.o) 82 | 83 | #--------------------------------------------------------------------------------- 84 | # build a list of include paths 85 | #--------------------------------------------------------------------------------- 86 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 87 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 88 | -I$(CURDIR)/$(BUILD) 89 | 90 | #--------------------------------------------------------------------------------- 91 | # build a list of library paths 92 | #--------------------------------------------------------------------------------- 93 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ 94 | $(CURDIR)/mgba/libmgba.a 95 | 96 | export OUTPUT := $(CURDIR)/$(TARGET) 97 | .PHONY: $(BUILD) clean 98 | 99 | #--------------------------------------------------------------------------------- 100 | $(BUILD): 101 | @[ -d $@ ] || mkdir -p $@ 102 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile.gc 103 | 104 | #--------------------------------------------------------------------------------- 105 | clean: 106 | @echo clean ... 107 | @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol 108 | 109 | #--------------------------------------------------------------------------------- 110 | run: 111 | wiiload $(TARGET).dol 112 | 113 | #--------------------------------------------------------------------------------- 114 | else 115 | 116 | DEPENDS := $(OFILES:.o=.d) 117 | 118 | #--------------------------------------------------------------------------------- 119 | # main targets 120 | #--------------------------------------------------------------------------------- 121 | $(OUTPUT).dol: $(OUTPUT).elf 122 | $(OUTPUT).elf: $(OFILES) 123 | 124 | #--------------------------------------------------------------------------------- 125 | %.dol: %.elf 126 | @echo output ... $(notdir $@) 127 | @elf2dol $< $@ 128 | 129 | %.bin.o: %.bin 130 | @echo $(notdir $<) 131 | @bin2s $< | $(AS) -o $@ 132 | 133 | -include $(DEPENDS) 134 | 135 | #--------------------------------------------------------------------------------- 136 | endif 137 | #--------------------------------------------------------------------------------- 138 | -------------------------------------------------------------------------------- /Makefile.wii: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | # Clear the implicit built in rules 3 | #--------------------------------------------------------------------------------- 4 | .SUFFIXES: 5 | #--------------------------------------------------------------------------------- 6 | ifeq ($(strip $(DEVKITRICE)),) 7 | $(error "Please set DEVKITRICE in your environment. export DEVKITRICE=devkitRice") 8 | endif 9 | 10 | include $(DEVKITRICE)/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 | # INCLUDES is a list of directories containing extra header files 17 | #--------------------------------------------------------------------------------- 18 | TARGET := emgba 19 | BUILD := build 20 | SOURCES := source 21 | DATA := data 22 | INCLUDES := mgba/src \ 23 | mgba/src/arm \ 24 | mgba/include 25 | 26 | #--------------------------------------------------------------------------------- 27 | # options for code generation 28 | #--------------------------------------------------------------------------------- 29 | 30 | CFLAGS = -g -Ofast -std=gnu99 -Wall -Wno-multichar -mrvl -flto=jobserver $(INCLUDE) 31 | CXXFLAGS = $(CFLAGS) 32 | 33 | LDFLAGS = $(CFLAGS) -Wl,-Map,$(notdir $@).map 34 | 35 | #--------------------------------------------------------------------------------- 36 | # any extra libraries we wish to link with the project 37 | #--------------------------------------------------------------------------------- 38 | LIBS := -lpng -lz -lfat -lwiiuse -lbte -lasnd -static 39 | 40 | #--------------------------------------------------------------------------------- 41 | # list of directories containing libraries, this must be the top level containing 42 | # include and lib 43 | #--------------------------------------------------------------------------------- 44 | LIBDIRS := 45 | 46 | #--------------------------------------------------------------------------------- 47 | # no real need to edit anything past this point unless you need to add additional 48 | # rules for different file extensions 49 | #--------------------------------------------------------------------------------- 50 | ifneq ($(BUILD),$(notdir $(CURDIR))) 51 | #--------------------------------------------------------------------------------- 52 | 53 | export OUTPUT := $(CURDIR)/$(TARGET) 54 | 55 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 56 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 57 | 58 | export DEPSDIR := $(CURDIR)/$(BUILD) 59 | 60 | #--------------------------------------------------------------------------------- 61 | # automatically build a list of object files for our project 62 | #--------------------------------------------------------------------------------- 63 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 64 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 65 | sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 66 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) 67 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 68 | 69 | #--------------------------------------------------------------------------------- 70 | # use CXX for linking C++ projects, CC for standard C 71 | #--------------------------------------------------------------------------------- 72 | ifeq ($(strip $(CPPFILES)),) 73 | export LD := $(CC) 74 | else 75 | export LD := $(CXX) 76 | endif 77 | 78 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 79 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ 80 | $(sFILES:.s=.o) $(SFILES:.S=.o) 81 | 82 | #--------------------------------------------------------------------------------- 83 | # build a list of include paths 84 | #--------------------------------------------------------------------------------- 85 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 86 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 87 | -I$(CURDIR)/$(BUILD) 88 | 89 | #--------------------------------------------------------------------------------- 90 | # build a list of library paths 91 | #--------------------------------------------------------------------------------- 92 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ 93 | $(CURDIR)/mgba/libmgba.a 94 | 95 | export OUTPUT := $(CURDIR)/$(TARGET) 96 | .PHONY: $(BUILD) clean 97 | 98 | #--------------------------------------------------------------------------------- 99 | $(BUILD): 100 | @[ -d $@ ] || mkdir -p $@ 101 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile.wii 102 | 103 | #--------------------------------------------------------------------------------- 104 | clean: 105 | @echo clean ... 106 | @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol 107 | 108 | #--------------------------------------------------------------------------------- 109 | run: 110 | wiiload $(TARGET).dol 111 | 112 | #--------------------------------------------------------------------------------- 113 | else 114 | 115 | DEPENDS := $(OFILES:.o=.d) 116 | 117 | #--------------------------------------------------------------------------------- 118 | # main targets 119 | #--------------------------------------------------------------------------------- 120 | $(OUTPUT).dol: $(OUTPUT).elf 121 | $(OUTPUT).elf: $(OFILES) 122 | 123 | #--------------------------------------------------------------------------------- 124 | %.dol: %.elf 125 | @echo output ... $(notdir $@) 126 | @elf2dol $< $@ 127 | 128 | %.bin.o: %.bin 129 | @echo $(notdir $<) 130 | @bin2s $< | $(AS) -o $@ 131 | 132 | -include $(DEPENDS) 133 | 134 | #--------------------------------------------------------------------------------- 135 | endif 136 | #--------------------------------------------------------------------------------- 137 | -------------------------------------------------------------------------------- /emgba.cli.example: -------------------------------------------------------------------------------- 1 | --aspect=4:3 2 | --cursor=point-srgb.tpl.gz 3 | --overlay=frame-srgb.tpl.gz 4 | --dither=none 5 | --profile=srgb 6 | --contrast=.78125 7 | -- -------------------------------------------------------------------------------- /emgba.dcp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/extremscorner/emgba/5704fcb8781adf0302471bd95901461793289fa3/emgba.dcp -------------------------------------------------------------------------------- /source/3ds.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "3ds.h" 13 | #include "network.h" 14 | #include "state.h" 15 | 16 | static syswd_t alarm = SYS_WD_NULL; 17 | 18 | ctr_state_t ctr = { 19 | .sv.sd = INVALID_SOCKET, 20 | .sv.nonblock = 1, 21 | .sv.sin.sin_family = AF_INET, 22 | .sv.sin.sin_port = 15708, 23 | .sv.sin.sin_addr.s_addr = INADDR_ANY, 24 | .sv.sinlen = sizeof(struct sockaddr), 25 | 26 | .tv.tv_sec = 1, 27 | .tv.tv_nsec = 0, 28 | }; 29 | 30 | static void timeout_cb(syswd_t alarm, void *arg) 31 | { 32 | ctr.data.held = 0; 33 | ctr.data.touch.x = 0; 34 | ctr.data.touch.y = 0; 35 | ctr.data.stick.x = 0; 36 | ctr.data.stick.y = 0; 37 | ctr.data.substick.x = 0; 38 | ctr.data.substick.y = 0; 39 | ctr.data.gyro.x = 0; 40 | ctr.data.gyro.y = 0; 41 | ctr.data.gyro.z = 0; 42 | ctr.data.gforce.x = 0; 43 | ctr.data.gforce.y = 0; 44 | ctr.data.gforce.z = 0; 45 | } 46 | 47 | void CTRScanPads(void) 48 | { 49 | ctr_packet_t packet = {0}; 50 | ctr.data.last = ctr.data.held; 51 | 52 | while (ctr.sv.sd != INVALID_SOCKET) { 53 | int ret = net_read(ctr.sv.sd, &packet, sizeof(packet)); 54 | if (ret < 0) break; 55 | if (packet.magic == 0x3D5C) { 56 | SYS_CancelAlarm(alarm); 57 | ctr.data.held = packet.held; 58 | ctr.data.touch.x = packet.touch.x; 59 | ctr.data.touch.y = packet.touch.y; 60 | ctr.data.stick.x = packet.stick.x; 61 | ctr.data.stick.y = packet.stick.y; 62 | ctr.data.substick.x = packet.substick.x; 63 | ctr.data.substick.y = packet.substick.y; 64 | ctr.data.gyro.x = -packet.gyro.x / 14.375; 65 | ctr.data.gyro.y = -packet.gyro.y / 14.375; 66 | ctr.data.gyro.z = -packet.gyro.z / 14.375; 67 | ctr.data.gforce.x = -packet.accel.x / 512.; 68 | ctr.data.gforce.y = -packet.accel.y / 512.; 69 | ctr.data.gforce.z = -packet.accel.z / 512.; 70 | SYS_SetAlarm(alarm, &ctr.tv, timeout_cb, NULL); 71 | } 72 | } 73 | 74 | ctr.data.down = ctr.data.held & ~ctr.data.last; 75 | ctr.data.up = ~ctr.data.held & ctr.data.last; 76 | 77 | if (ctr.data.gforce.z >= -1. && 78 | ctr.data.gforce.z <= 1.) { 79 | if (ctr.data.gforce.x >= -1. && 80 | ctr.data.gforce.x <= 1.) 81 | ctr.data.orient.roll = atan2f(ctr.data.gforce.x, ctr.data.gforce.z); 82 | if (ctr.data.gforce.y >= -1. && 83 | ctr.data.gforce.y <= 1.) 84 | ctr.data.orient.pitch = atan2f(ctr.data.gforce.y, ctr.data.gforce.z); 85 | } 86 | } 87 | 88 | bool CTRInit(void) 89 | { 90 | ctr.sv.sd = net_socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); 91 | 92 | if (ctr.sv.sd == INVALID_SOCKET) 93 | goto fail; 94 | if (net_ioctl(ctr.sv.sd, FIONBIO, &ctr.sv.nonblock) < 0) 95 | goto fail; 96 | if (net_bind(ctr.sv.sd, (struct sockaddr *)&ctr.sv.sin, ctr.sv.sinlen) < 0) 97 | goto fail; 98 | 99 | SYS_CreateAlarm(&alarm); 100 | return true; 101 | 102 | fail: 103 | net_close(ctr.sv.sd); 104 | ctr.sv.sd = INVALID_SOCKET; 105 | return false; 106 | } 107 | -------------------------------------------------------------------------------- /source/3ds.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef GBI_3DS_H 10 | #define GBI_3DS_H 11 | 12 | #include 13 | 14 | #define CTR_BUTTON_A 0x00000001 15 | #define CTR_BUTTON_B 0x00000002 16 | #define CTR_BUTTON_SELECT 0x00000004 17 | #define CTR_BUTTON_START 0x00000008 18 | #define CTR_BUTTON_RIGHT 0x00000010 19 | #define CTR_BUTTON_LEFT 0x00000020 20 | #define CTR_BUTTON_UP 0x00000040 21 | #define CTR_BUTTON_DOWN 0x00000080 22 | #define CTR_BUTTON_R 0x00000100 23 | #define CTR_BUTTON_L 0x00000200 24 | #define CTR_BUTTON_X 0x00000400 25 | #define CTR_BUTTON_Y 0x00000800 26 | #define CTR_BUTTON_ZL 0x00004000 27 | #define CTR_BUTTON_ZR 0x00008000 28 | 29 | #define CTR_TOUCH 0x00100000 30 | 31 | #define CTR_SUBSTICK_RIGHT 0x01000000 32 | #define CTR_SUBSTICK_LEFT 0x02000000 33 | #define CTR_SUBSTICK_UP 0x04000000 34 | #define CTR_SUBSTICK_DOWN 0x08000000 35 | #define CTR_STICK_RIGHT 0x10000000 36 | #define CTR_STICK_LEFT 0x20000000 37 | #define CTR_STICK_UP 0x40000000 38 | #define CTR_STICK_DOWN 0x80000000 39 | 40 | typedef struct { 41 | struct { 42 | int sd; 43 | int nonblock; 44 | struct sockaddr_in sin; 45 | socklen_t sinlen; 46 | } sv; 47 | 48 | struct timespec tv; 49 | 50 | struct { 51 | uint32_t held, last, down, up; 52 | struct { uint16_t x, y; } touch; 53 | struct { int16_t x, y; } stick; 54 | struct { int16_t x, y; } substick; 55 | struct { float x, y, z; } gyro; 56 | struct { float x, y, z; } gforce; 57 | struct { float roll, pitch; } orient; 58 | } data; 59 | } ctr_state_t; 60 | 61 | extern ctr_state_t ctr; 62 | 63 | typedef struct { 64 | uint16_t magic; 65 | uint16_t version; 66 | uint32_t up, down, held; 67 | struct { uint16_t x, y; } touch; 68 | struct { int16_t x, y; } stick; 69 | struct { int16_t x, y; } substick; 70 | struct { int16_t x, y, z; } gyro; 71 | struct { int16_t x, y, z; } accel; 72 | } ATTRIBUTE_PACKED ctr_packet_t; 73 | 74 | void CTRScanPads(void); 75 | bool CTRInit(void); 76 | 77 | #endif /* GBI_3DS_H */ 78 | -------------------------------------------------------------------------------- /source/clock.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include "clock.h" 11 | 12 | void ClockTick(timing_t *clock, uint32_t step) 13 | { 14 | uint64_t diff, curr; 15 | 16 | curr = __SYS_GetSystemTime(); 17 | diff = diff_ticks(clock->start, curr); 18 | 19 | if (clock->reset) { 20 | clock->reset = false; 21 | clock->count = 0; 22 | clock->time = clock->start = curr; 23 | clock->delta = 0; 24 | } else { 25 | clock->delta = diff_ticks(clock->time, curr); 26 | clock->time = curr; 27 | clock->count += step; 28 | 29 | if (diff >= secs_to_ticks(1)) { 30 | clock->hz = (double)secs_to_ticks(1) * clock->count / diff; 31 | clock->start = curr; 32 | clock->count = 0; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /source/clock.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef GBI_CLOCK_H 10 | #define GBI_CLOCK_H 11 | 12 | #include 13 | 14 | typedef struct { 15 | bool reset; 16 | uint32_t count; 17 | uint64_t start; 18 | uint64_t time; 19 | uint64_t delta; 20 | double hz; 21 | } timing_t; 22 | 23 | void ClockTick(timing_t *clock, uint32_t step); 24 | 25 | #endif /* GBI_CLOCK_H */ 26 | -------------------------------------------------------------------------------- /source/gba.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "gba.h" 13 | #include "gba_mb.h" 14 | #include "input.h" 15 | #include "state.h" 16 | 17 | static lwpq_t queue = LWP_TQUEUE_NULL; 18 | static lwp_t thread = LWP_THREAD_NULL; 19 | 20 | static void transfer_cb(int32_t chan, uint32_t type) 21 | { 22 | LWP_ThreadSignal(queue); 23 | } 24 | 25 | uint32_t GBAResetCommand(int32_t chan) 26 | { 27 | uint32_t level; 28 | uint8_t outbuf[1] = {0xFF}; 29 | uint8_t inbuf[3] = {0x00}; 30 | 31 | _CPU_ISR_Disable(level); 32 | 33 | if (SI_Transfer(chan, outbuf, 1, inbuf, 3, transfer_cb, 65)) 34 | LWP_ThreadSleep(queue); 35 | 36 | _CPU_ISR_Restore(level); 37 | 38 | return inbuf[0] << 24 | inbuf[1] << 16 | inbuf[2] << 8; 39 | } 40 | 41 | uint32_t GBAStatusCommand(int32_t chan) 42 | { 43 | uint32_t level; 44 | uint8_t outbuf[1] = {0x00}; 45 | uint8_t inbuf[3] = {0x00}; 46 | 47 | _CPU_ISR_Disable(level); 48 | 49 | if (SI_Transfer(chan, outbuf, 1, inbuf, 3, transfer_cb, 65)) 50 | LWP_ThreadSleep(queue); 51 | 52 | _CPU_ISR_Restore(level); 53 | 54 | return inbuf[0] << 24 | inbuf[1] << 16 | inbuf[2] << 8; 55 | } 56 | 57 | uint32_t GBAReadCommand(int32_t chan) 58 | { 59 | uint32_t level; 60 | uint8_t outbuf[1] = {0x14}; 61 | uint8_t inbuf[5] = {0x00}; 62 | 63 | _CPU_ISR_Disable(level); 64 | 65 | if (SI_Transfer(chan, outbuf, 1, inbuf, 5, transfer_cb, 65)) 66 | LWP_ThreadSleep(queue); 67 | 68 | _CPU_ISR_Restore(level); 69 | 70 | return inbuf[3] << 24 | inbuf[2] << 16 | inbuf[1] << 8 | inbuf[0]; 71 | } 72 | 73 | void GBAWriteCommand(int32_t chan, uint32_t val) 74 | { 75 | uint32_t level; 76 | uint8_t outbuf[5] = {0x15, val, val >> 8, val >> 16, val >> 24}; 77 | uint8_t inbuf[1] = {0x00}; 78 | 79 | _CPU_ISR_Disable(level); 80 | 81 | if (SI_Transfer(chan, outbuf, 5, inbuf, 1, transfer_cb, 65)) 82 | LWP_ThreadSleep(queue); 83 | 84 | _CPU_ISR_Restore(level); 85 | } 86 | 87 | static uint32_t GBAChecksum(uint32_t crc, uint32_t val) 88 | { 89 | crc ^= val; 90 | 91 | for (int bit = 0; bit < 32; bit++) 92 | crc = crc & 1 ? crc >> 1 ^ 0xA1C1 : crc >> 1; 93 | 94 | return crc; 95 | } 96 | 97 | static uint32_t GBAEncrypt(uint32_t addr, uint32_t val, uint32_t *key) 98 | { 99 | *key = *key * bswap32('Kawa') + 1; 100 | val ^= *key; 101 | val ^= -addr; 102 | val ^= bswap32(' by '); 103 | return val; 104 | } 105 | 106 | static uint32_t GBAGetKey(uint32_t size) 107 | { 108 | uint32_t key; 109 | key = (size - 0x200) >> 3; 110 | key = (key & 0x7F) | (key & 0x3F80) << 1 | (key & 0x4000) << 2 | 0x700000; 111 | key |= (key + (key >> 8) + (key >> 16)) << 24 | 0x80808080; 112 | 113 | if ((key & 0x200) == 0x200) 114 | return key ^ bswap32('sedo'); 115 | else 116 | return key ^ bswap32('Kawa'); 117 | } 118 | 119 | static void *thread_func(void *arg) 120 | { 121 | do { 122 | uint32_t type, reset = 0; 123 | 124 | for (int chan = 0; chan < SI_MAX_CHAN; chan++) { 125 | switch (SI_Probe(chan)) { 126 | case SI_GC_CONTROLLER: 127 | case SI_GC_WAVEBIRD: 128 | if (gc_controller.status[chan].err == PAD_ERR_NO_CONTROLLER) { 129 | gc_controller.status[chan].err = PAD_ERR_NOT_READY; 130 | reset |= SI_CHAN_BIT(chan); 131 | } 132 | break; 133 | case SI_GC_STEERING: 134 | if (gc_steering.status[chan].err == SI_STEERING_ERR_NO_CONTROLLER) 135 | gc_steering.status[chan].err = SI_ResetSteering(chan); 136 | break; 137 | case SI_N64_CONTROLLER: 138 | N64_ReadAsync(chan, &n64_controller.status[chan], NULL); 139 | break; 140 | case SI_GBA: 141 | type = GBAResetCommand(chan); 142 | type = GBAStatusCommand(chan); 143 | 144 | if ((type & 0x3000) == 0x1000) { 145 | uint32_t off, size = gba_mb_size < 0x200 ? 0x200 : (gba_mb_size + 7) & ~7; 146 | uint32_t crc = 0x15A0, key = bswap32('sedo'), val; 147 | 148 | key ^= GBAReadCommand(chan); 149 | GBAWriteCommand(chan, GBAGetKey(size)); 150 | 151 | for (off = 0; off < 0xC0; off += 4) { 152 | val = __lwbrx(gba_mb, off); 153 | GBAWriteCommand(chan, val); 154 | } 155 | 156 | for (off = 0xC0; off < size; off += 4) { 157 | val = __lwbrx(gba_mb, off); 158 | crc = GBAChecksum(crc, val); 159 | val = GBAEncrypt(0x02000000 + off, val, &key); 160 | GBAWriteCommand(chan, val); 161 | } 162 | 163 | crc |= size << 16; 164 | crc = GBAEncrypt(0x02000000 + off, crc, &key); 165 | GBAWriteCommand(chan, crc); 166 | GBAReadCommand(chan); 167 | } 168 | default: 169 | gc_controller.status[chan].err = PAD_ERR_NO_CONTROLLER; 170 | gc_steering.status[chan].err = SI_STEERING_ERR_NO_CONTROLLER; 171 | n64_controller.status[chan].err = N64_ERR_NO_CONTROLLER; 172 | break; 173 | } 174 | } 175 | 176 | if (reset) PAD_Reset(reset); 177 | VIDEO_WaitVSync(); 178 | } while (!state.quit); 179 | 180 | return NULL; 181 | } 182 | 183 | void GBAInit(void) 184 | { 185 | LWP_InitQueue(&queue); 186 | LWP_CreateThread(&thread, thread_func, NULL, NULL, 0, LWP_PRIO_NORMAL); 187 | } 188 | -------------------------------------------------------------------------------- /source/gba.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef GBI_GBA_H 10 | #define GBI_GBA_H 11 | 12 | #include 13 | 14 | #define GBA_BUTTON_A 0x0100 15 | #define GBA_BUTTON_B 0x0200 16 | #define GBA_BUTTON_SELECT 0x0400 17 | #define GBA_BUTTON_START 0x0800 18 | #define GBA_BUTTON_RIGHT 0x1000 19 | #define GBA_BUTTON_LEFT 0x2000 20 | #define GBA_BUTTON_UP 0x4000 21 | #define GBA_BUTTON_DOWN 0x8000 22 | #define GBA_BUTTON_R 0x0001 23 | #define GBA_BUTTON_L 0x0002 24 | #define GBA_BUTTON_RESET 0x0004 25 | 26 | uint32_t GBAResetCommand(int32_t chan); 27 | uint32_t GBAStatusCommand(int32_t chan); 28 | uint32_t GBAReadCommand(int32_t chan); 29 | void GBAWriteCommand(int32_t chan, uint32_t val); 30 | void GBAInit(void); 31 | 32 | void GBAVideoConvertBGR5(void *dst, void *src, int width, int height); 33 | 34 | #endif /* GBI_GBA_H */ 35 | -------------------------------------------------------------------------------- /source/gba_mb.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file was autogenerated by raw2c. 3 | Visit http://www.devkitpro.org 4 | */ 5 | 6 | const unsigned char gba_mb[] = { 7 | 0x2e, 0x00, 0x00, 0xea, 0x24, 0xff, 0xae, 0x51, 0x69, 0x9a, 0xa2, 0x21, 0x3d, 0x84, 0x82, 0x0a, 8 | 0x84, 0xe4, 0x09, 0xad, 0x11, 0x24, 0x8b, 0x98, 0xc0, 0x81, 0x7f, 0x21, 0xa3, 0x52, 0xbe, 0x19, 9 | 0x93, 0x09, 0xce, 0x20, 0x10, 0x46, 0x4a, 0x4a, 0xf8, 0x27, 0x31, 0xec, 0x58, 0xc7, 0xe8, 0x33, 10 | 0x82, 0xe3, 0xce, 0xbf, 0x85, 0xf4, 0xdf, 0x94, 0xce, 0x4b, 0x09, 0xc1, 0x94, 0x56, 0x8a, 0xc0, 11 | 0x13, 0x72, 0xa7, 0xfc, 0x9f, 0x84, 0x4d, 0x73, 0xa3, 0xca, 0x9a, 0x61, 0x58, 0x97, 0xa3, 0x27, 12 | 0xfc, 0x03, 0x98, 0x76, 0x23, 0x1d, 0xc7, 0x61, 0x03, 0x04, 0xae, 0x56, 0xbf, 0x38, 0x84, 0x00, 13 | 0x40, 0xa7, 0x0e, 0xfd, 0xff, 0x52, 0xfe, 0x03, 0x6f, 0x95, 0x30, 0xf1, 0x97, 0xfb, 0xc0, 0x85, 14 | 0x60, 0xd6, 0x80, 0x25, 0xa9, 0x63, 0xbe, 0x03, 0x01, 0x4e, 0x38, 0xe2, 0xf9, 0xa2, 0x34, 0xff, 15 | 0xbb, 0x3e, 0x03, 0x44, 0x78, 0x00, 0x90, 0xcb, 0x88, 0x11, 0x3a, 0x94, 0x65, 0xc0, 0x7c, 0x63, 16 | 0x87, 0xf0, 0x3c, 0xaf, 0xd6, 0x25, 0xe4, 0x8b, 0x38, 0x0a, 0xac, 0x72, 0x21, 0xd4, 0xf8, 0x07, 17 | 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x4f, 0x4c, 0x4c, 0x45, 0x52, 0x00, 0x00, 0x56, 0x47, 0x42, 0x41, 18 | 0x45, 0x43, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x9b, 0x00, 0x00, 19 | 0x06, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 20 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 21 | 0x01, 0x03, 0xa0, 0xe3, 0x08, 0x02, 0x80, 0xe5, 0x12, 0x00, 0xa0, 0xe3, 0x00, 0xf0, 0x29, 0xe1, 22 | 0xb8, 0xd0, 0x9f, 0xe5, 0x1f, 0x00, 0xa0, 0xe3, 0x00, 0xf0, 0x29, 0xe1, 0xb0, 0xd0, 0x9f, 0xe5, 23 | 0x01, 0x00, 0x8f, 0xe2, 0x10, 0xff, 0x2f, 0xe1, 0x2b, 0x48, 0x40, 0x01, 0x0b, 0xd2, 0x78, 0x46, 24 | 0x40, 0x01, 0x0d, 0xd3, 0x02, 0x22, 0x12, 0x06, 0x28, 0x4b, 0x9b, 0x1a, 0x16, 0x1c, 0x91, 0x00, 25 | 0x00, 0xf0, 0x3d, 0xf8, 0x30, 0x47, 0x40, 0x21, 0x09, 0x03, 0xc8, 0x01, 0x00, 0xf0, 0x2c, 0xf8, 26 | 0x23, 0x48, 0x24, 0x49, 0x09, 0x1a, 0x00, 0xf0, 0x27, 0xf8, 0x23, 0x48, 0x23, 0x49, 0x09, 0x1a, 27 | 0x00, 0xf0, 0x22, 0xf8, 0x22, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x00, 0xf0, 0x27, 0xf8, 0x23, 0x49, 28 | 0x23, 0x4a, 0x24, 0x4c, 0x00, 0xf0, 0x22, 0xf8, 0x23, 0x4a, 0x24, 0x49, 0x53, 0x1a, 0x02, 0xd0, 29 | 0x23, 0x4a, 0x00, 0xf0, 0x1c, 0xf8, 0x23, 0x49, 0x23, 0x4a, 0x24, 0x4c, 0x00, 0xf0, 0x16, 0xf8, 30 | 0x23, 0x49, 0x24, 0x48, 0x08, 0x60, 0x24, 0x4b, 0x00, 0xf0, 0x0f, 0xf8, 0x00, 0x20, 0x00, 0x21, 31 | 0x22, 0x4b, 0x00, 0xf0, 0x0a, 0xf8, 0x00, 0xdf, 0x03, 0x22, 0x89, 0x18, 0x91, 0x43, 0x03, 0xd0, 32 | 0x00, 0x22, 0x04, 0xc0, 0x04, 0x39, 0xfc, 0xd1, 0x70, 0x47, 0x18, 0x47, 0xa3, 0x1a, 0x03, 0x20, 33 | 0x1b, 0x18, 0x83, 0x43, 0x03, 0xd0, 0x01, 0xc9, 0x01, 0xc2, 0x04, 0x3b, 0xfb, 0xd1, 0x70, 0x47, 34 | 0xa0, 0x7f, 0x00, 0x03, 0x00, 0x7f, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0xe4, 0x0b, 0x00, 0x02, 35 | 0xf4, 0x08, 0x00, 0x03, 0xa4, 0x09, 0x00, 0x03, 0xe4, 0x0b, 0x00, 0x02, 0xe4, 0x0b, 0x00, 0x02, 36 | 0xd8, 0x0b, 0x00, 0x02, 0xa4, 0x09, 0x00, 0x03, 0xb0, 0x09, 0x00, 0x03, 0xe4, 0x02, 0x00, 0x02, 37 | 0x00, 0x00, 0x00, 0x03, 0xf4, 0x08, 0x00, 0x03, 0xe4, 0x0b, 0x00, 0x02, 0xe4, 0x0b, 0x00, 0x02, 38 | 0xb0, 0x09, 0x00, 0x03, 0xe4, 0x0b, 0x00, 0x02, 0xe4, 0x0b, 0x00, 0x02, 0xe4, 0x0b, 0x00, 0x02, 39 | 0xa0, 0x09, 0x00, 0x03, 0x00, 0x00, 0x04, 0x02, 0x6d, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 40 | 0xf8, 0xb5, 0xc0, 0x46, 0xf8, 0xbc, 0x08, 0xbc, 0x9e, 0x46, 0x70, 0x47, 0x10, 0xb5, 0x07, 0x4c, 41 | 0x23, 0x78, 0x00, 0x2b, 0x07, 0xd1, 0x06, 0x4b, 0x00, 0x2b, 0x02, 0xd0, 0x05, 0x48, 0x00, 0xe0, 42 | 0x00, 0xbf, 0x01, 0x23, 0x23, 0x70, 0x10, 0xbc, 0x01, 0xbc, 0x00, 0x47, 0xf4, 0x08, 0x00, 0x03, 43 | 0x00, 0x00, 0x00, 0x00, 0xe0, 0x02, 0x00, 0x02, 0x05, 0x4b, 0x10, 0xb5, 0x00, 0x2b, 0x03, 0xd0, 44 | 0x04, 0x49, 0x05, 0x48, 0x00, 0xe0, 0x00, 0xbf, 0x10, 0xbc, 0x01, 0xbc, 0x00, 0x47, 0xc0, 0x46, 45 | 0x00, 0x00, 0x00, 0x00, 0xf8, 0x08, 0x00, 0x03, 0xe0, 0x02, 0x00, 0x02, 0x70, 0xb5, 0x0f, 0x4b, 46 | 0x0f, 0x4d, 0x5e, 0x1b, 0xb6, 0x10, 0xab, 0x42, 0x06, 0xd0, 0x00, 0x24, 0x08, 0xcd, 0x01, 0x34, 47 | 0x00, 0xf0, 0x1c, 0xf8, 0xa6, 0x42, 0xf9, 0xd8, 0xff, 0xf7, 0xc2, 0xff, 0x09, 0x4b, 0x0a, 0x4d, 48 | 0x5e, 0x1b, 0xb6, 0x10, 0xab, 0x42, 0x06, 0xd0, 0x00, 0x24, 0x08, 0xcd, 0x01, 0x34, 0x00, 0xf0, 49 | 0x0d, 0xf8, 0xa6, 0x42, 0xf9, 0xd8, 0x70, 0xbc, 0x01, 0xbc, 0x00, 0x47, 0xd8, 0x02, 0x00, 0x02, 50 | 0xd8, 0x02, 0x00, 0x02, 0xdc, 0x02, 0x00, 0x02, 0xd8, 0x02, 0x00, 0x02, 0x18, 0x47, 0xc0, 0x46, 51 | 0xf8, 0xb5, 0xc0, 0x46, 0xf8, 0xbc, 0x08, 0xbc, 0x9e, 0x46, 0x70, 0x47, 0x29, 0x00, 0x34, 0x14, 52 | 0x08, 0x04, 0x00, 0x55, 0x3e, 0x34, 0x28, 0xff, 0x49, 0x02, 0x00, 0x02, 0x1d, 0x02, 0x00, 0x02, 53 | 0x00, 0x00, 0x00, 0x00, 0xf8, 0x4f, 0x2d, 0xe9, 0xe0, 0x00, 0xa0, 0xe3, 0x00, 0x00, 0x01, 0xef, 54 | 0xb8, 0x10, 0xa0, 0xe3, 0x42, 0x40, 0xe0, 0xe3, 0xc4, 0xe0, 0xa0, 0xe3, 0x80, 0xc0, 0xa0, 0xe3, 55 | 0x8c, 0x25, 0x9f, 0xe5, 0xb0, 0x10, 0xc2, 0xe1, 0x88, 0x35, 0x9f, 0xe5, 0xb2, 0x10, 0xd2, 0xe1, 56 | 0x84, 0x55, 0x9f, 0xe5, 0xb2, 0x10, 0xc2, 0xe1, 0x00, 0x00, 0xa0, 0xe3, 0xb4, 0x53, 0xc3, 0xe1, 57 | 0xb0, 0x40, 0xc3, 0xe1, 0xb6, 0xe0, 0xc3, 0xe1, 0xb2, 0xc0, 0xc3, 0xe1, 0x00, 0x00, 0x19, 0xef, 58 | 0x0f, 0xe0, 0xa0, 0xe1, 0x1a, 0xfe, 0xa0, 0xe3, 0x60, 0x45, 0x9f, 0xe5, 0x60, 0x55, 0x9f, 0xe5, 59 | 0x60, 0x75, 0x9f, 0xe5, 0x4c, 0x65, 0x9f, 0xe5, 0x04, 0x00, 0xa0, 0xe1, 0x58, 0x15, 0x9f, 0xe5, 60 | 0x0f, 0xe0, 0xa0, 0xe1, 0x15, 0xff, 0x2f, 0xe1, 0x08, 0x00, 0x50, 0xe3, 0xf9, 0xff, 0xff, 0xda, 61 | 0xb0, 0x23, 0xd6, 0xe1, 0x02, 0x20, 0xe0, 0xe1, 0x01, 0x80, 0xd7, 0xe5, 0x22, 0xa1, 0xa0, 0xe1, 62 | 0x01, 0xa0, 0x0a, 0xe2, 0x10, 0x80, 0xc8, 0xe3, 0x00, 0x30, 0xd7, 0xe5, 0x0a, 0x82, 0x88, 0xe1, 63 | 0x02, 0xbd, 0xa0, 0xe1, 0xa2, 0xc1, 0xa0, 0xe1, 0xfc, 0x80, 0x08, 0xe2, 0x01, 0xe0, 0x02, 0xe2, 64 | 0x02, 0xc0, 0x0c, 0xe2, 0xab, 0x8f, 0x88, 0xe1, 0xfc, 0x30, 0x03, 0xe2, 0x0c, 0x80, 0x88, 0xe1, 65 | 0x0e, 0x30, 0x83, 0xe1, 0x02, 0xc0, 0x02, 0xe2, 0x0c, 0x30, 0x83, 0xe1, 0x40, 0x30, 0xc3, 0xe3, 66 | 0x0a, 0x33, 0x83, 0xe1, 0xef, 0xb0, 0x03, 0xe2, 0xa2, 0x31, 0xa0, 0xe1, 0xf3, 0x80, 0x08, 0xe2, 67 | 0x08, 0x30, 0x03, 0xe2, 0x03, 0x30, 0x88, 0xe1, 0xa2, 0x82, 0xa0, 0xe1, 0x04, 0x80, 0x08, 0xe2, 68 | 0x08, 0x30, 0x83, 0xe1, 0x82, 0x80, 0xa0, 0xe1, 0x82, 0x90, 0xd4, 0xe5, 0x10, 0x80, 0x08, 0xe2, 69 | 0x08, 0x80, 0x8b, 0xe1, 0x00, 0x80, 0xc7, 0xe5, 0x9f, 0x90, 0x09, 0xe2, 0x20, 0x80, 0x08, 0xe2, 70 | 0x08, 0x90, 0x89, 0xe1, 0x20, 0x30, 0xc3, 0xe3, 0x01, 0x8c, 0x02, 0xe2, 0x00, 0x10, 0xd4, 0xe5, 71 | 0xa8, 0x31, 0x83, 0xe1, 0x02, 0x2c, 0x02, 0xe2, 0x40, 0x30, 0xc3, 0xe3, 0x0a, 0x93, 0x89, 0xe1, 72 | 0xa2, 0x31, 0x83, 0xe1, 0x43, 0x00, 0x51, 0xe3, 0x82, 0x90, 0xc4, 0xe5, 0x01, 0x30, 0xc7, 0xe5, 73 | 0x7e, 0x00, 0x00, 0x8a, 0x3f, 0x00, 0x51, 0xe3, 0x63, 0x00, 0x00, 0x9a, 0x7c, 0x34, 0x9f, 0xe5, 74 | 0x41, 0x10, 0x41, 0xe2, 0x02, 0x00, 0x51, 0xe3, 0x93, 0x00, 0x00, 0x8a, 0x01, 0x10, 0xd3, 0xe7, 75 | 0x01, 0xf1, 0x8f, 0xe0, 0x00, 0x00, 0xa0, 0xe1, 0x19, 0x00, 0x50, 0xe3, 0x28, 0x00, 0x00, 0x0a, 76 | 0x82, 0x20, 0xd4, 0xe5, 0x18, 0x20, 0x02, 0xe2, 0x08, 0x30, 0x42, 0xe2, 0x00, 0x10, 0x73, 0xe2, 77 | 0x03, 0x10, 0xa1, 0xe0, 0x83, 0x30, 0xd4, 0xe5, 0x44, 0x04, 0x9f, 0xe5, 0x01, 0x30, 0x43, 0xe2, 78 | 0x03, 0x00, 0x53, 0xe3, 0xb3, 0xff, 0xff, 0x8a, 0x03, 0x30, 0xd0, 0xe7, 0x03, 0xf1, 0x8f, 0xe0, 79 | 0x00, 0x00, 0xa0, 0xe1, 0x02, 0x33, 0xa0, 0xe3, 0x01, 0x14, 0xa0, 0xe1, 0xb0, 0x10, 0xc3, 0xe1, 80 | 0xab, 0xff, 0xff, 0xea, 0x1c, 0x34, 0x9f, 0xe5, 0x81, 0x10, 0xa0, 0xe1, 0xb0, 0x10, 0xc3, 0xe1, 81 | 0xa7, 0xff, 0xff, 0xea, 0x08, 0x00, 0x52, 0xe3, 0x01, 0x23, 0xa0, 0x13, 0x01, 0x33, 0xa0, 0x03, 82 | 0x0e, 0x24, 0xa0, 0x03, 0xdc, 0x30, 0x92, 0x15, 0xfc, 0x13, 0x9f, 0x05, 0x02, 0x34, 0xc3, 0x13, 83 | 0xd4, 0x20, 0x83, 0x05, 0xdc, 0x30, 0x82, 0x15, 0xd8, 0x20, 0x83, 0x05, 0xdc, 0x10, 0x83, 0x05, 84 | 0x9b, 0xff, 0xff, 0xea, 0x02, 0x33, 0xa0, 0xe3, 0x08, 0x20, 0xa0, 0xe3, 0x81, 0x11, 0xa0, 0xe1, 85 | 0xb6, 0x2c, 0xc3, 0xe1, 0xb4, 0x1c, 0xc3, 0xe1, 0x95, 0xff, 0xff, 0xea, 0x09, 0x00, 0x50, 0xe3, 86 | 0xd6, 0xff, 0xff, 0x1a, 0x00, 0x30, 0xd7, 0xe5, 0x20, 0x30, 0xc3, 0xe3, 0x00, 0x30, 0xc7, 0xe5, 87 | 0x50, 0x10, 0xa0, 0xe3, 0x07, 0x00, 0xa0, 0xe1, 0xb0, 0x33, 0x9f, 0xe5, 0x0f, 0xe0, 0xa0, 0xe1, 88 | 0x13, 0xff, 0x2f, 0xe1, 0xcd, 0xff, 0xff, 0xea, 0x19, 0x00, 0x50, 0xe3, 0xcb, 0xff, 0xff, 0x1a, 89 | 0x02, 0x30, 0xd4, 0xe5, 0x03, 0x30, 0x03, 0xe2, 0x03, 0x00, 0x53, 0xe3, 0x82, 0x10, 0xd4, 0x15, 90 | 0x18, 0x10, 0xc1, 0x13, 0x83, 0x31, 0x81, 0x11, 0x82, 0x30, 0xc4, 0x15, 0xb0, 0x30, 0xd7, 0xe1, 91 | 0xb4, 0x38, 0xc4, 0xe1, 0x02, 0x30, 0xd7, 0xe5, 0x86, 0x30, 0xc4, 0xe5, 0x03, 0x30, 0xd7, 0xe5, 92 | 0x87, 0x30, 0xc4, 0xe5, 0x04, 0x30, 0xd7, 0xe5, 0x88, 0x30, 0xc4, 0xe5, 0x05, 0x30, 0xd7, 0xe5, 93 | 0x00, 0x00, 0x52, 0xe3, 0x89, 0x30, 0xc4, 0xe5, 0xc8, 0x30, 0xa0, 0x13, 0x06, 0x30, 0xd7, 0x05, 94 | 0x00, 0x00, 0x58, 0xe3, 0x8a, 0x30, 0xc4, 0xe5, 0xc8, 0x30, 0xa0, 0x13, 0x07, 0x30, 0xd7, 0x05, 95 | 0x00, 0x00, 0x5e, 0xe3, 0x8b, 0x30, 0xc4, 0xe5, 0xc8, 0x30, 0xa0, 0x13, 0x08, 0x30, 0xd7, 0x05, 96 | 0x00, 0x00, 0x5c, 0xe3, 0x8c, 0x30, 0xc4, 0xe5, 0xc8, 0x30, 0xa0, 0x13, 0x09, 0x30, 0xd7, 0x05, 97 | 0x50, 0x10, 0xa0, 0xe3, 0x8d, 0x30, 0xc4, 0xe5, 0x14, 0x03, 0x9f, 0xe5, 0x0c, 0x33, 0x9f, 0xe5, 98 | 0x0f, 0xe0, 0xa0, 0xe1, 0x13, 0xff, 0x2f, 0xe1, 0xa4, 0xff, 0xff, 0xea, 0x00, 0x00, 0x51, 0xe3, 99 | 0xa2, 0xff, 0xff, 0x1a, 0x09, 0x00, 0x50, 0xe3, 0xa0, 0xff, 0xff, 0x1a, 0x02, 0x23, 0xa0, 0xe3, 100 | 0xb2, 0x3b, 0xd2, 0xe1, 0x59, 0x00, 0x53, 0xe3, 0x03, 0x38, 0xa0, 0xe1, 0x43, 0x38, 0xa0, 0xe1, 101 | 0x1c, 0x00, 0x00, 0x0a, 0x96, 0x00, 0x53, 0xe3, 0x12, 0x00, 0x00, 0x0a, 0x03, 0x00, 0x73, 0xe3, 102 | 0x15, 0x00, 0x00, 0x1a, 0x03, 0x20, 0xa0, 0xe3, 0x09, 0x3c, 0xa0, 0xe3, 0x23, 0x34, 0xa0, 0xe1, 103 | 0xb0, 0x38, 0xc4, 0xe1, 0x18, 0x10, 0xa0, 0xe3, 0xb8, 0x02, 0x9f, 0xe5, 0xac, 0x32, 0x9f, 0xe5, 104 | 0x83, 0x20, 0xc4, 0xe5, 0x0f, 0xe0, 0xa0, 0xe1, 0x13, 0xff, 0x2f, 0xe1, 0x8b, 0xff, 0xff, 0xea, 105 | 0xff, 0x00, 0x51, 0xe3, 0x89, 0xff, 0xff, 0x1a, 0x82, 0x30, 0xd4, 0xe5, 0x18, 0x30, 0xc3, 0xe3, 106 | 0x82, 0x30, 0xc4, 0xe5, 0xe2, 0xff, 0xff, 0xea, 0xbc, 0x3a, 0xd2, 0xe1, 0xfb, 0x30, 0x03, 0xe2, 107 | 0x52, 0x00, 0x53, 0xe3, 0x01, 0x20, 0xa0, 0x03, 0xea, 0xff, 0xff, 0x0a, 0x00, 0x20, 0xa0, 0xe3, 108 | 0x29, 0x3c, 0xa0, 0xe3, 0xe8, 0xff, 0xff, 0xea, 0xf6, 0x34, 0xe0, 0xe3, 0xb1, 0x30, 0x53, 0xe1, 109 | 0x64, 0x22, 0x9f, 0xe5, 0x03, 0x38, 0xa0, 0xe1, 0x43, 0x38, 0xa0, 0xe1, 0x02, 0x00, 0x53, 0xe1, 110 | 0x03, 0x00, 0x00, 0x0a, 0x03, 0x00, 0x73, 0xe3, 0xf3, 0xff, 0xff, 0x1a, 0x02, 0x20, 0xa0, 0xe3, 111 | 0xdc, 0xff, 0xff, 0xea, 0x04, 0x20, 0xa0, 0xe3, 0xda, 0xff, 0xff, 0xea, 0x19, 0x00, 0x50, 0xe3, 112 | 0x6e, 0xff, 0xff, 0x1a, 0x82, 0x30, 0xd4, 0xe5, 0x02, 0x00, 0xd4, 0xe5, 0x01, 0x10, 0xd4, 0xe5, 113 | 0x03, 0x00, 0x00, 0xe2, 0x07, 0x10, 0x01, 0xe2, 0x07, 0x30, 0xc3, 0xe3, 0x01, 0x30, 0x83, 0xe1, 114 | 0x03, 0x00, 0x50, 0xe3, 0x82, 0x30, 0xc4, 0xe5, 0xff, 0x30, 0x03, 0x12, 0x18, 0x30, 0xc3, 0x13, 115 | 0x80, 0x31, 0x83, 0x11, 0x82, 0x30, 0xc4, 0x15, 0xb0, 0x30, 0xd7, 0xe1, 0xb4, 0x38, 0xc4, 0xe1, 116 | 0x02, 0x30, 0xd7, 0xe5, 0xf4, 0x91, 0x9f, 0xe5, 0x86, 0x30, 0xc4, 0xe5, 0x03, 0x30, 0xd7, 0xe5, 117 | 0x01, 0x10, 0x41, 0xe2, 0x87, 0x30, 0xc4, 0xe5, 0x05, 0x00, 0xd7, 0xe5, 0x04, 0x30, 0xd7, 0xe5, 118 | 0x03, 0x00, 0x51, 0xe3, 0x02, 0x00, 0x00, 0x8a, 0x01, 0x10, 0xd9, 0xe7, 0x01, 0xf1, 0x8f, 0xe0, 119 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0x52, 0xe3, 0x0c, 0x20, 0xa0, 0x13, 0x88, 0x30, 0xc4, 0xe5, 120 | 0x89, 0x00, 0xc4, 0xe5, 0x8a, 0x30, 0xd4, 0xe5, 0x06, 0x20, 0xd7, 0x05, 0x0f, 0x30, 0x03, 0xe2, 121 | 0x22, 0x22, 0xa0, 0x01, 0x00, 0x00, 0x58, 0xe3, 0x02, 0x32, 0x83, 0xe1, 0x0c, 0x20, 0xa0, 0x13, 122 | 0x8a, 0x30, 0xc4, 0xe5, 0x07, 0x20, 0xd7, 0x05, 0x8a, 0x30, 0xd4, 0xe5, 0x22, 0x22, 0xa0, 0x01, 123 | 0x0f, 0x30, 0xc3, 0xe3, 0x02, 0x30, 0x83, 0xe1, 0x00, 0x00, 0x5e, 0xe3, 0x8a, 0x30, 0xc4, 0xe5, 124 | 0x4d, 0x00, 0x00, 0x1a, 0x08, 0x20, 0xd7, 0xe5, 0x22, 0x22, 0xa0, 0xe1, 0x8b, 0x30, 0xd4, 0xe5, 125 | 0x00, 0x00, 0x5c, 0xe3, 0x0f, 0x30, 0x03, 0xe2, 0x02, 0x32, 0x83, 0xe1, 0x0c, 0x20, 0xa0, 0x13, 126 | 0x8b, 0x30, 0xc4, 0xe5, 0x09, 0x20, 0xd7, 0x05, 0x8b, 0x30, 0xd4, 0xe5, 0x22, 0x22, 0xa0, 0x01, 127 | 0x0f, 0x30, 0xc3, 0xe3, 0x02, 0x30, 0x83, 0xe1, 0x8b, 0x30, 0xc4, 0xe5, 0x40, 0x10, 0xa0, 0xe3, 128 | 0x2c, 0x01, 0x9f, 0xe5, 0x24, 0x31, 0x9f, 0xe5, 0x0f, 0xe0, 0xa0, 0xe1, 0x13, 0xff, 0x2f, 0xe1, 129 | 0x2a, 0xff, 0xff, 0xea, 0x00, 0x00, 0x5e, 0xe3, 0x88, 0x30, 0xc4, 0xe5, 0x89, 0x00, 0xc4, 0xe5, 130 | 0x26, 0x00, 0x00, 0x1a, 0x08, 0x30, 0xd7, 0xe5, 0x8a, 0x30, 0xc4, 0xe5, 0x00, 0x00, 0x5c, 0xe3, 131 | 0x09, 0x30, 0xd7, 0x05, 0x00, 0x00, 0x00, 0x0a, 0xc8, 0x30, 0xa0, 0xe3, 0x8b, 0x30, 0xc4, 0xe5, 132 | 0xed, 0xff, 0xff, 0xea, 0x00, 0x00, 0x52, 0xe3, 0x88, 0x30, 0xc4, 0xe5, 0xc8, 0x30, 0xa0, 0x13, 133 | 0x89, 0x00, 0xc4, 0xe5, 0x06, 0x30, 0xd7, 0x05, 0x00, 0x00, 0x58, 0xe3, 0x8a, 0x30, 0xc4, 0xe5, 134 | 0x07, 0x30, 0xd7, 0x05, 0xf4, 0xff, 0xff, 0x0a, 0xf2, 0xff, 0xff, 0xea, 0x00, 0x00, 0x52, 0xe3, 135 | 0x0c, 0x20, 0xa0, 0x13, 0xf0, 0x30, 0x03, 0xe2, 0x20, 0x32, 0x83, 0xe1, 0x06, 0x20, 0xd7, 0x05, 136 | 0x88, 0x30, 0xc4, 0xe5, 0x89, 0x30, 0xd4, 0xe5, 0x22, 0x22, 0xa0, 0x01, 0x0f, 0x30, 0x03, 0xe2, 137 | 0x00, 0x00, 0x58, 0xe3, 0x02, 0x32, 0x83, 0xe1, 0x0c, 0x20, 0xa0, 0x13, 0x89, 0x30, 0xc4, 0xe5, 138 | 0x07, 0x20, 0xd7, 0x05, 0x89, 0x30, 0xd4, 0xe5, 0x22, 0x22, 0xa0, 0x01, 0x0f, 0x30, 0xc3, 0xe3, 139 | 0x02, 0x30, 0x83, 0xe1, 0x00, 0x00, 0x5e, 0xe3, 0x89, 0x30, 0xc4, 0xe5, 0xd8, 0xff, 0xff, 0x0a, 140 | 0xc8, 0x30, 0xa0, 0xe3, 0xd7, 0xff, 0xff, 0xea, 0xf0, 0x30, 0x03, 0xe2, 0x20, 0x32, 0x83, 0xe1, 141 | 0x00, 0x00, 0x52, 0xe3, 0x88, 0x30, 0xc4, 0xe5, 0xc8, 0x30, 0xa0, 0x13, 0x06, 0x30, 0xd7, 0x05, 142 | 0x00, 0x00, 0x58, 0xe3, 0x89, 0x30, 0xc4, 0xe5, 0xc8, 0x30, 0xa0, 0x13, 0x07, 0x30, 0xd7, 0x05, 143 | 0x00, 0x00, 0x5e, 0xe3, 0x8a, 0x30, 0xc4, 0xe5, 0xb1, 0xff, 0xff, 0x0a, 0x0c, 0x20, 0xa0, 0xe3, 144 | 0xb1, 0xff, 0xff, 0xea, 0x00, 0x02, 0x00, 0x04, 0x00, 0x01, 0x00, 0x04, 0x88, 0x81, 0xff, 0xff, 145 | 0x10, 0x09, 0x00, 0x03, 0x44, 0x08, 0x00, 0x03, 0xa4, 0x09, 0x00, 0x03, 0x01, 0x04, 0x00, 0x00, 146 | 0xcc, 0x02, 0x00, 0x02, 0xcf, 0x02, 0x00, 0x02, 0x00, 0x10, 0x00, 0x08, 0x01, 0x00, 0x00, 0x92, 147 | 0xf0, 0x05, 0x00, 0x03, 0x94, 0x09, 0x00, 0x03, 0x90, 0x09, 0x00, 0x03, 0xfe, 0xfe, 0xff, 0xff, 148 | 0xd3, 0x02, 0x00, 0x02, 0x00, 0x30, 0xa0, 0xe3, 0x30, 0x40, 0x2d, 0xe9, 0x03, 0xc0, 0xa0, 0xe1, 149 | 0x80, 0x50, 0xa0, 0xe3, 0x88, 0x40, 0xa0, 0xe3, 0x34, 0xe2, 0x9f, 0xe5, 0x36, 0x00, 0x00, 0xea, 150 | 0x34, 0x50, 0xce, 0xe5, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 151 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 152 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 153 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 154 | 0x34, 0x40, 0xce, 0xe5, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 155 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 156 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 157 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 158 | 0x34, 0x40, 0xce, 0xe5, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 159 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 160 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 161 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 162 | 0x34, 0x40, 0xce, 0xe5, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 163 | 0x00, 0x00, 0xa0, 0xe1, 0x01, 0x00, 0x53, 0xe1, 0x3c, 0x00, 0x00, 0x2a, 0x07, 0x00, 0x13, 0xe3, 164 | 0x01, 0xc0, 0xd0, 0x04, 0x8c, 0xc0, 0xa0, 0xe1, 0x01, 0x0c, 0x1c, 0xe3, 0x01, 0x30, 0x83, 0xe2, 165 | 0xc2, 0xff, 0xff, 0x1a, 0x34, 0x50, 0xce, 0xe5, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 166 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 167 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 168 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 169 | 0x00, 0x00, 0xa0, 0xe1, 0x34, 0x50, 0xce, 0xe5, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 170 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 171 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 172 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 173 | 0x00, 0x00, 0xa0, 0xe1, 0x34, 0x50, 0xce, 0xe5, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 174 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 175 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 176 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 177 | 0x00, 0x00, 0xa0, 0xe1, 0x34, 0x40, 0xce, 0xe5, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 178 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x01, 0x00, 0x53, 0xe1, 0xc2, 0xff, 0xff, 0x3a, 179 | 0x80, 0x30, 0xa0, 0xe3, 0x88, 0x20, 0xa0, 0xe3, 0x54, 0x10, 0x9f, 0xe5, 0x00, 0x00, 0xa0, 0xe1, 180 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x34, 0x30, 0xc1, 0xe5, 0x00, 0x00, 0xa0, 0xe1, 181 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 182 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 183 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 184 | 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x34, 0x20, 0xc1, 0xe5, 0x30, 0x40, 0xbd, 0xe8, 185 | 0x1e, 0xff, 0x2f, 0xe1, 0x00, 0x01, 0x00, 0x04, 0xf0, 0x43, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe3, 186 | 0xc3, 0x30, 0xa0, 0xe3, 0x94, 0x40, 0x9f, 0xe5, 0x94, 0x60, 0x9f, 0xe5, 0xb2, 0x50, 0xc4, 0xe1, 187 | 0xba, 0x50, 0xc4, 0xe1, 0xb2, 0x20, 0xd6, 0xe1, 0xc0, 0x90, 0xa0, 0xe3, 0xb2, 0x20, 0xc6, 0xe1, 188 | 0x05, 0x70, 0xa0, 0xe1, 0x05, 0x80, 0xa0, 0xe1, 0xba, 0x30, 0xc4, 0xe1, 0x0c, 0x00, 0x00, 0xea, 189 | 0xb4, 0xc3, 0xd4, 0xe1, 0xb4, 0xe3, 0xd4, 0xe1, 0xb4, 0x33, 0xd4, 0xe1, 0x0e, 0xc0, 0x8c, 0xe1, 190 | 0x0c, 0x30, 0x83, 0xe1, 0x23, 0x31, 0xa0, 0xe1, 0x01, 0x50, 0x85, 0xe2, 0x01, 0x30, 0x03, 0xe2, 191 | 0x07, 0x00, 0x15, 0xe3, 0x87, 0x70, 0x83, 0xe1, 0x01, 0x70, 0xc0, 0x04, 0x01, 0x00, 0x55, 0xe1, 192 | 0x0b, 0x00, 0x00, 0x2a, 0x02, 0x21, 0xa0, 0xe1, 0x80, 0x20, 0x02, 0xe2, 0x0f, 0xe0, 0xa0, 0xe1, 193 | 0x6b, 0xff, 0xa0, 0xe3, 0xb2, 0x80, 0xc4, 0xe1, 0xb2, 0x20, 0xd6, 0xe1, 0x80, 0x00, 0x12, 0xe3, 194 | 0xb2, 0x20, 0xc6, 0xe1, 0xb2, 0x90, 0xc4, 0xe1, 0xe8, 0xff, 0xff, 0x1a, 0x08, 0x00, 0x12, 0xe3, 195 | 0xf1, 0xff, 0xff, 0x0a, 0x05, 0x00, 0xa0, 0xe1, 0xf0, 0x43, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 196 | 0x00, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x04, 0x20, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 197 | 0x00, 0x00, 0xff, 0xff 198 | }; 199 | const int gba_mb_size = sizeof(gba_mb); 200 | -------------------------------------------------------------------------------- /source/gba_mb.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file was autogenerated by raw2c. 3 | Visit http://www.devkitpro.org 4 | */ 5 | 6 | //--------------------------------------------------------------------------------- 7 | #ifndef _gba_mb_h_ 8 | #define _gba_mb_h_ 9 | //--------------------------------------------------------------------------------- 10 | extern const unsigned char gba_mb[]; 11 | extern const int gba_mb_size; 12 | //--------------------------------------------------------------------------------- 13 | #endif //_gba_mb_h_ 14 | //--------------------------------------------------------------------------------- 15 | -------------------------------------------------------------------------------- /source/gba_memory.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include 11 | #include "vm/vm.h" 12 | 13 | uint32_t *romBuffer; 14 | size_t romBufferSize; 15 | 16 | static void __attribute__((constructor)) allocateRomBuffer(void) 17 | { 18 | #ifdef HW_DOL 19 | romBufferSize = 32 << 20; 20 | romBuffer = SYS_AllocArenaMem1Hi(romBufferSize, 32); 21 | 22 | if (!romBuffer) { 23 | AR_Init(NULL, 0); 24 | ARQ_Init(); 25 | 26 | romBufferSize = AR_GetSize(); 27 | romBuffer = VM_Init(romBufferSize, 8 << 20); 28 | } 29 | #else 30 | romBufferSize = 32 << 20; 31 | romBuffer = SYS_AllocArenaMem2Lo(romBufferSize, 32); 32 | #endif 33 | } 34 | 35 | void *anonymousMemoryMap(size_t size) 36 | { 37 | return malloc(size); 38 | } 39 | 40 | void mappedMemoryFree(void *memory, size_t size) 41 | { 42 | free(memory); 43 | } 44 | -------------------------------------------------------------------------------- /source/gba_video.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include "gba.h" 11 | 12 | void GBAVideoConvertBGR5(void *dst, void *src, int width, int height) 13 | { 14 | GX_RedirectWriteGatherPipe(dst); 15 | 16 | uint16_t *src0 = src - 4; 17 | uint16_t *src1 = src0 + width; 18 | uint16_t *src2 = src1 + width; 19 | uint16_t *src3 = src2 + width; 20 | 21 | register int reg00, reg01; 22 | register int reg10, reg11; 23 | register int reg20, reg21; 24 | register int reg30, reg31; 25 | 26 | int lines = height >> 2; 27 | 28 | while (lines--) { 29 | int tiles = width >> 2; 30 | 31 | do { 32 | asm volatile ( 33 | "lwz %00, 4 (%08) \n" 34 | "lwzu %01, 8 (%08) \n" 35 | "lwz %02, 4 (%09) \n" 36 | "lwzu %03, 8 (%09) \n" 37 | "lwz %04, 4 (%10) \n" 38 | "lwzu %05, 8 (%10) \n" 39 | "lwz %06, 4 (%11) \n" 40 | "lwzu %07, 8 (%11) \n" 41 | 42 | "or %00, %00, %12 \n" 43 | "or %01, %01, %12 \n" 44 | "or %02, %02, %12 \n" 45 | "or %03, %03, %12 \n" 46 | "or %04, %04, %12 \n" 47 | "or %05, %05, %12 \n" 48 | "or %06, %06, %12 \n" 49 | "or %07, %07, %12 \n" 50 | 51 | "stw %00, 0 (%13) \n" 52 | "stw %01, 0 (%13) \n" 53 | "stw %02, 0 (%13) \n" 54 | "stw %03, 0 (%13) \n" 55 | "stw %04, 0 (%13) \n" 56 | "stw %05, 0 (%13) \n" 57 | "stw %06, 0 (%13) \n" 58 | "stw %07, 0 (%13) \n" 59 | : "=r" (reg00), "=r" (reg01), 60 | "=r" (reg10), "=r" (reg11), 61 | "=r" (reg20), "=r" (reg21), 62 | "=r" (reg30), "=r" (reg31), 63 | "+b" (src0), "+b" (src1), "+b" (src2), "+b" (src3) 64 | : "r" (0x80008000), 65 | "b" (wgPipe) 66 | : "memory" 67 | ); 68 | } while (--tiles); 69 | 70 | src0 += width * 3; 71 | src1 += width * 3; 72 | src2 += width * 3; 73 | src3 += width * 3; 74 | } 75 | 76 | GX_RestoreWriteGatherPipe(); 77 | } 78 | -------------------------------------------------------------------------------- /source/gbp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef GBI_GBP_H 10 | #define GBI_GBP_H 11 | 12 | #include 13 | 14 | int GBPGetController(void); 15 | int GBPGetScreenSize(void); 16 | int GBPGetFrame(void); 17 | int GBPGetTimer(void); 18 | int GBPGetScreenFilter(void); 19 | 20 | #endif /* GBI_GBP_H */ 21 | -------------------------------------------------------------------------------- /source/gbp_sram.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include "gbp.h" 11 | 12 | extern syssramex *__SYS_LockSramEx(); 13 | extern uint32_t __SYS_UnlockSramEx(uint32_t write); 14 | 15 | static bool is_valid(syssramex *sramex) 16 | { 17 | if (!sramex) 18 | return false; 19 | if ((sramex->gbs & 0x8000) >> 15 == __builtin_parity(sramex->gbs & ~0x8000)) 20 | return false; 21 | 22 | return true; 23 | } 24 | 25 | int GBPGetController(void) 26 | { 27 | int value = 0; 28 | syssramex *sramex = __SYS_LockSramEx(); 29 | 30 | if (is_valid(sramex)) 31 | value = (sramex->gbs & 0x100) >> 8; 32 | 33 | __SYS_UnlockSramEx(0); 34 | 35 | return value; 36 | } 37 | 38 | int GBPGetScreenSize(void) 39 | { 40 | int value = 0; 41 | syssramex *sramex = __SYS_LockSramEx(); 42 | 43 | if (is_valid(sramex)) 44 | value = (sramex->gbs & 0x200) >> 9; 45 | 46 | __SYS_UnlockSramEx(0); 47 | 48 | return value; 49 | } 50 | 51 | int GBPGetFrame(void) 52 | { 53 | int value = 0; 54 | syssramex *sramex = __SYS_LockSramEx(); 55 | 56 | if (is_valid(sramex)) 57 | value = (sramex->gbs & 0x7C00) >> 10; 58 | 59 | __SYS_UnlockSramEx(0); 60 | 61 | return value; 62 | } 63 | 64 | int GBPGetTimer(void) 65 | { 66 | int value = 0; 67 | syssramex *sramex = __SYS_LockSramEx(); 68 | 69 | if (is_valid(sramex)) 70 | value = sramex->gbs & 0x3F; 71 | 72 | __SYS_UnlockSramEx(0); 73 | 74 | return value; 75 | } 76 | 77 | int GBPGetScreenFilter(void) 78 | { 79 | int value = 0; 80 | syssramex *sramex = __SYS_LockSramEx(); 81 | 82 | if (is_valid(sramex)) 83 | value = (sramex->gbs & 0xC0) >> 6; 84 | 85 | __SYS_UnlockSramEx(0); 86 | 87 | return value; 88 | } 89 | -------------------------------------------------------------------------------- /source/gx.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "gx.h" 16 | #include "state.h" 17 | #include "util.h" 18 | 19 | static void *fifo; 20 | static GXTexRegion texregion[24]; 21 | static GXTlutRegion tlutregion[20]; 22 | 23 | static GXTexRegion *texregion_cb(GXTexObj *texobj, uint8_t mapid) 24 | { 25 | uint8_t format = GX_GetTexObjFmt(texobj); 26 | uint8_t mipmap = GX_GetTexObjMipMap(texobj); 27 | 28 | if (format == GX_TF_CI4 || format == GX_TF_CI8 || format == GX_TF_CI14) 29 | return &texregion[mapid]; 30 | 31 | if (format == GX_TF_RGBA8) { 32 | if (mipmap) 33 | return &texregion[mapid + 16]; 34 | else 35 | return &texregion[mapid + 8]; 36 | } else { 37 | if (mipmap) 38 | return &texregion[mapid + 8]; 39 | else 40 | return &texregion[mapid]; 41 | } 42 | } 43 | 44 | static GXTlutRegion *tlutregion_cb(uint32_t tlut) 45 | { 46 | return &tlutregion[tlut]; 47 | } 48 | 49 | void GXInit(void) 50 | { 51 | Mtx m; 52 | 53 | fifo = GXAllocBuffer(GX_FIFO_MINSIZE); 54 | GX_Init(fifo, GX_FIFO_MINSIZE); 55 | 56 | CAST_SetGQR2(GQR_TYPE_U8, 8); 57 | CAST_SetGQR3(GQR_TYPE_U16, 16); 58 | 59 | switch (lrintf(state.output_gamma * 10.)) { 60 | case 10: GX_SetDispCopyGamma(GX_GM_1_0); break; 61 | case 17: GX_SetDispCopyGamma(GX_GM_1_7); break; 62 | case 22: GX_SetDispCopyGamma(GX_GM_2_2); break; 63 | } 64 | 65 | for (int i = GX_TEXCOORD0; i < GX_MAXCOORD; i++) 66 | GX_SetTexCoordScaleManually(i, GX_TRUE, 1, 1); 67 | 68 | for (int i = 0; i < 10; i++) { 69 | float s = 1 << (i + 1); 70 | 71 | guMtxScale(m, s, s, s); 72 | GX_LoadTexMtxImm(m, GX_TEXMTX0 + i * 3, GX_MTX3x4); 73 | } 74 | 75 | guMtxTrans(m, 1./64., 1./64., 0); 76 | GX_LoadTexMtxImm(m, GX_DTTIDENTITY, GX_MTX3x4); 77 | 78 | for (int i = 0; i < 9; i++) { 79 | float x = i % 3 - 1; 80 | float y = i / 3 - 1; 81 | 82 | if (i % 2 == 0) { 83 | x /= 2; 84 | y /= 2; 85 | } 86 | 87 | x += 1./64.; 88 | y += 1./64.; 89 | 90 | guMtxTrans(m, x, y, 0); 91 | GX_LoadTexMtxImm(m, GX_DTTMTX1 + i * 3, GX_MTX3x4); 92 | } 93 | 94 | for (int i = 0; i < 4; i++) { 95 | GX_InitTexCacheRegion(&texregion[i + 0], GX_FALSE, 0x00000 + i * 0x10000, GX_TEXCACHE_32K, 0x08000 + i * 0x10000, GX_TEXCACHE_32K); 96 | GX_InitTexCacheRegion(&texregion[i + 8], GX_FALSE, 0x00000 + i * 0x10000, GX_TEXCACHE_32K, 0x80000 + i * 0x10000, GX_TEXCACHE_32K); 97 | GX_InitTexCacheRegion(&texregion[i + 16], GX_TRUE, 0x00000 + i * 0x10000, GX_TEXCACHE_32K, 0x80000 + i * 0x10000, GX_TEXCACHE_32K); 98 | } 99 | 100 | for (int i = 0; i < 4; i++) { 101 | GX_InitTexCacheRegion(&texregion[i + 4], GX_FALSE, 0x08000 + i * 0x10000, GX_TEXCACHE_32K, 0x00000 + i * 0x10000, GX_TEXCACHE_32K); 102 | GX_InitTexCacheRegion(&texregion[i + 12], GX_FALSE, 0x08000 + i * 0x10000, GX_TEXCACHE_32K, 0x88000 + i * 0x10000, GX_TEXCACHE_32K); 103 | GX_InitTexCacheRegion(&texregion[i + 20], GX_TRUE, 0x80000 + i * 0x10000, GX_TEXCACHE_32K, 0x00000 + i * 0x10000, GX_TEXCACHE_32K); 104 | } 105 | 106 | for (int i = 0; i < 16; i++) 107 | GX_InitTlutRegion(&tlutregion[i + 0], 0xC0000 + i * 0x2000, GX_TLUT_256); 108 | 109 | for (int i = 0; i < 4; i++) 110 | GX_InitTlutRegion(&tlutregion[i + 16], 0xC0000 + i * 0x8000, GX_TLUT_1K); 111 | 112 | GX_SetTexRegionCallback(texregion_cb); 113 | GX_SetTlutRegionCallback(tlutregion_cb); 114 | } 115 | 116 | void *GXAllocBuffer(uint32_t size) 117 | { 118 | void *ptr = memalign(PPC_CACHE_ALIGNMENT, size); 119 | if (ptr) DCZeroRange(ptr, size); 120 | return ptr; 121 | } 122 | 123 | static double trc_linear(int ch, double f) { 124 | return f; 125 | } 126 | 127 | static double trc_gamma(int ch, double f) { 128 | double V = fabs(f); 129 | double L = pow(V, state.input_gamma[ch]); 130 | return copysign(L, f); 131 | } 132 | 133 | static double trc_piecewise(int ch, double f) { 134 | double V = fabs(f); 135 | double L; 136 | 137 | if (state.input_gamma[ch] <= 1. + state.input_alpha[ch]) { 138 | L = pow(V, state.input_gamma[ch]); 139 | } else { 140 | double kappa = state.input_alpha[ch] / (state.input_gamma[ch] - 1.); 141 | double phi = pow((1. + state.input_alpha[ch]) / state.input_gamma[ch], state.input_gamma[ch]) 142 | * pow((state.input_gamma[ch] - 1.) / state.input_alpha[ch], state.input_gamma[ch] - 1.); 143 | L = V <= kappa ? V / phi : pow((V + state.input_alpha[ch]) / (1. + state.input_alpha[ch]), state.input_gamma[ch]); 144 | } 145 | 146 | return copysign(L, f); 147 | } 148 | 149 | static double trc_iec61966(int ch, double f) { 150 | double V = fabs(f); 151 | double L = V <= .04045 ? V / 12.92 : pow((V + .055) / 1.055, 2.4); 152 | return copysign(L, f); 153 | } 154 | 155 | static double trc_itu709(int ch, double f) { 156 | double V = fabs(f); 157 | double L = V <= .081 ? V / 4.5 : pow((V + .099) / 1.099, 1/.45); 158 | return copysign(L, f); 159 | } 160 | 161 | static double trc_smpte240(int ch, double f) { 162 | double V = fabs(f); 163 | double L = V <= .0912 ? V / 4. : pow((V + .1115) / 1.1115, 1/.45); 164 | return copysign(L, f); 165 | } 166 | 167 | static double (*trc_funcs[])(int, double) = { 168 | trc_linear, 169 | trc_gamma, 170 | trc_piecewise, 171 | trc_iec61966, 172 | trc_itu709, 173 | trc_smpte240 174 | }; 175 | 176 | static void fill_lut(int ch, double (*func)(int, double), hword_t *lut) 177 | { 178 | double a = func(ch, state.contrast[ch]), b = state.brightness[ch] / state.contrast[ch]; 179 | 180 | for (int i = 0; i < 256; i++) { 181 | double f = a * func(ch, i / 255. + b); 182 | 183 | if (state.dither) { 184 | lut[i].u16 = f * 65535. + .5; 185 | } else { 186 | lut[i].u8[0] = f * 255. + .5; 187 | lut[i].u8[1] = 0; 188 | } 189 | } 190 | 191 | DCStoreRange(lut, 256 * sizeof(hword_t)); 192 | } 193 | 194 | void GXAllocSurface(gx_surface_t *surface, uint16_t width, uint16_t height, uint8_t format, uint8_t planes) 195 | { 196 | uint32_t size = GX_GetTexBufferSize(width, height, format, GX_FALSE, 0); 197 | 198 | surface->planes = planes; 199 | surface->slices = 0; 200 | surface->size = size; 201 | 202 | surface->buf = calloc(planes, sizeof(void *)); 203 | surface->lutbuf = calloc(planes, sizeof(void *)); 204 | 205 | surface->obj = calloc(planes, sizeof(GXTexObj)); 206 | surface->lutobj = calloc(planes, sizeof(GXTlutObj)); 207 | 208 | for (int i = 0; i < surface->planes; i++) { 209 | surface->buf[i] = GXAllocBuffer(surface->size); 210 | 211 | switch (format) { 212 | case GX_TF_CI4: 213 | break; 214 | case GX_TF_CI8: 215 | surface->lutbuf[i] = GXAllocBuffer(256 * sizeof(hword_t)); 216 | fill_lut(i, trc_funcs[state.input_trc], surface->lutbuf[i]); 217 | 218 | GX_InitTlutObj(&surface->lutobj[i], surface->lutbuf[i], GX_TL_IA8, 256); 219 | GX_InitTexObjCI(&surface->obj[i], surface->buf[i], width, height, format, GX_CLAMP, GX_CLAMP, GX_FALSE, GX_TLUT0 + i); 220 | break; 221 | case GX_TF_CI14: 222 | break; 223 | default: 224 | GX_InitTexObj(&surface->obj[i], surface->buf[i], width, height, format, GX_CLAMP, GX_CLAMP, GX_FALSE); 225 | } 226 | 227 | GX_InitTexObjUserData(&surface->obj[i], surface); 228 | } 229 | } 230 | 231 | void GXAllocSurfaceSliced(gx_surface_t *surface, uint16_t width, uint16_t height, uint8_t format, uint8_t slices) 232 | { 233 | uint32_t size = GX_GetTexBufferSize(width, height, format, GX_FALSE, 0); 234 | 235 | surface->planes = 1; 236 | surface->slices = slices; 237 | surface->size = size; 238 | 239 | surface->buf = calloc(slices, sizeof(void *)); 240 | surface->lutbuf = calloc(1, sizeof(void *)); 241 | 242 | surface->obj = calloc(1 + slices, sizeof(GXTexObj)); 243 | surface->lutobj = calloc(1, sizeof(GXTlutObj)); 244 | 245 | *surface->buf = GXAllocBuffer(surface->size * surface->slices); 246 | 247 | switch (format) { 248 | case GX_TF_CI4: 249 | break; 250 | case GX_TF_CI8: 251 | break; 252 | case GX_TF_CI14: 253 | break; 254 | default: 255 | GX_InitTexObj(&surface->obj[slices], *surface->buf, width, height * slices, format, GX_CLAMP, GX_CLAMP, GX_FALSE); 256 | } 257 | 258 | GX_InitTexObjUserData(&surface->obj[slices], surface); 259 | 260 | for (int i = 0; i < surface->slices; i++) { 261 | surface->buf[i] = *surface->buf + i * surface->size; 262 | 263 | switch (format) { 264 | case GX_TF_CI4: 265 | break; 266 | case GX_TF_CI8: 267 | break; 268 | case GX_TF_CI14: 269 | break; 270 | default: 271 | GX_InitTexObj(&surface->obj[i], surface->buf[i], width, height, format, GX_CLAMP, GX_CLAMP, GX_FALSE); 272 | } 273 | 274 | GX_InitTexObjUserData(&surface->obj[i], surface); 275 | } 276 | } 277 | 278 | void GXPreloadSurface(gx_surface_t *surface, uint32_t tmem_even, uint32_t tmem_odd, uint8_t count) 279 | { 280 | surface->shadows = count; 281 | surface->shadow = 0; 282 | 283 | surface->region = calloc(count, sizeof(GXTexRegion)); 284 | 285 | if (tmem_even == tmem_odd) { 286 | for (int i = 0; i < count; i++) { 287 | GX_InitTexPreloadRegion(&surface->region[i], tmem_even, surface->size, 0x00000, 0); 288 | GX_PreloadEntireTexture(&surface->obj[0], &surface->region[i]); 289 | tmem_even += surface->size; 290 | } 291 | } else { 292 | for (int i = 0; i < count; i++) { 293 | GX_InitTexPreloadRegion(&surface->region[i], tmem_even, surface->size / 2, tmem_odd, surface->size / 2); 294 | GX_PreloadEntireTexture(&surface->obj[0], &surface->region[i]); 295 | tmem_even += surface->size / 2; 296 | tmem_odd += surface->size / 2; 297 | } 298 | } 299 | } 300 | 301 | void GXPreloadSurfacev(gx_surface_t *surface, uint32_t tmem_even[], uint32_t tmem_odd[], uint8_t count) 302 | { 303 | surface->shadows = count; 304 | surface->shadow = 0; 305 | 306 | surface->region = calloc(count, sizeof(GXTexRegion)); 307 | 308 | if (!tmem_odd) { 309 | for (int i = 0; i < count; i++) { 310 | GX_InitTexPreloadRegion(&surface->region[i], tmem_even[i], surface->size, 0x00000, 0); 311 | GX_PreloadEntireTexture(&surface->obj[0], &surface->region[i]); 312 | } 313 | } else { 314 | for (int i = 0; i < count; i++) { 315 | GX_InitTexPreloadRegion(&surface->region[i], tmem_even[i], surface->size / 2, tmem_odd[i], surface->size / 2); 316 | GX_PreloadEntireTexture(&surface->obj[0], &surface->region[i]); 317 | } 318 | } 319 | } 320 | 321 | void GXCacheSurface(gx_surface_t *surface, uint32_t tmem_even, uint32_t tmem_odd, uint8_t count) 322 | { 323 | surface->shadows = count; 324 | surface->shadow = 0; 325 | 326 | surface->region = calloc(count, sizeof(GXTexRegion)); 327 | 328 | if (tmem_even == tmem_odd) { 329 | for (int i = 0; i < count; i++) { 330 | GX_InitTexCacheRegion(&surface->region[i], GX_FALSE, tmem_even, GX_TEXCACHE_32K, 0x00000, GX_TEXCACHE_NONE); 331 | GX_InvalidateTexRegion(&surface->region[i]); 332 | tmem_even += 0x10000; 333 | } 334 | } else { 335 | for (int i = 0; i < count; i++) { 336 | GX_InitTexCacheRegion(&surface->region[i], GX_FALSE, tmem_even, GX_TEXCACHE_32K, tmem_odd, GX_TEXCACHE_32K); 337 | GX_InvalidateTexRegion(&surface->region[i]); 338 | tmem_even += 0x10000; 339 | tmem_odd += 0x10000; 340 | } 341 | } 342 | } 343 | 344 | void GXCacheSurfacev(gx_surface_t *surface, uint32_t tmem_even[], uint32_t tmem_odd[], uint8_t count) 345 | { 346 | surface->shadows = count; 347 | surface->shadow = 0; 348 | 349 | surface->region = calloc(count, sizeof(GXTexRegion)); 350 | 351 | if (!tmem_odd) { 352 | for (int i = 0; i < count; i++) { 353 | GX_InitTexCacheRegion(&surface->region[i], GX_FALSE, tmem_even[i], GX_TEXCACHE_32K, 0x00000, GX_TEXCACHE_NONE); 354 | GX_InvalidateTexRegion(&surface->region[i]); 355 | } 356 | } else { 357 | for (int i = 0; i < count; i++) { 358 | GX_InitTexCacheRegion(&surface->region[i], GX_FALSE, tmem_even[i], GX_TEXCACHE_32K, tmem_odd[i], GX_TEXCACHE_32K); 359 | GX_InvalidateTexRegion(&surface->region[i]); 360 | } 361 | } 362 | } 363 | 364 | void GXSetSurfaceFilt(gx_surface_t *surface, uint8_t filter) 365 | { 366 | switch (filter) { 367 | case GX_NEAR: 368 | for (int i = 0; i < surface->planes + surface->slices; i++) 369 | GX_InitTexObjLOD(&surface->obj[i], GX_NEAR, GX_NEAR, 0., 0., 0., GX_FALSE, GX_FALSE, GX_ANISO_1); 370 | break; 371 | case GX_LINEAR: 372 | for (int i = 0; i < surface->planes + surface->slices; i++) 373 | GX_InitTexObjLOD(&surface->obj[i], GX_LINEAR, GX_LINEAR, 0., 0., 0., GX_TRUE, GX_TRUE, GX_ANISO_4); 374 | break; 375 | } 376 | } 377 | 378 | void GXSetSurfaceTlut(gx_surface_t *surface, uint32_t tlut) 379 | { 380 | for (int i = 0; i < surface->planes + surface->slices; i++) 381 | GX_InitTexObjTlut(&surface->obj[i], tlut); 382 | } 383 | 384 | void GXFreeSurface(gx_surface_t *surface) 385 | { 386 | bulk_free(surface->buf, surface->planes); 387 | bulk_free(surface->lutbuf, surface->planes); 388 | 389 | free(surface->buf); 390 | free(surface->lutbuf); 391 | 392 | free(surface->obj); 393 | free(surface->region); 394 | free(surface->lutobj); 395 | 396 | surface->planes = 0; 397 | surface->slices = 0; 398 | surface->size = 0; 399 | 400 | surface->buf = NULL; 401 | surface->lutbuf = NULL; 402 | 403 | surface->obj = NULL; 404 | surface->region = NULL; 405 | surface->lutobj = NULL; 406 | } 407 | 408 | void *GXOpenMem(void *buffer, int size) 409 | { 410 | return fmemopen(buffer, size, "rb"); 411 | } 412 | 413 | void *GXOpenFile(const char *file) 414 | { 415 | gzFile zfp = gzopen(file, "rb"); 416 | 417 | return funopen(zfp, 418 | (int (*)(void *, char *, size_t))gzread, 419 | (int (*)(void *, const char *, size_t))gzwrite, 420 | (fpos_t (*)(void *, fpos_t, int))gzseek, 421 | (int (*)(void *))gzclose); 422 | } 423 | 424 | rect_t GXReadRect(void) 425 | { 426 | rect_t rect = {0}; 427 | uint16_t top, bottom, left, right; 428 | 429 | GX_ReadBoundingBox(&top, &bottom, &left, &right); 430 | 431 | if (top < bottom && left < right) { 432 | rect.x = viewport.x + left; 433 | rect.y = viewport.y + top; 434 | rect.w = right - left + 1; 435 | rect.h = bottom - top + 1; 436 | } 437 | 438 | return rect; 439 | } 440 | -------------------------------------------------------------------------------- /source/gx.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef GBI_GX_H 10 | #define GBI_GX_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include "video.h" 16 | 17 | typedef struct { 18 | uint8_t planes; 19 | uint8_t slices; 20 | uint8_t shadows, shadow; 21 | uint32_t size; 22 | void **buf; 23 | void **lutbuf; 24 | GXTexObj *obj; 25 | GXTexRegion *region; 26 | GXTlutObj *lutobj; 27 | rect_t rect; 28 | bool dirty; 29 | } gx_surface_t; 30 | 31 | typedef union { 32 | uint32_t addr; 33 | uint32_t *ptr; 34 | struct { 35 | uint32_t : 10; 36 | uint32_t y : 10; 37 | uint32_t x : 10; 38 | uint32_t : 2; 39 | }; 40 | } gx_efb32_t; 41 | 42 | typedef union { 43 | uint32_t addr; 44 | uint64_t *ptr; 45 | struct { 46 | uint32_t : 9; 47 | uint32_t y : 10; 48 | uint32_t x : 10; 49 | uint32_t : 3; 50 | }; 51 | } gx_efb64_t; 52 | 53 | static inline float GXCast1u8f32(uint8_t inval) 54 | { 55 | float outval; 56 | asm("psq_l%U1%X1 %0,%1,1,2" : "=f" (outval) : "m" (inval)); 57 | return outval; 58 | } 59 | 60 | static inline float GXCast1u16f32(uint16_t inval) 61 | { 62 | float outval; 63 | asm("psq_l%U1%X1 %0,%1,1,3" : "=f" (outval) : "m" (inval)); 64 | return outval; 65 | } 66 | 67 | static inline vector float GXCast2u8f32(uint8_t inval[2]) 68 | { 69 | vector float outval; 70 | asm("psq_l%U1%X1 %0,%1,0,2" : "=f" (outval) : "m" (inval)); 71 | return outval; 72 | } 73 | 74 | static inline vector float GXCast2u16f32(uint16_t inval[2]) 75 | { 76 | vector float outval; 77 | asm("psq_l%U1%X1 %0,%1,1,3" : "=f" (outval) : "m" (inval)); 78 | return outval; 79 | } 80 | 81 | static inline uint8_t GXCast1f32u8(float inval) 82 | { 83 | uint8_t outval; 84 | asm("psq_st%U0%X0 %1,%0,1,2" : "=m" (outval) : "f" (inval)); 85 | return outval; 86 | } 87 | 88 | static inline uint16_t GXCast1f32u16(float inval) 89 | { 90 | uint16_t outval; 91 | asm("psq_st%U0%X0 %1,%0,1,3" : "=m" (outval) : "f" (inval)); 92 | return outval; 93 | } 94 | 95 | void GXInit(void); 96 | void *GXAllocBuffer(uint32_t size); 97 | void GXAllocSurface(gx_surface_t *surface, uint16_t width, uint16_t height, uint8_t format, uint8_t planes); 98 | void GXAllocSurfaceSliced(gx_surface_t *surface, uint16_t width, uint16_t height, uint8_t format, uint8_t slices); 99 | void GXPreloadSurface(gx_surface_t *surface, uint32_t tmem_even, uint32_t tmem_odd, uint8_t count); 100 | void GXPreloadSurfacev(gx_surface_t *surface, uint32_t tmem_even[], uint32_t tmem_odd[], uint8_t count); 101 | void GXCacheSurface(gx_surface_t *surface, uint32_t tmem_even, uint32_t tmem_odd, uint8_t count); 102 | void GXCacheSurfacev(gx_surface_t *surface, uint32_t tmem_even[], uint32_t tmem_odd[], uint8_t count); 103 | void GXSetSurfaceFilt(gx_surface_t *surface, uint8_t filter); 104 | void GXSetSurfaceTlut(gx_surface_t *surface, uint32_t tlut); 105 | void GXFreeSurface(gx_surface_t *surface); 106 | void *GXOpenMem(void *buffer, int size); 107 | void *GXOpenFile(const char *file); 108 | rect_t GXReadRect(void); 109 | 110 | void GXCursorDrawPoint(uint32_t index, float x, float y, float angle); 111 | void GXCursorAllocState(void); 112 | void GXCursorSetState(void); 113 | 114 | void GXFontAllocState(void); 115 | void GXFontSetState(void); 116 | 117 | void GXOverlayDrawRect(rect_t rect); 118 | void GXOverlayAllocState(void); 119 | void GXOverlaySetState(void); 120 | void GXOverlayReadMemEx(void *buffer, int size, int index); 121 | void GXOverlayReadMem(void *buffer, int size, int index); 122 | void GXOverlayReadFile(const char *file, int index); 123 | 124 | void GXPackedApplyMix(gx_surface_t *dst, gx_surface_t *src); 125 | void GXPackedApplyYUV(gx_surface_t *dst, gx_surface_t *src); 126 | 127 | void GXPlanarApply(gx_surface_t *dst, gx_surface_t *src); 128 | void GXPlanarApplyBlend(gx_surface_t *dst, gx_surface_t *src); 129 | void GXPlanarApplyDeflicker(gx_surface_t *dst, gx_surface_t *src); 130 | void GXPlanarApplyScale2xEx(gx_surface_t *dst, gx_surface_t *src, gx_surface_t *yuv); 131 | void GXPlanarApplyScale2x(gx_surface_t *dst, gx_surface_t *src, bool blend); 132 | void GXPlanarApplyEagle2x(gx_surface_t *dst, gx_surface_t *src); 133 | void GXPlanarApplyScan2x(gx_surface_t *dst, gx_surface_t *src, bool field); 134 | void GXPlanarAllocState(void); 135 | 136 | void GXPrescaleApply(gx_surface_t *dst, gx_surface_t *src); 137 | void GXPrescaleApplyDither(gx_surface_t *dst, gx_surface_t *src); 138 | void GXPrescaleApplyDitherFast(gx_surface_t *dst, gx_surface_t *src); 139 | void GXPrescaleApplyBlend(gx_surface_t *dst, gx_surface_t **src, uint8_t *alpha, uint32_t count); 140 | void GXPrescaleApplyBlendDither(gx_surface_t *dst, gx_surface_t **src, uint8_t *alpha, uint32_t count); 141 | void GXPrescaleApplyBlendDitherFast(gx_surface_t *dst, gx_surface_t **src, uint8_t *alpha, uint32_t count); 142 | void GXPrescaleAllocState(void); 143 | rect_t GXPrescaleGetRect(uint16_t width, uint16_t height); 144 | 145 | void GXPreviewDrawRect(GXTexObj texobj[3], rect_t dst_rect, rect_t src_rect); 146 | void GXPreviewAllocState(void); 147 | void GXPreviewSetState(uint32_t index); 148 | 149 | void GXSolidDrawRect(rect_t rect, uint32_t color[4]); 150 | void GXSolidAllocState(void); 151 | void GXSolidSetState(void); 152 | 153 | #endif /* GBI_GX_H */ 154 | -------------------------------------------------------------------------------- /source/gx_cursor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include 11 | #include "gx.h" 12 | #include "state.h" 13 | #include "video.h" 14 | 15 | static void *displist; 16 | static uint32_t dispsize; 17 | 18 | static TPLFile tdf; 19 | static GXTexObj texobj[5]; 20 | 21 | void GXCursorDrawPoint(uint32_t index, float x, float y, float angle) 22 | { 23 | uint16_t width, height; 24 | 25 | if (TPL_GetTextureInfo(&tdf, index, NULL, &width, &height) == 0) { 26 | Mtx viewmodel; 27 | guMtxRotDeg(viewmodel, 'z', angle); 28 | guMtxTransApply(viewmodel, viewmodel, x, y, 0); 29 | GX_LoadPosMtxImm(viewmodel, GX_PNMTX1); 30 | 31 | GX_LoadTexObj(&texobj[index], GX_TEXMAP0); 32 | 33 | GX_Begin(GX_QUADS, GX_VTXFMT0, 4); 34 | 35 | GX_Position2s16(-width, -height); 36 | GX_TexCoord2s16(0, 0); 37 | 38 | GX_Position2s16(+width, -height); 39 | GX_TexCoord2s16(width, 0); 40 | 41 | GX_Position2s16(+width, +height); 42 | GX_TexCoord2s16(width, height); 43 | 44 | GX_Position2s16(-width, +height); 45 | GX_TexCoord2s16(0, height); 46 | } 47 | } 48 | 49 | void GXCursorAllocState(void) 50 | { 51 | Mtx44 projection; 52 | guOrtho(projection, 53 | -screen.y, screen.y + screen.h, 54 | -screen.x, screen.x + screen.w, 0., 1.); 55 | 56 | TPL_OpenTPLFromHandle(&tdf, GXOpenFile(state.cursor)); 57 | TPL_GetTexture(&tdf, 0, &texobj[0]); 58 | TPL_GetTexture(&tdf, 1, &texobj[1]); 59 | TPL_GetTexture(&tdf, 2, &texobj[2]); 60 | TPL_GetTexture(&tdf, 3, &texobj[3]); 61 | TPL_GetTexture(&tdf, 4, &texobj[4]); 62 | 63 | displist = GXAllocBuffer(GX_FIFO_MINSIZE); 64 | GX_BeginDispList(displist, GX_FIFO_MINSIZE); 65 | 66 | GX_SetBlendMode(GX_BM_BLEND, GX_BL_ONE, GX_BL_INVSRCALPHA, GX_LO_CLEAR); 67 | GX_SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0); 68 | GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); 69 | GX_SetZCompLoc(GX_FALSE); 70 | 71 | GX_SetNumChans(0); 72 | GX_SetNumTexGens(1); 73 | GX_SetNumIndStages(0); 74 | GX_SetNumTevStages(1); 75 | 76 | GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); 77 | 78 | GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORNULL); 79 | GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC); 80 | GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 81 | GX_SetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_TEXA); 82 | GX_SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 83 | GX_SetTevDirect(GX_TEVSTAGE0); 84 | 85 | GX_ClearVtxDesc(); 86 | GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); 87 | GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); 88 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 1); 89 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0); 90 | 91 | GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); 92 | GX_SetCurrentMtx(GX_PNMTX1); 93 | 94 | dispsize = GX_EndDispList(); 95 | displist = realloc_in_place(displist, dispsize); 96 | } 97 | 98 | void GXCursorSetState(void) 99 | { 100 | if (state.current != STATE_CURSOR) { 101 | state.current = STATE_CURSOR; 102 | GX_CallDispList(displist, dispsize); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /source/gx_font.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include 11 | #include "gx.h" 12 | #include "state.h" 13 | #include "video.h" 14 | 15 | #include 16 | 17 | static void *displist; 18 | static uint32_t dispsize; 19 | 20 | static sys_fontheader *fontdata; 21 | static GXTexObj texobj; 22 | static GXTexRegion texregion; 23 | 24 | static void GXFontDrawCell(int16_t x1, int16_t y1, uint32_t c, int16_t s1, int16_t t1) 25 | { 26 | int16_t x2 = x1 + fontdata->cell_width; 27 | int16_t y2 = y1 - fontdata->cell_height; 28 | 29 | int16_t s2 = s1 + fontdata->cell_width; 30 | int16_t t2 = t1 + fontdata->cell_height; 31 | 32 | GX_Begin(GX_QUADS, GX_VTXFMT0, 4); 33 | 34 | GX_Position2s16(x1, y2); 35 | GX_Color1u32(c); 36 | GX_TexCoord2s16(s1, t1); 37 | 38 | GX_Position2s16(x2, y2); 39 | GX_Color1u32(c); 40 | GX_TexCoord2s16(s2, t1); 41 | 42 | GX_Position2s16(x2, y1); 43 | GX_Color1u32(c); 44 | GX_TexCoord2s16(s2, t2); 45 | 46 | GX_Position2s16(x1, y1); 47 | GX_Color1u32(c); 48 | GX_TexCoord2s16(s1, t2); 49 | } 50 | 51 | void GXFontAllocState(void) 52 | { 53 | Mtx44 projection; 54 | guOrtho(projection, 55 | -screen.y - screen.h / 2, screen.y + screen.h / 2, 56 | -screen.x - screen.w / 2, screen.x + screen.w / 2, 0., 1.); 57 | 58 | SYS_SetFontEncoding(SYS_FONTENC_ANSI); 59 | fontdata = SYS_AllocArenaMemHi(SYS_FONTSIZE_ANSI, 32); 60 | 61 | SYS_InitFont(fontdata); 62 | fontdata->sheet_image = (fontdata->sheet_image + 31) & ~31; 63 | 64 | GX_InitTexObj(&texobj, (void *)fontdata + fontdata->sheet_image, 65 | fontdata->sheet_width, fontdata->sheet_height, 66 | fontdata->sheet_format, GX_CLAMP, GX_CLAMP, GX_FALSE); 67 | GX_InitTexObjLOD(&texobj, GX_LINEAR, GX_LINEAR, 0., 0., 0., GX_TRUE, GX_TRUE, GX_ANISO_4); 68 | GX_InitTexCacheRegion(&texregion, GX_FALSE, 0xC0000, GX_TEXCACHE_128K, 0x00000, GX_TEXCACHE_NONE); 69 | 70 | displist = GXAllocBuffer(GX_FIFO_MINSIZE); 71 | GX_BeginDispList(displist, GX_FIFO_MINSIZE); 72 | 73 | GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); 74 | GX_SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0); 75 | GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); 76 | GX_SetZCompLoc(GX_FALSE); 77 | 78 | GX_SetNumChans(1); 79 | GX_SetNumTexGens(1); 80 | GX_SetNumIndStages(0); 81 | GX_SetNumTevStages(1); 82 | 83 | GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_ALPHA, GX_CH_BLUE, GX_CH_GREEN, GX_CH_RED); 84 | 85 | GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); 86 | GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_RASC, GX_CC_ZERO); 87 | GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 88 | GX_SetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_TEXA, GX_CA_RASA, GX_CA_ZERO); 89 | GX_SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 90 | GX_SetTevDirect(GX_TEVSTAGE0); 91 | 92 | GX_ClearVtxDesc(); 93 | GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); 94 | GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT); 95 | GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); 96 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); 97 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); 98 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0); 99 | 100 | GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); 101 | GX_SetCurrentMtx(GX_PNMTX1); 102 | 103 | GX_LoadTexObjPreloaded(&texobj, &texregion, GX_TEXMAP0); 104 | 105 | dispsize = GX_EndDispList(); 106 | displist = realloc_in_place(displist, dispsize); 107 | } 108 | 109 | void GXFontSetState(void) 110 | { 111 | if (state.current != STATE_FONT) { 112 | state.current = STATE_FONT; 113 | GX_CallDispList(displist, dispsize); 114 | } 115 | } 116 | 117 | unsigned GUIFontHeight(const struct GUIFont *font) 118 | { 119 | return fontdata->cell_height; 120 | } 121 | 122 | unsigned GUIFontGlyphWidth(const struct GUIFont *font, uint32_t glyph) 123 | { 124 | uint8_t *table = (void *)fontdata + fontdata->width_table; 125 | 126 | if (glyph < fontdata->first_char || glyph > fontdata->last_char) 127 | return table[fontdata->inval_char]; 128 | else 129 | return table[glyph - fontdata->first_char]; 130 | } 131 | 132 | void GUIFontIconMetrics(const struct GUIFont *font, enum GUIIcon icon, unsigned *w, unsigned *h) 133 | { 134 | if (w) *w = 0; 135 | if (h) *h = 0; 136 | } 137 | 138 | void GUIFontDrawGlyph(struct GUIFont *font, int x, int y, uint32_t color, uint32_t glyph) 139 | { 140 | void *image; 141 | int32_t xpos, ypos, width; 142 | 143 | SYS_GetFontTexture(glyph, &image, &xpos, &ypos, &width); 144 | 145 | if (GX_GetTexObjData(&texobj) != (void *)MEM_VIRTUAL_TO_PHYSICAL(image)) { 146 | GX_InitTexObjData(&texobj, image); 147 | GX_LoadTexObjPreloaded(&texobj, &texregion, GX_TEXMAP0); 148 | } 149 | 150 | GXFontDrawCell(x, y, color, xpos, ypos); 151 | } 152 | 153 | void GUIFontDrawIcon(struct GUIFont *font, int x, int y, enum GUIAlignment align, enum GUIOrientation orient, uint32_t color, enum GUIIcon icon) 154 | { 155 | } 156 | 157 | void GUIFontDrawIconSize(struct GUIFont *font, int x, int y, int w, int h, uint32_t color, enum GUIIcon icon) 158 | { 159 | } 160 | -------------------------------------------------------------------------------- /source/gx_overlay.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include 11 | #include "gx.h" 12 | #include "state.h" 13 | #include "video.h" 14 | 15 | static void *displist; 16 | static uint32_t dispsize; 17 | 18 | static TPLFile tdf; 19 | static GXTexObj texobj; 20 | 21 | void GXOverlayDrawRect(rect_t rect) 22 | { 23 | uint16_t width, height; 24 | 25 | if (TPL_GetTextureInfo(&tdf, MIN(state.overlay_id, tdf.ntextures - 1), NULL, &width, &height) == 0) { 26 | uint16_t scale = width < height ? width / rect.w : height / rect.h; 27 | 28 | Mtx viewmodel; 29 | guMtxRotDeg(viewmodel, 'z', state.rotation); 30 | guMtxScaleApply(viewmodel, viewmodel, 31 | state.overlay_scale.x ? state.overlay_scale.x : state.zoom.x / scale, 32 | state.overlay_scale.y ? state.overlay_scale.y : state.zoom.y / scale, 1); 33 | GX_LoadPosMtxImm(viewmodel, GX_PNMTX1); 34 | 35 | GX_LoadTexObj(&texobj, GX_TEXMAP0); 36 | 37 | GX_Begin(GX_QUADS, GX_VTXFMT0, 4); 38 | 39 | GX_Position2s16(-width, -height); 40 | GX_TexCoord2s16(0, 0); 41 | 42 | GX_Position2s16(+width, -height); 43 | GX_TexCoord2s16(width, 0); 44 | 45 | GX_Position2s16(+width, +height); 46 | GX_TexCoord2s16(width, height); 47 | 48 | GX_Position2s16(-width, +height); 49 | GX_TexCoord2s16(0, height); 50 | } 51 | } 52 | 53 | void GXOverlayAllocState(void) 54 | { 55 | Mtx44 projection; 56 | guOrtho(projection, 57 | -screen.y - screen.h / 2, screen.y + screen.h / 2, 58 | -screen.x - screen.w / 2, screen.x + screen.w / 2, 0., 1.); 59 | 60 | displist = GXAllocBuffer(GX_FIFO_MINSIZE); 61 | GX_BeginDispList(displist, GX_FIFO_MINSIZE); 62 | 63 | GX_SetBlendMode(GX_BM_BLEND, GX_BL_ONE, GX_BL_INVSRCALPHA, GX_LO_CLEAR); 64 | GX_SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0); 65 | GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); 66 | GX_SetZCompLoc(GX_FALSE); 67 | 68 | GX_SetNumChans(0); 69 | GX_SetNumTexGens(1); 70 | GX_SetNumIndStages(0); 71 | GX_SetNumTevStages(1); 72 | 73 | GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); 74 | 75 | GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORNULL); 76 | GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC); 77 | GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 78 | GX_SetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_TEXA); 79 | GX_SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 80 | GX_SetTevDirect(GX_TEVSTAGE0); 81 | 82 | GX_ClearVtxDesc(); 83 | GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); 84 | GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); 85 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 1); 86 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0); 87 | 88 | GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); 89 | GX_SetCurrentMtx(GX_PNMTX1); 90 | 91 | dispsize = GX_EndDispList(); 92 | displist = realloc_in_place(displist, dispsize); 93 | } 94 | 95 | void GXOverlaySetState(void) 96 | { 97 | if (state.current != STATE_OVERLAY) { 98 | state.current = STATE_OVERLAY; 99 | GX_CallDispList(displist, dispsize); 100 | } 101 | } 102 | 103 | void GXOverlayReadMemEx(void *buffer, int size, int index) 104 | { 105 | TPL_CloseTPLFile(&tdf); 106 | TPL_OpenTPLFromMemory(&tdf, buffer, size); 107 | TPL_GetTexture(&tdf, MIN(index, tdf.ntextures - 1), &texobj); 108 | } 109 | 110 | void GXOverlayReadMem(void *buffer, int size, int index) 111 | { 112 | TPL_CloseTPLFile(&tdf); 113 | TPL_OpenTPLFromHandle(&tdf, GXOpenMem(buffer, size)); 114 | TPL_GetTexture(&tdf, MIN(index, tdf.ntextures - 1), &texobj); 115 | } 116 | 117 | void GXOverlayReadFile(const char *file, int index) 118 | { 119 | TPL_CloseTPLFile(&tdf); 120 | TPL_OpenTPLFromHandle(&tdf, GXOpenFile(file)); 121 | TPL_GetTexture(&tdf, MIN(index, tdf.ntextures - 1), &texobj); 122 | } 123 | -------------------------------------------------------------------------------- /source/gx_packed.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include 11 | #include "gx.h" 12 | #include "state.h" 13 | 14 | static void GXPackedCopyRGB(GXTexObj texobj, rect_t dst_rect, rect_t src_rect, uint8_t level) 15 | { 16 | void *ptr; 17 | uint16_t width, height; 18 | uint8_t format, wrap_s, wrap_t, mipmap; 19 | 20 | GX_GetTexObjAll(&texobj, &ptr, &width, &height, &format, &wrap_s, &wrap_t, &mipmap); 21 | 22 | switch (format) { 23 | case GX_TF_I4: 24 | format = GX_CTF_R4; 25 | break; 26 | case GX_TF_I8: 27 | format = GX_CTF_R8; 28 | break; 29 | case GX_TF_IA4: 30 | format = GX_CTF_RA4; 31 | break; 32 | case GX_TF_IA8: 33 | format = GX_CTF_RA8; 34 | break; 35 | } 36 | 37 | GX_SetScissor(0, 0, width << level, height << level); 38 | GX_SetScissorBoxOffset(0, 0); 39 | GX_ClearBoundingBox(); 40 | 41 | GX_Begin(GX_QUADS, GX_VTXFMT0, 4); 42 | 43 | GX_Position2s16(dst_rect.x, dst_rect.y); 44 | GX_TexCoord2s16(src_rect.x, src_rect.y); 45 | 46 | GX_Position2s16(dst_rect.x + dst_rect.w, dst_rect.y); 47 | GX_TexCoord2s16(src_rect.x + src_rect.w, src_rect.y); 48 | 49 | GX_Position2s16(dst_rect.x + dst_rect.w, dst_rect.y + dst_rect.h); 50 | GX_TexCoord2s16(src_rect.x + src_rect.w, src_rect.y + src_rect.h); 51 | 52 | GX_Position2s16(dst_rect.x, dst_rect.y + dst_rect.h); 53 | GX_TexCoord2s16(src_rect.x, src_rect.y + src_rect.h); 54 | 55 | GX_SetTexCopySrc(0, 0, width << level, height << level); 56 | GX_SetTexCopyDst(width, height, format, level); 57 | 58 | GX_CopyTex(ptr, GX_FALSE); 59 | } 60 | 61 | static void GXPackedCopyYUV(GXTexObj texobj, rect_t dst_rect, rect_t src_rect, uint8_t level) 62 | { 63 | void *ptr; 64 | uint16_t width, height; 65 | uint8_t format, wrap_s, wrap_t, mipmap; 66 | 67 | GX_GetTexObjAll(&texobj, &ptr, &width, &height, &format, &wrap_s, &wrap_t, &mipmap); 68 | 69 | switch (format) { 70 | case GX_TF_RGBA8: 71 | format = GX_CTF_YUVA8; 72 | break; 73 | case GX_TF_CI4: 74 | format = GX_TF_I4; 75 | break; 76 | case GX_TF_CI8: 77 | format = GX_TF_I8; 78 | break; 79 | case GX_TF_CI14: 80 | format = GX_TF_IA8; 81 | break; 82 | } 83 | 84 | GX_SetScissor(0, 0, width << level, height << level); 85 | GX_SetScissorBoxOffset(0, 0); 86 | GX_ClearBoundingBox(); 87 | 88 | GX_Begin(GX_QUADS, GX_VTXFMT0, 4); 89 | 90 | GX_Position2s16(dst_rect.x, dst_rect.y); 91 | GX_TexCoord2s16(src_rect.x, src_rect.y); 92 | 93 | GX_Position2s16(dst_rect.x + dst_rect.w, dst_rect.y); 94 | GX_TexCoord2s16(src_rect.x + src_rect.w, src_rect.y); 95 | 96 | GX_Position2s16(dst_rect.x + dst_rect.w, dst_rect.y + dst_rect.h); 97 | GX_TexCoord2s16(src_rect.x + src_rect.w, src_rect.y + src_rect.h); 98 | 99 | GX_Position2s16(dst_rect.x, dst_rect.y + dst_rect.h); 100 | GX_TexCoord2s16(src_rect.x, src_rect.y + src_rect.h); 101 | 102 | GX_SetTexCopySrc(0, 0, width << level, height << level); 103 | GX_SetTexCopyDst(width, height, format, level); 104 | 105 | GX_CopyTex(ptr, GX_FALSE); 106 | } 107 | 108 | void GXPackedApplyMix(gx_surface_t *dst, gx_surface_t *src) 109 | { 110 | uint8_t alpha[3] = { 111 | state.filter_weight[0] * 255. + .5, 112 | state.filter_weight[1] * 255. + .5, 113 | state.filter_weight[2] * 255. + .5 114 | }; 115 | 116 | Mtx44 projection; 117 | guOrtho(projection, 0., 1024., 0., 1024., 0., 1.); 118 | 119 | GX_SetBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR); 120 | GX_SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0); 121 | GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); 122 | GX_SetZCompLoc(GX_FALSE); 123 | 124 | GX_SetNumChans(0); 125 | GX_SetNumTexGens(1); 126 | GX_SetNumIndStages(0); 127 | GX_SetNumTevStages(2); 128 | 129 | GX_SetTevKColor(GX_KCOLOR0, (GXColor){alpha[2], alpha[1], alpha[0]}); 130 | 131 | GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORNULL); 132 | GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_TEXC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO); 133 | GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 134 | GX_SetTevKAlphaSel(GX_TEVSTAGE0, GX_TEV_KASEL_1); 135 | GX_SetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_KONST, GX_CA_ZERO); 136 | GX_SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_COMP_BGR24_GT, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 137 | GX_SetTevDirect(GX_TEVSTAGE0); 138 | 139 | GX_SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP1, GX_COLORNULL); 140 | GX_SetTevKColorSel(GX_TEVSTAGE1, GX_TEV_KCSEL_K0); 141 | GX_SetTevColorIn(GX_TEVSTAGE1, GX_CC_TEXC, GX_CC_CPREV, GX_CC_KONST, GX_CC_ZERO); 142 | GX_SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 143 | GX_SetTevKAlphaSel(GX_TEVSTAGE1, GX_TEV_KASEL_1); 144 | GX_SetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_KONST, GX_CA_APREV); 145 | GX_SetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_COMP_BGR24_GT, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 146 | GX_SetTevDirect(GX_TEVSTAGE1); 147 | 148 | GX_ClearVtxDesc(); 149 | GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); 150 | GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); 151 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); 152 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0); 153 | 154 | GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); 155 | GX_SetCurrentMtx(GX_PNMTX0); 156 | GX_SetViewport(0., 0., 1024., 1024., 0., 1.); 157 | 158 | GX_SetFieldMask(GX_TRUE, GX_TRUE); 159 | GX_SetFieldMode(GX_FALSE, GX_FALSE); 160 | 161 | GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR); 162 | GX_SetCopyFilter(GX_FALSE, NULL, GX_FALSE, NULL); 163 | 164 | if (src->dirty) GX_PreloadEntireTexture(&src->obj[0], &src->region[0]); 165 | if (dst->dirty) GX_PreloadEntireTexture(&dst->obj[0], &dst->region[0]); 166 | GX_LoadTexObjPreloaded(&src->obj[0], &src->region[0], GX_TEXMAP0); 167 | GX_LoadTexObjPreloaded(&dst->obj[0], &dst->region[0], GX_TEXMAP1); 168 | 169 | GXPackedCopyRGB(dst->obj[0], dst->rect, src->rect, 0); 170 | 171 | dst->dirty = true; src->dirty = false; 172 | } 173 | 174 | void GXPackedApplyYUV(gx_surface_t *dst, gx_surface_t *src) 175 | { 176 | Mtx44 projection; 177 | guOrtho(projection, 0., 1024., 0., 1024., 0., 1.); 178 | 179 | GX_SetBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR); 180 | GX_SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0); 181 | GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); 182 | GX_SetZCompLoc(GX_FALSE); 183 | 184 | GX_SetNumChans(0); 185 | GX_SetNumTexGens(1); 186 | GX_SetNumIndStages(0); 187 | GX_SetNumTevStages(1); 188 | 189 | GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_BLUE, GX_CH_GREEN, GX_CH_RED, GX_CH_ALPHA); 190 | 191 | GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORNULL); 192 | GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_TEXC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO); 193 | GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 194 | GX_SetTevKAlphaSel(GX_TEVSTAGE0, GX_TEV_KASEL_1); 195 | GX_SetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_KONST, GX_CA_ZERO); 196 | GX_SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_COMP_BGR24_GT, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 197 | GX_SetTevDirect(GX_TEVSTAGE0); 198 | 199 | GX_ClearVtxDesc(); 200 | GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); 201 | GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); 202 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); 203 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0); 204 | 205 | GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); 206 | GX_SetCurrentMtx(GX_PNMTX0); 207 | GX_SetViewport(0., 0., 1024., 1024., 0., 1.); 208 | 209 | GX_SetFieldMask(GX_TRUE, GX_TRUE); 210 | GX_SetFieldMode(GX_FALSE, GX_FALSE); 211 | 212 | GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR); 213 | GX_SetCopyFilter(GX_FALSE, NULL, GX_FALSE, NULL); 214 | 215 | if (src->dirty) GX_PreloadEntireTexture(&src->obj[0], &src->region[0]); 216 | GX_LoadTexObjPreloaded(&src->obj[0], &src->region[0], GX_TEXMAP0); 217 | 218 | GXPackedCopyYUV(dst->obj[0], dst->rect, src->rect, 1); 219 | 220 | dst->dirty = true; src->dirty = false; 221 | } 222 | -------------------------------------------------------------------------------- /source/gx_prescale.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "gx.h" 13 | #include "state.h" 14 | #include "util.h" 15 | 16 | static float texmtx[][3][4] ATTRIBUTE_ALIGN(32) = { 17 | { 18 | { +1., +0., +0., +1./64. }, 19 | { +0., +1., +0., +1./64. }, 20 | { +0., +0., +1., +0. } 21 | }, { 22 | { +0., +1., +0., +1./64. }, 23 | { -1., +0., +0., +1./64. }, 24 | { +0., +0., +1., +0. } 25 | }, { 26 | { -1., +0., +0., +1./64. }, 27 | { +0., -1., +0., +1./64. }, 28 | { +0., +0., +1., +0. } 29 | }, { 30 | { +0., -1., +0., +1./64. }, 31 | { +1., +0., +0., +1./64. }, 32 | { +0., +0., +1., +0. } 33 | }, 34 | }; 35 | 36 | static uint8_t texdata[][8 * 8] ATTRIBUTE_ALIGN(32) = { 37 | { 38 | 0x60, 0x90, 0x6C, 0x9C, 0x63, 0x93, 0x6F, 0x9F, 39 | 0x80, 0x70, 0x8C, 0x7C, 0x83, 0x73, 0x8F, 0x7F, 40 | 0x68, 0x98, 0x64, 0x94, 0x6B, 0x9B, 0x67, 0x97, 41 | 0x88, 0x78, 0x84, 0x74, 0x8B, 0x7B, 0x87, 0x77, 42 | 0x62, 0x92, 0x6E, 0x9E, 0x61, 0x91, 0x6D, 0x9D, 43 | 0x82, 0x72, 0x8E, 0x7E, 0x81, 0x71, 0x8D, 0x7D, 44 | 0x6A, 0x9A, 0x66, 0x96, 0x69, 0x99, 0x65, 0x95, 45 | 0x8A, 0x7A, 0x86, 0x76, 0x89, 0x79, 0x85, 0x75, 46 | }, { 47 | 0x78, 0x6A, 0x6C, 0x7A, 0x83, 0x8F, 0x91, 0x85, 48 | 0x68, 0x60, 0x62, 0x6E, 0x8D, 0x9B, 0x9D, 0x93, 49 | 0x76, 0x66, 0x64, 0x70, 0x8B, 0x99, 0x9F, 0x95, 50 | 0x7E, 0x74, 0x72, 0x7C, 0x81, 0x89, 0x97, 0x87, 51 | 0x82, 0x8E, 0x90, 0x84, 0x79, 0x6B, 0x6D, 0x7B, 52 | 0x8C, 0x9A, 0x9C, 0x92, 0x69, 0x61, 0x63, 0x6F, 53 | 0x8A, 0x98, 0x9E, 0x94, 0x77, 0x67, 0x65, 0x71, 54 | 0x80, 0x88, 0x96, 0x86, 0x7F, 0x75, 0x73, 0x7D, 55 | } 56 | }; 57 | 58 | static GXTexObj texobj; 59 | 60 | static uint16_t tlutdata[GX_MAX_TEXMAP][3][256] ATTRIBUTE_ALIGN(32); 61 | static GXTlutObj tlutobj[GX_MAX_TEXMAP][3]; 62 | 63 | static void GXPrescaleCopyChannel(GXTexObj texobj, rect_t dst_rect, rect_t src_rect, uint8_t channel) 64 | { 65 | void *ptr; 66 | uint16_t width, height; 67 | uint8_t format, wrap_s, wrap_t, mipmap; 68 | 69 | GX_GetTexObjAll(&texobj, &ptr, &width, &height, &format, &wrap_s, &wrap_t, &mipmap); 70 | 71 | if (channel == GX_CH_RED) { 72 | GX_SetScissor(0, 0, width << mipmap, height << mipmap); 73 | GX_SetScissorBoxOffset(0, 0); 74 | GX_ClearBoundingBox(); 75 | } 76 | 77 | GX_Begin(GX_QUADS, GX_VTXFMT0, 4); 78 | 79 | GX_Position2s16(dst_rect.x, dst_rect.y); 80 | GX_TexCoord2s16(src_rect.x, src_rect.y); 81 | 82 | GX_Position2s16(dst_rect.x + dst_rect.w, dst_rect.y); 83 | GX_TexCoord2s16(src_rect.x + src_rect.w, src_rect.y); 84 | 85 | GX_Position2s16(dst_rect.x + dst_rect.w, dst_rect.y + dst_rect.h); 86 | GX_TexCoord2s16(src_rect.x + src_rect.w, src_rect.y + src_rect.h); 87 | 88 | GX_Position2s16(dst_rect.x, dst_rect.y + dst_rect.h); 89 | GX_TexCoord2s16(src_rect.x, src_rect.y + src_rect.h); 90 | 91 | GX_SetTexCopySrc(0, 0, width << mipmap, height << mipmap); 92 | GX_SetTexCopyDst(width, height, GX_CTF_R8, mipmap); 93 | 94 | GX_CopyTex(ptr, channel == GX_CH_BLUE ? GX_TRUE : GX_FALSE); 95 | } 96 | 97 | void GXPrescaleApply(gx_surface_t *dst, gx_surface_t *src) 98 | { 99 | Mtx44 projection; 100 | guOrtho(projection, 0., 1024., 0., 1024., 0., 1.); 101 | 102 | GX_SetBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR); 103 | GX_SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); 104 | GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); 105 | GX_SetZCompLoc(GX_FALSE); 106 | 107 | GX_SetNumChans(0); 108 | GX_SetNumTexGens(1); 109 | GX_SetNumIndStages(0); 110 | GX_SetNumTevStages(1); 111 | 112 | GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); 113 | 114 | GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORNULL); 115 | GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_TEXA, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO); 116 | GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 117 | GX_SetTevDirect(GX_TEVSTAGE0); 118 | 119 | GX_ClearVtxDesc(); 120 | GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); 121 | GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); 122 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); 123 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0); 124 | 125 | GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); 126 | GX_SetCurrentMtx(GX_PNMTX0); 127 | GX_SetViewport(0., 0., 1024., 1024., 0., 1.); 128 | 129 | GX_SetFieldMask(GX_TRUE, GX_TRUE); 130 | GX_SetFieldMode(GX_FALSE, GX_FALSE); 131 | 132 | GX_SetPixelFmt(GX_PF_Y8, GX_ZC_LINEAR); 133 | GX_SetCopyFilter(GX_FALSE, NULL, GX_FALSE, NULL); 134 | 135 | GX_LoadTlut(&src->lutobj[GX_CH_RED], GX_TLUT0); 136 | GX_LoadTlut(&src->lutobj[GX_CH_GREEN], GX_TLUT1); 137 | GX_LoadTlut(&src->lutobj[GX_CH_BLUE], GX_TLUT2); 138 | 139 | for (int ch = GX_CH_RED; ch <= GX_CH_BLUE; ch++) { 140 | if (src->dirty) GX_PreloadEntireTexture(&src->obj[ch], &src->region[ch]); 141 | GX_LoadTexObjPreloaded(&src->obj[ch], &src->region[ch], GX_TEXMAP0); 142 | 143 | GXPrescaleCopyChannel(dst->obj[ch], dst->rect, src->rect, ch); 144 | } 145 | 146 | dst->dirty = true; src->dirty = false; 147 | } 148 | 149 | void GXPrescaleApplyDither(gx_surface_t *dst, gx_surface_t *src) 150 | { 151 | Mtx44 projection; 152 | guOrtho(projection, 0., 1024., 0., 1024., 0., 1.); 153 | 154 | GX_SetBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR); 155 | GX_SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); 156 | GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); 157 | GX_SetZCompLoc(GX_FALSE); 158 | 159 | GX_SetNumChans(0); 160 | GX_SetNumTexGens(2); 161 | GX_SetNumIndStages(0); 162 | GX_SetNumTevStages(3); 163 | 164 | GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); 165 | GX_SetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_POS, GX_IDENTITY); 166 | 167 | GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); 168 | 169 | GX_SetTevKColor(GX_KCOLOR0, (GXColor){4, 4, 4, 4}); 170 | 171 | GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORNULL); 172 | GX_SetTevKColorSel(GX_TEVSTAGE0, GX_TEV_KCSEL_1_4); 173 | GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_KONST, GX_CC_ZERO); 174 | GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_FALSE, GX_TEVPREV); 175 | GX_SetTevAlphaIn(GX_TEVSTAGE0, GX_CA_TEXA, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); 176 | GX_SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_FALSE, GX_TEVPREV); 177 | GX_SetTevDirect(GX_TEVSTAGE0); 178 | 179 | GX_SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD1, GX_TEXMAP7, GX_COLORNULL); 180 | GX_SetTevColorIn(GX_TEVSTAGE1, GX_CC_TEXA, GX_CC_ZERO, GX_CC_ZERO, GX_CC_CPREV); 181 | GX_SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_SUBHALF, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 182 | GX_SetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV); 183 | GX_SetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 184 | GX_SetTevDirect(GX_TEVSTAGE1); 185 | 186 | GX_SetTevOrder(GX_TEVSTAGE2, GX_TEXCOORDNULL, GX_TEXMAP_NULL, GX_COLORNULL); 187 | GX_SetTevKColorSel(GX_TEVSTAGE2, GX_TEV_KCSEL_K0); 188 | GX_SetTevColorIn(GX_TEVSTAGE2, GX_CC_ZERO, GX_CC_CPREV, GX_CC_KONST, GX_CC_APREV); 189 | GX_SetTevColorOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 190 | GX_SetTevDirect(GX_TEVSTAGE2); 191 | 192 | GX_ClearVtxDesc(); 193 | GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); 194 | GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); 195 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); 196 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0); 197 | 198 | GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); 199 | GX_SetCurrentMtx(GX_PNMTX0); 200 | GX_SetViewport(0., 0., 1024., 1024., 0., 1.); 201 | 202 | GX_SetFieldMask(GX_TRUE, GX_TRUE); 203 | GX_SetFieldMode(GX_FALSE, GX_FALSE); 204 | 205 | GX_SetPixelFmt(GX_PF_Y8, GX_ZC_LINEAR); 206 | GX_SetCopyFilter(GX_FALSE, NULL, GX_FALSE, NULL); 207 | 208 | GX_LoadTlut(&src->lutobj[GX_CH_RED], GX_TLUT0); 209 | GX_LoadTlut(&src->lutobj[GX_CH_GREEN], GX_TLUT1); 210 | GX_LoadTlut(&src->lutobj[GX_CH_BLUE], GX_TLUT2); 211 | 212 | for (int ch = GX_CH_RED; ch <= GX_CH_BLUE; ch++) { 213 | if (src->dirty) GX_PreloadEntireTexture(&src->obj[ch], &src->region[ch]); 214 | GX_LoadTexObjPreloaded(&src->obj[ch], &src->region[ch], GX_TEXMAP0); 215 | 216 | GX_LoadTexObj(&texobj, GX_TEXMAP7); 217 | 218 | GXPrescaleCopyChannel(dst->obj[ch], dst->rect, src->rect, ch); 219 | } 220 | 221 | dst->dirty = true; src->dirty = false; 222 | } 223 | 224 | void GXPrescaleApplyDitherFast(gx_surface_t *dst, gx_surface_t *src) 225 | { 226 | Mtx44 projection; 227 | guOrtho(projection, 0., 1024., 0., 1024., 0., 1.); 228 | 229 | GX_SetBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR); 230 | GX_SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); 231 | GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); 232 | GX_SetZCompLoc(GX_FALSE); 233 | 234 | GX_SetNumChans(0); 235 | GX_SetNumTexGens(2); 236 | GX_SetNumIndStages(0); 237 | GX_SetNumTevStages(2); 238 | 239 | GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); 240 | GX_SetTexCoordGen2(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_POS, GX_IDENTITY, GX_FALSE, GX_DTTMTX0); 241 | 242 | GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); 243 | 244 | GX_SetTevKColor(GX_KCOLOR0, (GXColor){4, 4, 4, 4}); 245 | 246 | GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORNULL); 247 | GX_SetTevKColorSel(GX_TEVSTAGE0, GX_TEV_KCSEL_1_4); 248 | GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_KONST, GX_CC_A0); 249 | GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 250 | GX_SetTevAlphaIn(GX_TEVSTAGE0, GX_CA_TEXA, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); 251 | GX_SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 252 | GX_SetTevDirect(GX_TEVSTAGE0); 253 | 254 | if (state.dither > DITHER_THRESHOLD) { 255 | GX_SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD1, GX_TEXMAP7, GX_COLORNULL); 256 | GX_SetTevKColorSel(GX_TEVSTAGE1, GX_TEV_KCSEL_K0); 257 | GX_SetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_CPREV, GX_CC_KONST, GX_CC_APREV); 258 | GX_SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 259 | GX_SetTevAlphaIn(GX_TEVSTAGE1, GX_CA_TEXA, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); 260 | GX_SetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_SUBHALF, GX_CS_SCALE_1, GX_FALSE, GX_TEVREG0); 261 | GX_SetTevDirect(GX_TEVSTAGE1); 262 | } else { 263 | GX_SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORDNULL, GX_TEXMAP_NULL, GX_COLORNULL); 264 | GX_SetTevKColorSel(GX_TEVSTAGE1, GX_TEV_KCSEL_K0); 265 | GX_SetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_CPREV, GX_CC_KONST, GX_CC_APREV); 266 | GX_SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 267 | GX_SetTevAlphaIn(GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_A0); 268 | GX_SetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_FALSE, GX_TEVREG0); 269 | GX_SetTevDirect(GX_TEVSTAGE1); 270 | } 271 | 272 | GX_ClearVtxDesc(); 273 | GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); 274 | GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); 275 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); 276 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0); 277 | GX_SetArray(GX_TEXMTXARRAY, texmtx, sizeof(texmtx[0])); 278 | 279 | GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); 280 | GX_SetCurrentMtx(GX_PNMTX0); 281 | GX_SetViewport(0., 0., 1024., 1024., 0., 1.); 282 | 283 | GX_SetFieldMask(GX_TRUE, GX_TRUE); 284 | GX_SetFieldMode(GX_FALSE, GX_FALSE); 285 | 286 | GX_SetPixelFmt(GX_PF_Y8, GX_ZC_LINEAR); 287 | GX_SetCopyFilter(GX_FALSE, NULL, GX_FALSE, NULL); 288 | 289 | GX_LoadTlut(&src->lutobj[GX_CH_RED], GX_TLUT0); 290 | GX_LoadTlut(&src->lutobj[GX_CH_GREEN], GX_TLUT1); 291 | GX_LoadTlut(&src->lutobj[GX_CH_BLUE], GX_TLUT2); 292 | 293 | for (int ch = GX_CH_RED; ch <= GX_CH_BLUE; ch++) { 294 | int idx = state.retrace + ch; 295 | 296 | if (src->dirty) GX_PreloadEntireTexture(&src->obj[ch], &src->region[ch]); 297 | GX_LoadTexObjPreloaded(&src->obj[ch], &src->region[ch], GX_TEXMAP0); 298 | 299 | if (state.dither > DITHER_THRESHOLD) { 300 | GX_LoadTexObj(&texobj, GX_TEXMAP7); 301 | GX_LoadTexMtxIdx(idx % 4, GX_DTTMTX0, GX_MTX3x4); 302 | } 303 | 304 | GX_SetTevColorS10(GX_TEVREG0, idx % 2 ? (GXColorS10){-32, -32, -32, -32} 305 | : (GXColorS10){+15, +15, +15, +15}); 306 | 307 | GXPrescaleCopyChannel(dst->obj[ch], dst->rect, src->rect, ch); 308 | } 309 | 310 | dst->dirty = true; src->dirty = false; 311 | } 312 | 313 | void GXPrescaleApplyBlend(gx_surface_t *dst, gx_surface_t **src, uint8_t *alpha, uint32_t count) 314 | { 315 | Mtx44 projection; 316 | guOrtho(projection, 0., 1024., 0., 1024., 0., 1.); 317 | 318 | GX_SetBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR); 319 | GX_SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); 320 | GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); 321 | GX_SetZCompLoc(GX_FALSE); 322 | 323 | GX_SetNumChans(0); 324 | GX_SetNumTexGens(1); 325 | GX_SetNumIndStages(0); 326 | GX_SetNumTevStages(count); 327 | 328 | GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); 329 | 330 | GX_SetTevKColor(GX_KCOLOR0, (GXColor){alpha[0], alpha[4]}); 331 | GX_SetTevKColor(GX_KCOLOR1, (GXColor){alpha[1], alpha[5]}); 332 | GX_SetTevKColor(GX_KCOLOR2, (GXColor){alpha[2], alpha[6]}); 333 | GX_SetTevKColor(GX_KCOLOR3, (GXColor){alpha[3], alpha[7]}); 334 | 335 | for (int i = 0; i < count; i++) { 336 | GX_SetTevOrder(GX_TEVSTAGE0 + i, GX_TEXCOORD0, GX_TEXMAP0 + i, GX_COLORNULL); 337 | GX_SetTevKColorSel(GX_TEVSTAGE0 + i, GX_TEV_KCSEL_K0_R + i); 338 | GX_SetTevColorIn(GX_TEVSTAGE0 + i, GX_CC_TEXA, i == 0 ? GX_CC_ZERO : GX_CC_CPREV, GX_CC_KONST, GX_CC_ZERO); 339 | GX_SetTevColorOp(GX_TEVSTAGE0 + i, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 340 | GX_SetTevDirect(GX_TEVSTAGE0 + i); 341 | } 342 | 343 | GX_ClearVtxDesc(); 344 | GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); 345 | GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); 346 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); 347 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0); 348 | 349 | GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); 350 | GX_SetCurrentMtx(GX_PNMTX0); 351 | GX_SetViewport(0., 0., 1024., 1024., 0., 1.); 352 | 353 | GX_SetFieldMask(GX_TRUE, GX_TRUE); 354 | GX_SetFieldMode(GX_FALSE, GX_FALSE); 355 | 356 | GX_SetPixelFmt(GX_PF_Y8, GX_ZC_LINEAR); 357 | GX_SetCopyFilter(GX_FALSE, NULL, GX_FALSE, NULL); 358 | 359 | for (int ch = GX_CH_RED; ch <= GX_CH_BLUE; ch++) { 360 | for (int i = 0; i < count; i++) { 361 | GX_LoadTlut(&src[i]->lutobj[ch], GX_GetTexObjTlut(&src[i]->obj[ch])); 362 | GX_LoadTexObj(&src[i]->obj[ch], GX_TEXMAP0 + i); 363 | } 364 | 365 | GXPrescaleCopyChannel(dst->obj[ch], dst->rect, src[0]->rect, ch); 366 | } 367 | 368 | dst->dirty = true; src[0]->dirty = false; 369 | } 370 | 371 | static void GXPrescaleTlut(gx_surface_t **surfaces, uint8_t *alpha, uint32_t count) 372 | { 373 | float next = 1.; 374 | 375 | for (int i = count - 1; i >= 0; i--) { 376 | float curr = GXCast1u8f32(alpha[i]); 377 | float scale = LERP(next, 0., curr, 1.); 378 | 379 | next *= curr; 380 | 381 | for (int ch = GX_CH_RED; ch <= GX_CH_BLUE; ch++) { 382 | void *dst = tlutdata[i][ch]; 383 | void *src = surfaces[i]->lutbuf[ch]; 384 | 385 | for (int j = 0; j < 32; j++) { 386 | register float rega, regb, regc, regd; 387 | asm volatile ( 388 | "psq_l %0, 0 (%4), 0, 5 \n" 389 | "psq_l %1, 4 (%4), 0, 5 \n" 390 | "psq_l %2, 8 (%4), 0, 5 \n" 391 | "psq_l %3, 12 (%4), 0, 5 \n" 392 | 393 | "ps_madd %0, %0, %6, %7 \n" 394 | "ps_madd %1, %1, %6, %7 \n" 395 | "ps_madd %2, %2, %6, %7 \n" 396 | "ps_madd %3, %3, %6, %7 \n" 397 | 398 | "psq_st %0, 0 (%5), 0, 5 \n" 399 | "psq_st %1, 4 (%5), 0, 5 \n" 400 | "psq_st %2, 8 (%5), 0, 5 \n" 401 | "psq_st %3, 12 (%5), 0, 5 \n" 402 | 403 | "addi %4, %4, 16 \n" 404 | "addi %5, %5, 16 \n" 405 | : "=f" (rega), "=f" (regb), "=f" (regc), "=f" (regd), 406 | "+b" (src), "+b" (dst) 407 | : "f" (scale), "f" (0.5f) 408 | ); 409 | } 410 | } 411 | } 412 | 413 | DCStoreRange(tlutdata, sizeof(tlutdata[0]) * count); 414 | } 415 | 416 | void GXPrescaleApplyBlendDither(gx_surface_t *dst, gx_surface_t **src, uint8_t *alpha, uint32_t count) 417 | { 418 | count = MIN(count, 7); 419 | 420 | Mtx44 projection; 421 | guOrtho(projection, 0., 1024., 0., 1024., 0., 1.); 422 | 423 | GX_SetBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR); 424 | GX_SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); 425 | GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); 426 | GX_SetZCompLoc(GX_FALSE); 427 | 428 | GX_SetNumChans(0); 429 | GX_SetNumTexGens(2); 430 | GX_SetNumIndStages(0); 431 | GX_SetNumTevStages(count + 2); 432 | 433 | GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); 434 | GX_SetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_POS, GX_IDENTITY); 435 | 436 | GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); 437 | 438 | GX_SetTevKColor(GX_KCOLOR0, (GXColor){4, 4, 4, 4}); 439 | 440 | for (int i = 0; i < count; i++) { 441 | GX_SetTevOrder(GX_TEVSTAGE0 + i, GX_TEXCOORD0, GX_TEXMAP0 + i, GX_COLORNULL); 442 | GX_SetTevKColorSel(GX_TEVSTAGE0 + i, GX_TEV_KCSEL_1_4); 443 | GX_SetTevColorIn(GX_TEVSTAGE0 + i, GX_CC_ZERO, GX_CC_TEXC, GX_CC_KONST, i == 0 ? GX_CC_ZERO : GX_CC_CPREV); 444 | GX_SetTevColorOp(GX_TEVSTAGE0 + i, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_FALSE, GX_TEVPREV); 445 | GX_SetTevAlphaIn(GX_TEVSTAGE0 + i, GX_CA_TEXA, GX_CA_ZERO, GX_CA_ZERO, i == 0 ? GX_CA_ZERO : GX_CA_APREV); 446 | GX_SetTevAlphaOp(GX_TEVSTAGE0 + i, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_FALSE, GX_TEVPREV); 447 | GX_SetTevDirect(GX_TEVSTAGE0 + i); 448 | } 449 | 450 | GX_SetTevOrder(GX_TEVSTAGE0 + count, GX_TEXCOORD1, GX_TEXMAP7, GX_COLORNULL); 451 | GX_SetTevColorIn(GX_TEVSTAGE0 + count, GX_CC_TEXA, GX_CC_ZERO, GX_CC_ZERO, GX_CC_CPREV); 452 | GX_SetTevColorOp(GX_TEVSTAGE0 + count, GX_TEV_ADD, GX_TB_SUBHALF, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 453 | GX_SetTevAlphaIn(GX_TEVSTAGE0 + count, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV); 454 | GX_SetTevAlphaOp(GX_TEVSTAGE0 + count, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 455 | GX_SetTevDirect(GX_TEVSTAGE0 + count); 456 | 457 | GX_SetTevOrder(GX_TEVSTAGE1 + count, GX_TEXCOORDNULL, GX_TEXMAP_NULL, GX_COLORNULL); 458 | GX_SetTevKColorSel(GX_TEVSTAGE1 + count, GX_TEV_KCSEL_K0); 459 | GX_SetTevColorIn(GX_TEVSTAGE1 + count, GX_CC_ZERO, GX_CC_CPREV, GX_CC_KONST, GX_CC_APREV); 460 | GX_SetTevColorOp(GX_TEVSTAGE1 + count, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 461 | GX_SetTevDirect(GX_TEVSTAGE1 + count); 462 | 463 | GX_ClearVtxDesc(); 464 | GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); 465 | GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); 466 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); 467 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0); 468 | 469 | GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); 470 | GX_SetCurrentMtx(GX_PNMTX0); 471 | GX_SetViewport(0., 0., 1024., 1024., 0., 1.); 472 | 473 | GX_SetFieldMask(GX_TRUE, GX_TRUE); 474 | GX_SetFieldMode(GX_FALSE, GX_FALSE); 475 | 476 | GX_SetPixelFmt(GX_PF_Y8, GX_ZC_LINEAR); 477 | GX_SetCopyFilter(GX_FALSE, NULL, GX_FALSE, NULL); 478 | 479 | GXPrescaleTlut(src, alpha, count); 480 | 481 | for (int ch = GX_CH_RED; ch <= GX_CH_BLUE; ch++) { 482 | for (int i = 0; i < count; i++) { 483 | GX_LoadTlut(&tlutobj[i][ch], GX_GetTexObjTlut(&src[i]->obj[ch])); 484 | GX_LoadTexObj(&src[i]->obj[ch], GX_TEXMAP0 + i); 485 | } 486 | 487 | GX_LoadTexObj(&texobj, GX_TEXMAP7); 488 | 489 | GXPrescaleCopyChannel(dst->obj[ch], dst->rect, src[0]->rect, ch); 490 | } 491 | 492 | dst->dirty = true; src[0]->dirty = false; 493 | } 494 | 495 | void GXPrescaleApplyBlendDitherFast(gx_surface_t *dst, gx_surface_t **src, uint8_t *alpha, uint32_t count) 496 | { 497 | Mtx44 projection; 498 | guOrtho(projection, 0., 1024., 0., 1024., 0., 1.); 499 | 500 | GX_SetBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR); 501 | GX_SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); 502 | GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); 503 | GX_SetZCompLoc(GX_FALSE); 504 | 505 | GX_SetNumChans(0); 506 | GX_SetNumTexGens(2); 507 | GX_SetNumIndStages(0); 508 | GX_SetNumTevStages(count + 1); 509 | 510 | GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); 511 | GX_SetTexCoordGen2(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_POS, GX_IDENTITY, GX_FALSE, GX_DTTMTX0); 512 | 513 | GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); 514 | 515 | GX_SetTevKColor(GX_KCOLOR0, (GXColor){4, 4, 4, 4}); 516 | 517 | for (int i = 0; i < count; i++) { 518 | GX_SetTevOrder(GX_TEVSTAGE0 + i, GX_TEXCOORD0, GX_TEXMAP0 + i, GX_COLORNULL); 519 | GX_SetTevKColorSel(GX_TEVSTAGE0 + i, GX_TEV_KCSEL_1_4); 520 | GX_SetTevColorIn(GX_TEVSTAGE0 + i, GX_CC_ZERO, GX_CC_TEXC, GX_CC_KONST, i == 0 ? GX_CC_A0 : GX_CC_CPREV); 521 | GX_SetTevColorOp(GX_TEVSTAGE0 + i, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, i == count - 1 ? GX_TRUE : GX_FALSE, GX_TEVPREV); 522 | GX_SetTevAlphaIn(GX_TEVSTAGE0 + i, GX_CA_TEXA, GX_CA_ZERO, GX_CA_ZERO, i == 0 ? GX_CA_ZERO : GX_CA_APREV); 523 | GX_SetTevAlphaOp(GX_TEVSTAGE0 + i, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, i == count - 1 ? GX_TRUE : GX_FALSE, GX_TEVPREV); 524 | GX_SetTevDirect(GX_TEVSTAGE0 + i); 525 | } 526 | 527 | if (state.dither > DITHER_THRESHOLD && count < GX_MAX_TEXMAP) { 528 | GX_SetTevOrder(GX_TEVSTAGE0 + count, GX_TEXCOORD1, GX_TEXMAP7, GX_COLORNULL); 529 | GX_SetTevKColorSel(GX_TEVSTAGE0 + count, GX_TEV_KCSEL_K0); 530 | GX_SetTevColorIn(GX_TEVSTAGE0 + count, GX_CC_ZERO, GX_CC_CPREV, GX_CC_KONST, GX_CC_APREV); 531 | GX_SetTevColorOp(GX_TEVSTAGE0 + count, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 532 | GX_SetTevAlphaIn(GX_TEVSTAGE0 + count, GX_CA_TEXA, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO); 533 | GX_SetTevAlphaOp(GX_TEVSTAGE0 + count, GX_TEV_ADD, GX_TB_SUBHALF, GX_CS_SCALE_1, GX_FALSE, GX_TEVREG0); 534 | GX_SetTevDirect(GX_TEVSTAGE0 + count); 535 | } else { 536 | GX_SetTevOrder(GX_TEVSTAGE0 + count, GX_TEXCOORDNULL, GX_TEXMAP_NULL, GX_COLORNULL); 537 | GX_SetTevKColorSel(GX_TEVSTAGE0 + count, GX_TEV_KCSEL_K0); 538 | GX_SetTevColorIn(GX_TEVSTAGE0 + count, GX_CC_ZERO, GX_CC_CPREV, GX_CC_KONST, GX_CC_APREV); 539 | GX_SetTevColorOp(GX_TEVSTAGE0 + count, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 540 | GX_SetTevAlphaIn(GX_TEVSTAGE0 + count, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_A0); 541 | GX_SetTevAlphaOp(GX_TEVSTAGE0 + count, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_FALSE, GX_TEVREG0); 542 | GX_SetTevDirect(GX_TEVSTAGE0 + count); 543 | } 544 | 545 | GX_ClearVtxDesc(); 546 | GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); 547 | GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); 548 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); 549 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0); 550 | GX_SetArray(GX_TEXMTXARRAY, texmtx, sizeof(texmtx[0])); 551 | 552 | GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); 553 | GX_SetCurrentMtx(GX_PNMTX0); 554 | GX_SetViewport(0., 0., 1024., 1024., 0., 1.); 555 | 556 | GX_SetFieldMask(GX_TRUE, GX_TRUE); 557 | GX_SetFieldMode(GX_FALSE, GX_FALSE); 558 | 559 | GX_SetPixelFmt(GX_PF_Y8, GX_ZC_LINEAR); 560 | GX_SetCopyFilter(GX_FALSE, NULL, GX_FALSE, NULL); 561 | 562 | GXPrescaleTlut(src, alpha, count); 563 | 564 | for (int ch = GX_CH_RED; ch <= GX_CH_BLUE; ch++) { 565 | int idx = state.retrace + ch; 566 | 567 | for (int i = 0; i < count; i++) { 568 | GX_LoadTlut(&tlutobj[i][ch], GX_GetTexObjTlut(&src[i]->obj[ch])); 569 | GX_LoadTexObj(&src[i]->obj[ch], GX_TEXMAP0 + i); 570 | } 571 | 572 | if (state.dither > DITHER_THRESHOLD && count < GX_MAX_TEXMAP) { 573 | GX_LoadTexObj(&texobj, GX_TEXMAP7); 574 | GX_LoadTexMtxIdx(idx % 4, GX_DTTMTX0, GX_MTX3x4); 575 | } 576 | 577 | GX_SetTevColorS10(GX_TEVREG0, idx % 2 ? (GXColorS10){-32, -32, -32, -32} 578 | : (GXColorS10){+15, +15, +15, +15}); 579 | 580 | GXPrescaleCopyChannel(dst->obj[ch], dst->rect, src[0]->rect, ch); 581 | } 582 | 583 | dst->dirty = true; src[0]->dirty = false; 584 | } 585 | 586 | void GXPrescaleAllocState(void) 587 | { 588 | switch (state.dither) { 589 | case DITHER_BAYER8x8: 590 | GX_InitTexObj(&texobj, texdata[0], 8, 8, GX_TF_I8, GX_REPEAT, GX_REPEAT, GX_FALSE); 591 | GX_InitTexObjFilterMode(&texobj, GX_NEAR, GX_NEAR); 592 | break; 593 | case DITHER_BAYER4x4: 594 | GX_InitTexObj(&texobj, texdata[0], 4, 4, GX_TF_I8, GX_REPEAT, GX_REPEAT, GX_FALSE); 595 | GX_InitTexObjFilterMode(&texobj, GX_NEAR, GX_NEAR); 596 | break; 597 | case DITHER_BAYER2x2: 598 | GX_InitTexObj(&texobj, texdata[0], 2, 2, GX_TF_I8, GX_REPEAT, GX_REPEAT, GX_FALSE); 599 | GX_InitTexObjFilterMode(&texobj, GX_NEAR, GX_NEAR); 600 | break; 601 | case DITHER_CLUSTER8x8: 602 | GX_InitTexObj(&texobj, texdata[1], 8, 8, GX_TF_I8, GX_REPEAT, GX_REPEAT, GX_FALSE); 603 | GX_InitTexObjFilterMode(&texobj, GX_NEAR, GX_NEAR); 604 | break; 605 | case DITHER_CLUSTER4x4: 606 | GX_InitTexObj(&texobj, texdata[1], 4, 4, GX_TF_I8, GX_REPEAT, GX_REPEAT, GX_FALSE); 607 | GX_InitTexObjFilterMode(&texobj, GX_NEAR, GX_NEAR); 608 | break; 609 | default: 610 | break; 611 | } 612 | 613 | for (int i = GX_TEXMAP0; i < GX_MAX_TEXMAP; i++) 614 | for (int ch = GX_CH_RED; ch <= GX_CH_BLUE; ch++) 615 | GX_InitTlutObj(&tlutobj[i][ch], tlutdata[i][ch], GX_TL_IA8, 256); 616 | } 617 | 618 | rect_t GXPrescaleGetRect(uint16_t width, uint16_t height) 619 | { 620 | rect_t rect; 621 | 622 | float x = (viewport.w * state.zoom.x) / (state.scale * screen.w); 623 | float y = (viewport.h * state.zoom.y) / (state.scale * screen.h); 624 | float r = DegToRad(state.rotation); 625 | 626 | int X = lrintf(x * fabsf(cosf(r)) + x * fabsf(sinf(r))); 627 | int Y = lrintf(y * fabsf(cosf(r)) + y * fabsf(sinf(r))); 628 | 629 | rect.x = 0; 630 | rect.y = 0; 631 | rect.w = width * MIN(MAX(X, 1), 1024 / width); 632 | rect.h = height * MIN(MAX(Y, 1), 640 / height); 633 | 634 | return rect; 635 | } 636 | -------------------------------------------------------------------------------- /source/gx_preview.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include 11 | #include "gx.h" 12 | #include "state.h" 13 | 14 | static void *displist[2]; 15 | static uint32_t dispsize[2]; 16 | 17 | static uint16_t indtexdata[][4 * 4] ATTRIBUTE_ALIGN(32) = { 18 | { 19 | 0xE0E0, 0xA0E0, 0x60E0, 0x20E0, 20 | 0xE0A0, 0xA0A0, 0x60A0, 0x20A0, 21 | 0xE060, 0xA060, 0x6060, 0x2060, 22 | 0xE020, 0xA020, 0x6020, 0x2020, 23 | }, { 24 | 0xC0C0, 0x40C0, 0xC0C0, 0x40C0, 25 | 0xC040, 0x4040, 0xC040, 0x4040, 26 | 0xC0C0, 0x40C0, 0xC0C0, 0x40C0, 27 | 0xC040, 0x4040, 0xC040, 0x4040, 28 | }, { 29 | 0x8080, 0x8080, 0x8080, 0x8080, 30 | 0x8080, 0x8080, 0x8080, 0x8080, 31 | 0x8080, 0x8080, 0x8080, 0x8080, 32 | 0x8080, 0x8080, 0x8080, 0x8080, 33 | } 34 | }; 35 | 36 | static float indtexmtx[][2][3] = { 37 | { 38 | { +.5, +.0, +.0 }, 39 | { +.0, +.5, +.0 } 40 | }, { 41 | { -.5, +.0, +.0 }, 42 | { +.0, -.5, +.0 } 43 | } 44 | }; 45 | 46 | static GXTexObj indtexobj; 47 | 48 | static GXColorS10 color[][3] = { 49 | [MATRIX_IDENTITY] = { 50 | { + 0, + 0, + 0 }, 51 | { + 0, + 0, + 0 }, 52 | { + 0, + 0, + 0 } 53 | }, [MATRIX_GAMBATTE] = { 54 | { + 0, + 0, + 0 }, 55 | { + 0, + 0, + 0 }, 56 | { + 0, + 0, + 0 } 57 | }, [MATRIX_GBA] = { 58 | { -19, + 0, + 0 }, 59 | { + 0, + 0, + 0 }, 60 | { +19, + 0, + 0 } 61 | }, [MATRIX_GBASP] = { 62 | { -15, + 0, - 7 }, 63 | { + 0, + 0, + 7 }, 64 | { +15, + 0, + 0 } 65 | }, [MATRIX_GBASP_D65] = { 66 | { -16, + 0, - 7 }, 67 | { + 0, + 0, + 7 }, 68 | { +16, + 0, + 0 } 69 | }, [MATRIX_GBI] = { 70 | { -27, + 0, + 0 }, 71 | { + 0, + 0, + 0 }, 72 | { +27, + 0, + 0 } 73 | }, [MATRIX_HIGAN] = { 74 | { + 0, + 0, + 0 }, 75 | { + 0, + 0, + 0 }, 76 | { + 0, + 0, + 0 } 77 | }, [MATRIX_NDS] = { 78 | { -19, + 0, + 0 }, 79 | { + 0, + 0, + 0 }, 80 | { +19, + 0, + 0 } 81 | }, [MATRIX_NDS_D65] = { 82 | { -23, + 0, + 0 }, 83 | { + 0, + 0, + 0 }, 84 | { +23, + 0, + 0 } 85 | }, [MATRIX_PALM] = { 86 | { -18, + 0, + 0 }, 87 | { + 0, + 0, + 0 }, 88 | { +18, + 0, + 0 } 89 | }, [MATRIX_PALM_D65] = { 90 | { -32, + 0, + 0 }, 91 | { + 0, + 0, + 0 }, 92 | { +32, + 0, + 0 } 93 | }, [MATRIX_PSP] = { 94 | { -33, + 0, + 0 }, 95 | { + 0, + 0, + 0 }, 96 | { +33, + 0, + 0 } 97 | }, [MATRIX_PSP_D65] = { 98 | { -38, + 0, + 0 }, 99 | { + 0, + 0, + 0 }, 100 | { +38, + 0, + 0 } 101 | }, [MATRIX_VBA] = { 102 | { + 0, + 0, + 0 }, 103 | { + 0, + 0, + 0 }, 104 | { + 0, + 0, + 0 } 105 | } 106 | }; 107 | 108 | static GXColor kcolor[][4] = { 109 | [MATRIX_IDENTITY] = { 110 | { 255, 0, 0 }, 111 | { 0, 255, 0 }, 112 | { 0, 0, 255 }, 113 | { 54, 182, 18 } 114 | }, [MATRIX_GAMBATTE] = { 115 | { 207, 0, 48 }, 116 | { 32, 191, 32 }, 117 | { 16, 64, 175 }, 118 | { 68, 141, 46 } 119 | }, [MATRIX_GBA] = { 120 | { 204, 34, 50 }, 121 | { 70, 163, 40 }, 122 | { 0, 57, 166 }, 123 | { 72, 134, 49 } 124 | }, [MATRIX_GBASP] = { 125 | { 213, 7, 1 }, 126 | { 25, 185, 0 }, 127 | { 0, 17, 255 }, 128 | { 51, 137, 27 } 129 | }, [MATRIX_GBASP_D65] = { 130 | { 237, 9, 1 }, 131 | { 27, 220, 0 }, 132 | { 0, 19, 255 }, 133 | { 57, 162, 29 } 134 | }, [MATRIX_GBI] = { 135 | { 238, 12, 7 }, 136 | { 45, 195, 9 }, 137 | { 0, 48, 239 }, 138 | { 60, 149, 46 } 139 | }, [MATRIX_HIGAN] = { 140 | { 255, 10, 50 }, 141 | { 50, 230, 10 }, 142 | { 0, 30, 220 }, 143 | { 65, 176, 37 } 144 | }, [MATRIX_NDS] = { 145 | { 180, 23, 27 }, 146 | { 60, 149, 44 }, 147 | { 0, 61, 184 }, 148 | { 57, 123, 53 } 149 | }, [MATRIX_NDS_D65] = { 150 | { 208, 26, 27 }, 151 | { 70, 163, 44 }, 152 | { 0, 66, 184 }, 153 | { 64, 135, 56 } 154 | }, [MATRIX_PALM] = { 155 | { 134, 13, 23 }, 156 | { 33, 139, 38 }, 157 | { 0, 50, 194 }, 158 | { 40, 109, 46 } 159 | }, [MATRIX_PALM_D65] = { 160 | { 230, 17, 23 }, 161 | { 57, 175, 38 }, 162 | { 0, 64, 194 }, 163 | { 62, 140, 53 } 164 | }, [MATRIX_PSP] = { 165 | { 213, 11, 6 }, 166 | { 41, 190, 4 }, 167 | { 0, 40, 245 }, 168 | { 53, 145, 39 } 169 | }, [MATRIX_PSP_D65] = { 170 | { 246, 11, 6 }, 171 | { 47, 201, 4 }, 172 | { 0, 42, 245 }, 173 | { 61, 154, 40 } 174 | }, [MATRIX_VBA] = { 175 | { 186, 22, 22 }, 176 | { 69, 172, 61 }, 177 | { 0, 61, 172 }, 178 | { 57, 142, 56 }, 179 | } 180 | }; 181 | 182 | void GXPreviewDrawRect(GXTexObj texobj[3], rect_t dst_rect, rect_t src_rect) 183 | { 184 | Mtx viewmodel; 185 | guMtxRotDeg(viewmodel, 'z', state.rotation); 186 | guMtxScaleApply(viewmodel, viewmodel, state.zoom.x, state.zoom.y, 1); 187 | GX_LoadPosMtxImm(viewmodel, GX_PNMTX1); 188 | 189 | GX_LoadTexObj(&texobj[0], GX_TEXMAP0); 190 | GX_LoadTexObj(&texobj[1], GX_TEXMAP1); 191 | GX_LoadTexObj(&texobj[2], GX_TEXMAP2); 192 | 193 | GX_LoadTexObj(&indtexobj, GX_TEXMAP3); 194 | 195 | GX_Begin(GX_QUADS, GX_VTXFMT0, 4); 196 | 197 | GX_Position2s16(-dst_rect.w, -dst_rect.h); 198 | GX_TexCoord2s16(src_rect.x, src_rect.y); 199 | 200 | GX_Position2s16(+dst_rect.w, -dst_rect.h); 201 | GX_TexCoord2s16(src_rect.x + src_rect.w, src_rect.y); 202 | 203 | GX_Position2s16(+dst_rect.w, +dst_rect.h); 204 | GX_TexCoord2s16(src_rect.x + src_rect.w, src_rect.y + src_rect.h); 205 | 206 | GX_Position2s16(-dst_rect.w, +dst_rect.h); 207 | GX_TexCoord2s16(src_rect.x, src_rect.y + src_rect.h); 208 | } 209 | 210 | void GXPreviewAllocState(void) 211 | { 212 | Mtx44 projection; 213 | guOrtho(projection, 214 | -screen.y - screen.h / 2, screen.y + screen.h / 2, 215 | -screen.x - screen.w / 2, screen.x + screen.w / 2, 0., 1.); 216 | 217 | switch (state.scaler) { 218 | case SCALER_AREA: 219 | GX_InitTexObj(&indtexobj, indtexdata, 4, 4, GX_TF_IA8, GX_REPEAT, GX_REPEAT, GX_TRUE); 220 | GX_InitTexObjLOD(&indtexobj, GX_LIN_MIP_LIN, GX_LINEAR, 0., 2., -1./3., GX_FALSE, GX_TRUE, GX_ANISO_4); 221 | break; 222 | case SCALER_BOX: 223 | GX_InitTexObj(&indtexobj, indtexdata, 4, 4, GX_TF_IA8, GX_REPEAT, GX_REPEAT, GX_TRUE); 224 | GX_InitTexObjLOD(&indtexobj, GX_LIN_MIP_LIN, GX_LINEAR, 1., 2., 0., GX_TRUE, GX_TRUE, GX_ANISO_4); 225 | break; 226 | default: 227 | break; 228 | } 229 | 230 | displist[0] = GXAllocBuffer(GX_FIFO_MINSIZE); 231 | GX_BeginDispList(displist[0], GX_FIFO_MINSIZE); 232 | 233 | GX_SetBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR); 234 | GX_SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); 235 | GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); 236 | GX_SetZCompLoc(GX_FALSE); 237 | 238 | if (state.scaler > SCALER_BILINEAR) { 239 | GX_SetNumChans(0); 240 | GX_SetNumTexGens(2); 241 | GX_SetNumIndStages(1); 242 | GX_SetNumTevStages(3); 243 | 244 | GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); 245 | GX_SetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX0, GX_TEXMTX1); 246 | 247 | GX_SetIndTexOrder(GX_INDTEXSTAGE0, GX_TEXCOORD1, GX_TEXMAP3); 248 | GX_SetIndTexMatrix(GX_ITM_0, indtexmtx[state.scaler == SCALER_BOX], -7); 249 | 250 | GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); 251 | 252 | GXColorS10 *colorptr = color[state.matrix]; 253 | GXColor *kcolorptr = kcolor[state.matrix]; 254 | 255 | GX_SetTevColorS10(GX_TEVREG0, colorptr[GX_CH_RED]); 256 | GX_SetTevColorS10(GX_TEVREG1, colorptr[GX_CH_GREEN]); 257 | GX_SetTevColorS10(GX_TEVREG2, colorptr[GX_CH_BLUE]); 258 | 259 | GX_SetTevKColor(GX_KCOLOR0, kcolorptr[GX_CH_RED]); 260 | GX_SetTevKColor(GX_KCOLOR1, kcolorptr[GX_CH_GREEN]); 261 | GX_SetTevKColor(GX_KCOLOR2, kcolorptr[GX_CH_BLUE]); 262 | 263 | GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORNULL); 264 | GX_SetTevKColorSel(GX_TEVSTAGE0, GX_TEV_KCSEL_K0); 265 | GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_KONST, GX_CC_TEXC, GX_CC_C0); 266 | GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_FALSE, GX_TEVPREV); 267 | GX_SetTevIndWarp(GX_TEVSTAGE0, GX_INDTEXSTAGE0, GX_TRUE, GX_FALSE, GX_ITM_0); 268 | 269 | GX_SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP1, GX_COLORNULL); 270 | GX_SetTevKColorSel(GX_TEVSTAGE1, GX_TEV_KCSEL_K1); 271 | GX_SetTevColorIn(GX_TEVSTAGE1, GX_CC_C1, GX_CC_KONST, GX_CC_TEXC, GX_CC_CPREV); 272 | GX_SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_FALSE, GX_TEVPREV); 273 | GX_SetTevIndRepeat(GX_TEVSTAGE1); 274 | 275 | GX_SetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD0, GX_TEXMAP2, GX_COLORNULL); 276 | GX_SetTevKColorSel(GX_TEVSTAGE2, GX_TEV_KCSEL_K2); 277 | GX_SetTevColorIn(GX_TEVSTAGE2, GX_CC_C2, GX_CC_KONST, GX_CC_TEXC, GX_CC_CPREV); 278 | GX_SetTevColorOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 279 | GX_SetTevIndRepeat(GX_TEVSTAGE2); 280 | } else { 281 | GX_SetNumChans(0); 282 | GX_SetNumTexGens(1); 283 | GX_SetNumIndStages(0); 284 | GX_SetNumTevStages(3); 285 | 286 | GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); 287 | 288 | GXColorS10 *colorptr = color[state.matrix]; 289 | GXColor *kcolorptr = kcolor[state.matrix]; 290 | 291 | GX_SetTevColorS10(GX_TEVREG0, colorptr[GX_CH_RED]); 292 | GX_SetTevColorS10(GX_TEVREG1, colorptr[GX_CH_GREEN]); 293 | GX_SetTevColorS10(GX_TEVREG2, colorptr[GX_CH_BLUE]); 294 | 295 | GX_SetTevKColor(GX_KCOLOR0, kcolorptr[GX_CH_RED]); 296 | GX_SetTevKColor(GX_KCOLOR1, kcolorptr[GX_CH_GREEN]); 297 | GX_SetTevKColor(GX_KCOLOR2, kcolorptr[GX_CH_BLUE]); 298 | 299 | GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORNULL); 300 | GX_SetTevKColorSel(GX_TEVSTAGE0, GX_TEV_KCSEL_K0); 301 | GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_KONST, GX_CC_TEXC, GX_CC_C0); 302 | GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_FALSE, GX_TEVPREV); 303 | GX_SetTevDirect(GX_TEVSTAGE0); 304 | 305 | GX_SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP1, GX_COLORNULL); 306 | GX_SetTevKColorSel(GX_TEVSTAGE1, GX_TEV_KCSEL_K1); 307 | GX_SetTevColorIn(GX_TEVSTAGE1, GX_CC_C1, GX_CC_KONST, GX_CC_TEXC, GX_CC_CPREV); 308 | GX_SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_FALSE, GX_TEVPREV); 309 | GX_SetTevDirect(GX_TEVSTAGE1); 310 | 311 | GX_SetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD0, GX_TEXMAP2, GX_COLORNULL); 312 | GX_SetTevKColorSel(GX_TEVSTAGE2, GX_TEV_KCSEL_K2); 313 | GX_SetTevColorIn(GX_TEVSTAGE2, GX_CC_C2, GX_CC_KONST, GX_CC_TEXC, GX_CC_CPREV); 314 | GX_SetTevColorOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 315 | GX_SetTevDirect(GX_TEVSTAGE2); 316 | } 317 | 318 | GX_ClearVtxDesc(); 319 | GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); 320 | GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); 321 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 1); 322 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0); 323 | 324 | GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); 325 | GX_SetCurrentMtx(GX_PNMTX1); 326 | 327 | dispsize[0] = GX_EndDispList(); 328 | displist[0] = realloc_in_place(displist[0], dispsize[0]); 329 | 330 | displist[1] = GXAllocBuffer(GX_FIFO_MINSIZE); 331 | GX_BeginDispList(displist[1], GX_FIFO_MINSIZE); 332 | 333 | GX_SetBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR); 334 | GX_SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); 335 | GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); 336 | GX_SetZCompLoc(GX_FALSE); 337 | 338 | if (state.scaler > SCALER_BILINEAR) { 339 | GX_SetNumChans(0); 340 | GX_SetNumTexGens(2); 341 | GX_SetNumIndStages(1); 342 | GX_SetNumTevStages(3); 343 | 344 | GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); 345 | GX_SetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX0, GX_TEXMTX1); 346 | 347 | GX_SetIndTexOrder(GX_INDTEXSTAGE0, GX_TEXCOORD1, GX_TEXMAP3); 348 | GX_SetIndTexMatrix(GX_ITM_0, indtexmtx[state.scaler == SCALER_BOX], -7); 349 | 350 | GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); 351 | 352 | GX_SetTevKColor(GX_KCOLOR3, kcolor[state.matrix][GX_CH_ALPHA]); 353 | 354 | GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORNULL); 355 | GX_SetTevKColorSel(GX_TEVSTAGE0, GX_TEV_KCSEL_K3_R); 356 | GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_KONST, GX_CC_TEXC, GX_CC_ZERO); 357 | GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 358 | GX_SetTevIndWarp(GX_TEVSTAGE0, GX_INDTEXSTAGE0, GX_TRUE, GX_FALSE, GX_ITM_0); 359 | 360 | GX_SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP1, GX_COLORNULL); 361 | GX_SetTevKColorSel(GX_TEVSTAGE1, GX_TEV_KCSEL_K3_G); 362 | GX_SetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_KONST, GX_CC_TEXC, GX_CC_CPREV); 363 | GX_SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 364 | GX_SetTevIndRepeat(GX_TEVSTAGE1); 365 | 366 | GX_SetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD0, GX_TEXMAP2, GX_COLORNULL); 367 | GX_SetTevKColorSel(GX_TEVSTAGE2, GX_TEV_KCSEL_K3_B); 368 | GX_SetTevColorIn(GX_TEVSTAGE2, GX_CC_ZERO, GX_CC_KONST, GX_CC_TEXC, GX_CC_CPREV); 369 | GX_SetTevColorOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 370 | GX_SetTevIndRepeat(GX_TEVSTAGE2); 371 | } else { 372 | GX_SetNumChans(0); 373 | GX_SetNumTexGens(1); 374 | GX_SetNumIndStages(0); 375 | GX_SetNumTevStages(3); 376 | 377 | GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); 378 | 379 | GX_SetTevKColor(GX_KCOLOR3, kcolor[state.matrix][GX_CH_ALPHA]); 380 | 381 | GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORNULL); 382 | GX_SetTevKColorSel(GX_TEVSTAGE0, GX_TEV_KCSEL_K3_R); 383 | GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_KONST, GX_CC_TEXC, GX_CC_ZERO); 384 | GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 385 | GX_SetTevDirect(GX_TEVSTAGE0); 386 | 387 | GX_SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP1, GX_COLORNULL); 388 | GX_SetTevKColorSel(GX_TEVSTAGE1, GX_TEV_KCSEL_K3_G); 389 | GX_SetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_KONST, GX_CC_TEXC, GX_CC_CPREV); 390 | GX_SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 391 | GX_SetTevDirect(GX_TEVSTAGE1); 392 | 393 | GX_SetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD0, GX_TEXMAP2, GX_COLORNULL); 394 | GX_SetTevKColorSel(GX_TEVSTAGE2, GX_TEV_KCSEL_K3_B); 395 | GX_SetTevColorIn(GX_TEVSTAGE2, GX_CC_ZERO, GX_CC_KONST, GX_CC_TEXC, GX_CC_CPREV); 396 | GX_SetTevColorOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 397 | GX_SetTevDirect(GX_TEVSTAGE2); 398 | } 399 | 400 | GX_ClearVtxDesc(); 401 | GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); 402 | GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); 403 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 1); 404 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_S16, 0); 405 | 406 | GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); 407 | GX_SetCurrentMtx(GX_PNMTX1); 408 | 409 | dispsize[1] = GX_EndDispList(); 410 | displist[1] = realloc_in_place(displist[1], dispsize[1]); 411 | } 412 | 413 | void GXPreviewSetState(uint32_t index) 414 | { 415 | if (state.current != STATE_PREVIEW + index) { 416 | state.current = STATE_PREVIEW + index; 417 | GX_CallDispList(displist[index], dispsize[index]); 418 | } 419 | } 420 | -------------------------------------------------------------------------------- /source/gx_solid.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include 11 | #include "gx.h" 12 | #include "state.h" 13 | #include "video.h" 14 | 15 | static void *displist; 16 | static uint32_t dispsize; 17 | 18 | void GXSolidDrawRect(rect_t rect, uint32_t color[4]) 19 | { 20 | GX_Begin(GX_QUADS, GX_VTXFMT0, 4); 21 | 22 | GX_Position2s16(rect.x, rect.y); 23 | GX_Color1u32(color[0]); 24 | 25 | GX_Position2s16(rect.x + rect.w, rect.y); 26 | GX_Color1u32(color[1]); 27 | 28 | GX_Position2s16(rect.x + rect.w, rect.y + rect.h); 29 | GX_Color1u32(color[2]); 30 | 31 | GX_Position2s16(rect.x, rect.y + rect.h); 32 | GX_Color1u32(color[3]); 33 | } 34 | 35 | void GXSolidAllocState(void) 36 | { 37 | Mtx44 projection; 38 | guOrtho(projection, 39 | -screen.y, screen.y + screen.h, 40 | -screen.x, screen.x + screen.w, 0., 1.); 41 | 42 | displist = GXAllocBuffer(GX_FIFO_MINSIZE); 43 | GX_BeginDispList(displist, GX_FIFO_MINSIZE); 44 | 45 | GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); 46 | GX_SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0); 47 | GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); 48 | GX_SetZCompLoc(GX_FALSE); 49 | 50 | GX_SetNumChans(1); 51 | GX_SetNumTexGens(0); 52 | GX_SetNumIndStages(0); 53 | GX_SetNumTevStages(1); 54 | 55 | GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); 56 | 57 | GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORDNULL, GX_TEXMAP_NULL, GX_COLOR0A0); 58 | GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC); 59 | GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 60 | GX_SetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA); 61 | GX_SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV); 62 | GX_SetTevDirect(GX_TEVSTAGE0); 63 | 64 | GX_ClearVtxDesc(); 65 | GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); 66 | GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT); 67 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_S16, 0); 68 | GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); 69 | 70 | GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); 71 | GX_SetCurrentMtx(GX_PNMTX0); 72 | 73 | dispsize = GX_EndDispList(); 74 | displist = realloc_in_place(displist, dispsize); 75 | } 76 | 77 | void GXSolidSetState(void) 78 | { 79 | if (state.current != STATE_SOLID) { 80 | state.current = STATE_SOLID; 81 | GX_CallDispList(displist, dispsize); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /source/input.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "input.h" 16 | #include "state.h" 17 | #include "video.h" 18 | 19 | gc_controller_t gc_controller; 20 | gc_steering_t gc_steering; 21 | n64_controller_t n64_controller; 22 | 23 | #ifdef HW_RVL 24 | static void power_cb(void) 25 | { 26 | state.quit |= KEY_POWEROFF; 27 | } 28 | #endif 29 | 30 | static void reset_cb(void) 31 | { 32 | if (state.draw_osd) state.quit |= KEY_RESET; 33 | else state.reset = true; 34 | PAD_Recalibrate(PAD_CHAN0_BIT | PAD_CHAN1_BIT | PAD_CHAN2_BIT | PAD_CHAN3_BIT); 35 | } 36 | 37 | static void poll_cb() 38 | { 39 | PAD_Read(gc_controller.status); 40 | 41 | for (int chan = 0; chan < SI_MAX_CHAN; chan++) 42 | SI_ReadSteering(chan, &gc_steering.status[chan]); 43 | } 44 | 45 | void InputRead(void) 46 | { 47 | for (int chan = 0; chan < SI_MAX_CHAN; chan++) { 48 | gc_controller.data[chan].last = gc_controller.data[chan].held; 49 | 50 | if (gc_controller.status[chan].err != PAD_ERR_TRANSFER) { 51 | gc_controller.data[chan].barrel = PAD_IsBarrel(chan); 52 | 53 | gc_controller.data[chan].held = gc_controller.status[chan].button & PAD_BUTTON_ALL; 54 | gc_controller.data[chan].stick.x = gc_controller.status[chan].stickX; 55 | gc_controller.data[chan].stick.y = gc_controller.status[chan].stickY; 56 | gc_controller.data[chan].substick.x = gc_controller.status[chan].substickX; 57 | gc_controller.data[chan].substick.y = gc_controller.status[chan].substickY; 58 | gc_controller.data[chan].trigger.l = gc_controller.status[chan].triggerL; 59 | gc_controller.data[chan].trigger.r = gc_controller.status[chan].triggerR; 60 | gc_controller.data[chan].button.a = gc_controller.status[chan].analogA; 61 | gc_controller.data[chan].button.b = gc_controller.status[chan].analogB; 62 | } 63 | 64 | gc_controller.data[chan].down = gc_controller.data[chan].held & ~gc_controller.data[chan].last; 65 | gc_controller.data[chan].up = ~gc_controller.data[chan].held & gc_controller.data[chan].last; 66 | } 67 | 68 | for (int chan = 0; chan < SI_MAX_CHAN; chan++) { 69 | gc_steering.data[chan].last = gc_steering.data[chan].held; 70 | 71 | if (gc_steering.status[chan].err != SI_STEERING_ERR_TRANSFER) { 72 | gc_steering.data[chan].held = gc_steering.status[chan].button & SI_STEERING_BUTTON_ALL; 73 | gc_steering.data[chan].flag = gc_steering.status[chan].flag; 74 | gc_steering.data[chan].wheel = gc_steering.status[chan].wheel; 75 | gc_steering.data[chan].pedal.l = gc_steering.status[chan].pedalL; 76 | gc_steering.data[chan].pedal.r = gc_steering.status[chan].pedalR; 77 | gc_steering.data[chan].paddle.l = gc_steering.status[chan].paddleL; 78 | gc_steering.data[chan].paddle.r = gc_steering.status[chan].paddleR; 79 | } 80 | 81 | gc_steering.data[chan].down = gc_steering.data[chan].held & ~gc_steering.data[chan].last; 82 | gc_steering.data[chan].up = ~gc_steering.data[chan].held & gc_steering.data[chan].last; 83 | } 84 | 85 | for (int chan = 0; chan < SI_MAX_CHAN; chan++) { 86 | n64_controller.data[chan].last = n64_controller.data[chan].held; 87 | 88 | if (n64_controller.status[chan].err != N64_ERR_TRANSFER) { 89 | n64_controller.data[chan].held = n64_controller.status[chan].button & N64_BUTTON_ALL; 90 | n64_controller.data[chan].stick.x = n64_controller.status[chan].stickX; 91 | n64_controller.data[chan].stick.y = n64_controller.status[chan].stickY; 92 | } 93 | 94 | n64_controller.data[chan].down = n64_controller.data[chan].held & ~n64_controller.data[chan].last; 95 | n64_controller.data[chan].up = ~n64_controller.data[chan].held & n64_controller.data[chan].last; 96 | } 97 | } 98 | 99 | void InputInit(void) 100 | { 101 | #ifdef HW_RVL 102 | SYS_SetPowerCallback(power_cb); 103 | #endif 104 | SYS_SetResetCallback(reset_cb); 105 | 106 | SI_SetSamplingRate(state.poll); 107 | SI_RegisterPollingHandler(poll_cb); 108 | 109 | PAD_Init(); 110 | SI_InitSteering(); 111 | N64_Init(); 112 | 113 | for (int chan = 0; chan < SI_MAX_CHAN; chan++) { 114 | gc_controller.status[chan].err = PAD_ERR_NO_CONTROLLER; 115 | gc_steering.status[chan].err = SI_ResetSteering(chan); 116 | n64_controller.status[chan].err = N64_ERR_NO_CONTROLLER; 117 | } 118 | 119 | #ifdef HW_RVL 120 | WPAD_Init(); 121 | WPAD_SetDataFormat(WPAD_CHAN_ALL, WPAD_FMT_BTNS_ACC_IR); 122 | WPAD_SetVRes(WPAD_CHAN_ALL, screen.w, screen.h); 123 | #endif 124 | } 125 | -------------------------------------------------------------------------------- /source/input.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef GBI_INPUT_H 10 | #define GBI_INPUT_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define MAT_BUTTON_MINUS PAD_BUTTON_LEFT 19 | #define MAT_BUTTON_BLUE_DOWN PAD_BUTTON_RIGHT 20 | #define MAT_BUTTON_BLUE_SQUARE PAD_BUTTON_DOWN 21 | #define MAT_BUTTON_BLUE_LEFT PAD_BUTTON_UP 22 | #define MAT_BUTTON_ORANGE_DOWN PAD_BUTTON_Z 23 | #define MAT_BUTTON_ORANGE_SQUARE PAD_BUTTON_A 24 | #define MAT_BUTTON_ORANGE_UP PAD_BUTTON_B 25 | #define MAT_BUTTON_PLUS PAD_BUTTON_X 26 | #define MAT_BUTTON_ORANGE_RIGHT PAD_BUTTON_Y 27 | #define MAT_BUTTON_BLUE_UP PAD_BUTTON_START 28 | 29 | #define PAD_BUTTON_ALL 0x1F7F 30 | #define PAD_COMBO_ORIGIN (PAD_BUTTON_Y | PAD_BUTTON_X | PAD_BUTTON_START) 31 | #define PAD_COMBO_RESET (PAD_BUTTON_B | PAD_BUTTON_X | PAD_BUTTON_START) 32 | #define PAD_COMBO_RESTART (PAD_BUTTON_R | PAD_BUTTON_Z | PAD_BUTTON_START) 33 | 34 | #define SI_STEERING_BUTTON_ALL PAD_BUTTON_ALL 35 | #define SI_STEERING_COMBO_ORIGIN PAD_COMBO_ORIGIN 36 | #define SI_STEERING_COMBO_RESET PAD_COMBO_RESET 37 | #define SI_STEERING_COMBO_RESTART PAD_COMBO_RESTART 38 | 39 | #define N64_BUTTON_ALL 0x3FFF 40 | #define N64_COMBO_ORIGIN (N64_BUTTON_START | N64_BUTTON_R | N64_BUTTON_L) 41 | #define N64_COMBO_RESET (N64_BUTTON_START | N64_BUTTON_Z | N64_BUTTON_B | N64_BUTTON_A | N64_BUTTON_R) 42 | #define N64_COMBO_RESTART (N64_BUTTON_START | N64_BUTTON_Z | N64_BUTTON_R) 43 | 44 | typedef struct { 45 | PADStatus status[SI_MAX_CHAN]; 46 | 47 | struct { 48 | uint16_t held, last, down, up; 49 | struct { int8_t x, y; } stick; 50 | struct { int8_t x, y; } substick; 51 | struct { uint8_t l, r; } trigger; 52 | struct { uint8_t a, b; } button; 53 | bool barrel; 54 | } data[SI_MAX_CHAN]; 55 | } gc_controller_t; 56 | 57 | typedef struct { 58 | SISteeringStatus status[SI_MAX_CHAN]; 59 | 60 | struct { 61 | uint16_t held, last, down, up; 62 | uint8_t flag; 63 | int8_t wheel; 64 | struct { uint8_t l, r; } pedal; 65 | struct { uint8_t l, r; } paddle; 66 | } data[SI_MAX_CHAN]; 67 | } gc_steering_t; 68 | 69 | typedef struct { 70 | N64Status status[SI_MAX_CHAN]; 71 | 72 | struct { 73 | uint16_t held, last, down, up; 74 | struct { int8_t x, y; } stick; 75 | } data[SI_MAX_CHAN]; 76 | } n64_controller_t; 77 | 78 | extern gc_controller_t gc_controller; 79 | extern gc_steering_t gc_steering; 80 | extern n64_controller_t n64_controller; 81 | 82 | void InputRead(void); 83 | void InputInit(void); 84 | 85 | #endif /* GBI_INPUT_H */ 86 | -------------------------------------------------------------------------------- /source/network.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include 11 | #include "3ds.h" 12 | #include "network.h" 13 | #include "sntp.h" 14 | #include "wiiload.h" 15 | 16 | static lwp_t thread = LWP_THREAD_NULL; 17 | 18 | network_state_t network = { 19 | .inited = (-1), 20 | .use_dhcp = true 21 | }; 22 | 23 | int tcp_read(int socket, void *buffer, int size, int minsize) 24 | { 25 | int ret, len = 0; 26 | 27 | while (len < minsize) { 28 | ret = net_read(socket, buffer + len, size - len); 29 | if (ret < 1) return ret < 0 ? ret : len; 30 | else len += ret; 31 | } 32 | 33 | return len; 34 | } 35 | 36 | int tcp_read_complete(int socket, void *buffer, int size) 37 | { 38 | return tcp_read(socket, buffer, size, size); 39 | } 40 | 41 | static void *thread_func(void *arg) 42 | { 43 | network.inited = if_configex(&network.address, &network.gateway, &network.netmask, network.use_dhcp); 44 | 45 | if (network.inited < 0) { 46 | network.disabled = true; 47 | return NULL; 48 | } 49 | 50 | CTRInit(); 51 | SNTPInit(); 52 | WIILOADInit(); 53 | 54 | return NULL; 55 | } 56 | 57 | void NetworkInit(void) 58 | { 59 | if (network.disabled) return; 60 | if (LWP_CreateThread(&thread, thread_func, NULL, NULL, 0, LWP_PRIO_NORMAL) < 0) 61 | network.disabled = true; 62 | } 63 | -------------------------------------------------------------------------------- /source/network.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef GBI_NETWORK_H 10 | #define GBI_NETWORK_H 11 | 12 | #include 13 | 14 | typedef struct { 15 | int inited; 16 | struct in_addr address; 17 | struct in_addr gateway; 18 | struct in_addr netmask; 19 | bool use_dhcp; 20 | bool disabled; 21 | } network_state_t; 22 | 23 | extern network_state_t network; 24 | 25 | int tcp_read(int socket, void *buffer, int size, int minsize); 26 | int tcp_read_complete(int socket, void *buffer, int size); 27 | 28 | void NetworkInit(void); 29 | 30 | #endif /* GBI_NETWORK_H */ 31 | -------------------------------------------------------------------------------- /source/sntp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include "network.h" 11 | #include "sntp.h" 12 | #include "state.h" 13 | 14 | static lwp_t thread = LWP_THREAD_NULL; 15 | 16 | sntp_state_t sntp = { 17 | .sv.sd = INVALID_SOCKET, 18 | .sv.sin.sin_family = AF_INET, 19 | .sv.sin.sin_port = 123, 20 | .sv.sin.sin_addr.s_addr = INADDR_ANY, 21 | .sv.sinlen = sizeof(struct sockaddr), 22 | }; 23 | 24 | extern uint32_t __SYS_SetRTC(uint32_t time); 25 | 26 | static bool sntp_read(int socket) 27 | { 28 | sntp_packet_t packet; 29 | 30 | if (net_read(socket, &packet, sizeof(packet)) < sizeof(packet)) 31 | return false; 32 | if (!__SYS_SetRTC((packet.transmit_time >> 32) - DIFF_SEC_1900_2000)) 33 | return false; 34 | 35 | return true; 36 | } 37 | 38 | static void *thread_func(void *arg) 39 | { 40 | sntp.sv.sd = net_socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); 41 | 42 | if (sntp.sv.sd == INVALID_SOCKET) 43 | goto fail; 44 | if (net_bind(sntp.sv.sd, (struct sockaddr *)&sntp.sv.sin, sntp.sv.sinlen) < 0) 45 | goto fail; 46 | 47 | do { 48 | #ifdef HW_DOL 49 | fd_set readset; 50 | struct timeval tv; 51 | 52 | FD_ZERO(&readset); 53 | FD_SET(sntp.sv.sd, &readset); 54 | 55 | tv.tv_sec = 1; tv.tv_usec = 0; 56 | net_select(FD_SETSIZE, &readset, NULL, NULL, &tv); 57 | 58 | if (FD_ISSET(sntp.sv.sd, &readset)) 59 | #else 60 | struct pollsd psd = {sntp.sv.sd, POLLIN, 0}; 61 | net_poll(&psd, 1, 1000); 62 | 63 | if (psd.revents & POLLIN) 64 | #endif 65 | sntp_read(sntp.sv.sd); 66 | } while (!state.quit); 67 | 68 | fail: 69 | net_close(sntp.sv.sd); 70 | sntp.sv.sd = INVALID_SOCKET; 71 | return NULL; 72 | } 73 | 74 | void SNTPInit(void) 75 | { 76 | LWP_CreateThread(&thread, thread_func, NULL, NULL, 0, LWP_PRIO_NORMAL); 77 | } 78 | -------------------------------------------------------------------------------- /source/sntp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef GBI_SNTP_H 10 | #define GBI_SNTP_H 11 | 12 | #include 13 | 14 | #define DIFF_SEC_1900_2000 3155673600UL 15 | 16 | typedef struct { 17 | struct { 18 | int sd; 19 | struct sockaddr_in sin; 20 | socklen_t sinlen; 21 | } sv; 22 | } sntp_state_t; 23 | 24 | extern sntp_state_t sntp; 25 | 26 | typedef struct { 27 | uint8_t flags; 28 | uint8_t stratum; 29 | int8_t poll; 30 | int8_t precision; 31 | uint32_t root_delay; 32 | uint32_t root_dispersion; 33 | uint32_t reference_id; 34 | uint64_t reference_time; 35 | uint64_t origin_time; 36 | uint64_t receive_time; 37 | uint64_t transmit_time; 38 | } ATTRIBUTE_PACKED sntp_packet_t; 39 | 40 | void SNTPInit(void); 41 | 42 | #endif /* GBI_SNTP_H */ 43 | -------------------------------------------------------------------------------- /source/state.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef GBI_STATE_H 10 | #define GBI_STATE_H 11 | 12 | #include 13 | 14 | #define KEY_QUIT 0x01 15 | #define KEY_RESET 0x02 16 | #define KEY_POWEROFF 0x04 17 | 18 | typedef struct { 19 | const char *path; 20 | bool draw_osd; 21 | bool draw_wait; 22 | volatile int reset, quit; 23 | 24 | struct { float w, h; } aspect; 25 | struct { float x, y; } offset; 26 | struct { float x, y; } zoom; 27 | bool zoom_auto; 28 | float zoom_ratio; 29 | float rotation; 30 | unsigned scale; 31 | 32 | unsigned poll; 33 | 34 | const char *cursor; 35 | const char *overlay; 36 | unsigned overlay_id; 37 | struct { float x, y; } overlay_scale; 38 | 39 | enum { 40 | FILTER_NONE = 0, 41 | FILTER_BLEND, 42 | FILTER_DEFLICKER, 43 | FILTER_ACCUMULATE, 44 | FILTER_SCALE2XEX, 45 | FILTER_SCALE2XPLUS, 46 | FILTER_SCALE2X, 47 | FILTER_EAGLE2X, 48 | FILTER_SCAN2X, 49 | FILTER_NORMAL2X, 50 | FILTER_MAX 51 | } filter; 52 | 53 | float filter_weight[3]; 54 | bool filter_prescale; 55 | 56 | enum { 57 | DITHER_NONE = 0, 58 | DITHER_THRESHOLD, 59 | DITHER_BAYER8x8, 60 | DITHER_BAYER4x4, 61 | DITHER_BAYER2x2, 62 | DITHER_CLUSTER8x8, 63 | DITHER_CLUSTER4x4, 64 | DITHER_MAX 65 | } dither; 66 | 67 | enum { 68 | SCALER_NEAREST = 0, 69 | SCALER_BILINEAR, 70 | SCALER_AREA, 71 | SCALER_BOX, 72 | SCALER_MAX 73 | } scaler; 74 | 75 | enum { 76 | INTENT_PERCEPTUAL = 0, 77 | INTENT_RELATIVE_COLORIMETRIC, 78 | INTENT_SATURATION, 79 | INTENT_ABSOLUTE_COLORIMETRIC, 80 | } profile_intent; 81 | 82 | enum { 83 | PROFILE_SRGB = 0, 84 | PROFILE_GAMBATTE, 85 | PROFILE_GBA, 86 | PROFILE_GBASP, 87 | PROFILE_GBC, 88 | PROFILE_GBI, 89 | PROFILE_HICOLOUR, 90 | PROFILE_HIGAN, 91 | PROFILE_NDS, 92 | PROFILE_PALM, 93 | PROFILE_PSP, 94 | PROFILE_MAX 95 | } profile; 96 | 97 | enum { 98 | MATRIX_IDENTITY = 0, 99 | MATRIX_GAMBATTE, 100 | MATRIX_GBA, 101 | MATRIX_GBASP, 102 | MATRIX_GBASP_D65, 103 | MATRIX_GBI, 104 | MATRIX_HIGAN, 105 | MATRIX_NDS, 106 | MATRIX_NDS_D65, 107 | MATRIX_PALM, 108 | MATRIX_PALM_D65, 109 | MATRIX_PSP, 110 | MATRIX_PSP_D65, 111 | MATRIX_VBA, 112 | MATRIX_MAX, 113 | MATRIX_GBC = MATRIX_GBA, 114 | MATRIX_HICOLOUR = MATRIX_VBA, 115 | } matrix; 116 | 117 | enum { 118 | TRC_LINEAR = 0, 119 | TRC_GAMMA, 120 | TRC_PIECEWISE, 121 | TRC_IEC61966, 122 | TRC_ITU709, 123 | TRC_SMPTE240, 124 | TRC_MAX 125 | } input_trc; 126 | 127 | float input_gamma[3]; 128 | float input_alpha[3]; 129 | float output_gamma; 130 | float brightness[3]; 131 | float contrast[3]; 132 | 133 | unsigned retrace, field; 134 | 135 | enum { 136 | STATE_INIT = 0, 137 | STATE_PREVIEW, 138 | STATE_PREVIEW_LUMA, 139 | STATE_OVERLAY, 140 | STATE_SOLID, 141 | STATE_FONT, 142 | STATE_CURSOR, 143 | } current; 144 | } state_t; 145 | 146 | extern state_t default_state, state; 147 | 148 | enum { 149 | SM_DEFAULT = 0, 150 | SM_NORMAL, 151 | SM_FULL, 152 | SM_STRETCH, 153 | SM_MAX 154 | }; 155 | 156 | #endif /* GBI_STATE_H */ 157 | -------------------------------------------------------------------------------- /source/stub.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file was autogenerated by raw2c. 3 | Visit http://www.devkitpro.org 4 | */ 5 | 6 | const unsigned char stub[] = { 7 | 0x94, 0x21, 0xfe, 0xe8, 0x38, 0xe6, 0x00, 0x1e, 0x7d, 0x08, 0x02, 0xa6, 0xbf, 0x21, 0x00, 0xfc, 8 | 0x7c, 0xbd, 0x2b, 0x78, 0x7c, 0xdc, 0x33, 0x78, 0x80, 0xa1, 0x00, 0x00, 0x54, 0xe6, 0x00, 0x36, 9 | 0x7c, 0x3f, 0x0b, 0x78, 0x7c, 0x86, 0x00, 0xd0, 0x91, 0x01, 0x01, 0x1c, 0x7c, 0xa1, 0x21, 0x6e, 10 | 0x7c, 0x7e, 0x1b, 0x78, 0x3b, 0x7f, 0x00, 0x08, 0x7f, 0xc4, 0xf3, 0x78, 0x3b, 0x41, 0x00, 0x17, 11 | 0x38, 0xa0, 0x00, 0xe4, 0x57, 0x5a, 0x00, 0x36, 0x7f, 0x63, 0xdb, 0x78, 0x48, 0x00, 0x01, 0x91, 12 | 0x7f, 0xa4, 0xeb, 0x78, 0x7f, 0x43, 0xd3, 0x78, 0x7f, 0x85, 0xe3, 0x78, 0x48, 0x00, 0x01, 0x81, 13 | 0x80, 0x7f, 0x00, 0x08, 0x80, 0x5f, 0x00, 0x50, 0x3b, 0xbf, 0x00, 0x98, 0x7c, 0x1e, 0x1a, 0x14, 14 | 0x3b, 0x3f, 0x00, 0x6c, 0x7f, 0x82, 0x00, 0x40, 0x40, 0x9c, 0x00, 0x68, 0x3b, 0xbf, 0x00, 0x4c, 15 | 0x3b, 0x3f, 0x00, 0x68, 0x84, 0x7d, 0x00, 0x04, 0x81, 0x3d, 0xff, 0xb8, 0x80, 0xbd, 0x00, 0x48, 16 | 0x7c, 0x9e, 0x4a, 0x14, 0x48, 0x00, 0x01, 0x49, 0x80, 0x7d, 0x00, 0x00, 0x80, 0x9d, 0x00, 0x48, 17 | 0x48, 0x00, 0x01, 0x21, 0x7c, 0x1d, 0xc8, 0x00, 0x40, 0x82, 0xff, 0xdc, 0x3b, 0xbf, 0x00, 0x68, 18 | 0x3b, 0x7b, 0x00, 0x8c, 0x84, 0x7d, 0x00, 0x04, 0x81, 0x5d, 0xff, 0xb8, 0x80, 0xbd, 0x00, 0x48, 19 | 0x7c, 0x9e, 0x52, 0x14, 0x48, 0x00, 0x01, 0x19, 0x80, 0x7d, 0x00, 0x00, 0x80, 0x9d, 0x00, 0x48, 20 | 0x48, 0x00, 0x00, 0xf1, 0x7c, 0x9d, 0xd8, 0x00, 0x40, 0x86, 0xff, 0xdc, 0x48, 0x00, 0x00, 0x58, 21 | 0x84, 0x7d, 0xff, 0xfc, 0x81, 0x7d, 0xff, 0xb8, 0x80, 0xbd, 0x00, 0x48, 0x7c, 0x9e, 0x5a, 0x14, 22 | 0x48, 0x00, 0x00, 0xed, 0x80, 0x7d, 0x00, 0x00, 0x80, 0x9d, 0x00, 0x48, 0x48, 0x00, 0x00, 0xc5, 23 | 0x7f, 0x1d, 0xc8, 0x00, 0x40, 0x9a, 0xff, 0xdc, 0x3b, 0xbf, 0x00, 0x24, 0x80, 0x7d, 0x00, 0x44, 24 | 0x85, 0x9d, 0xff, 0xfc, 0x80, 0xbd, 0x00, 0x90, 0x7c, 0x9e, 0x62, 0x14, 0x48, 0x00, 0x00, 0xc1, 25 | 0x80, 0x7d, 0x00, 0x48, 0x80, 0x9d, 0x00, 0x90, 0x48, 0x00, 0x00, 0x99, 0x7f, 0x9d, 0xd8, 0x00, 26 | 0x40, 0x9e, 0xff, 0xdc, 0x83, 0x7f, 0x00, 0xe4, 0x38, 0x00, 0x00, 0x00, 0x80, 0x5f, 0x00, 0xe0, 27 | 0x3b, 0x3b, 0x00, 0x01, 0x7f, 0x29, 0x03, 0xa6, 0x48, 0x00, 0x00, 0x0c, 0x98, 0x02, 0x00, 0x00, 28 | 0x38, 0x42, 0x00, 0x01, 0x42, 0x00, 0xff, 0xf8, 0x80, 0x7f, 0x00, 0xe0, 0x80, 0x9f, 0x00, 0xe4, 29 | 0x48, 0x00, 0x00, 0x61, 0x81, 0x3f, 0x00, 0xe8, 0x3c, 0x49, 0x40, 0x00, 0x80, 0x02, 0x00, 0x04, 30 | 0x6c, 0x1e, 0x5f, 0x61, 0x2c, 0x1e, 0x72, 0x67, 0x40, 0x82, 0x00, 0x18, 0x2c, 0x9c, 0x00, 0x00, 31 | 0x41, 0x86, 0x00, 0x10, 0x90, 0x02, 0x00, 0x08, 0x93, 0x42, 0x00, 0x0c, 0x93, 0x82, 0x00, 0x10, 32 | 0x7c, 0x00, 0x04, 0xac, 0x7c, 0x50, 0xfa, 0xa6, 0x60, 0x40, 0x0c, 0x00, 0x7c, 0x10, 0xfb, 0xa6, 33 | 0x7d, 0x29, 0x03, 0xa6, 0x4e, 0x80, 0x04, 0x21, 0x39, 0x7f, 0x01, 0x18, 0x83, 0x8b, 0x00, 0x04, 34 | 0x7f, 0x88, 0x03, 0xa6, 0xbb, 0x2b, 0xff, 0xe4, 0x7d, 0x61, 0x5b, 0x78, 0x4e, 0x80, 0x00, 0x20, 35 | 0x38, 0x40, 0x00, 0x00, 0x48, 0x00, 0x00, 0x0c, 0x7c, 0x03, 0x10, 0x6c, 0x38, 0x42, 0x00, 0x20, 36 | 0x7f, 0x82, 0x20, 0x40, 0x41, 0x9c, 0xff, 0xf4, 0x4e, 0x80, 0x00, 0x20, 0x7f, 0x83, 0x20, 0x40, 37 | 0x38, 0x05, 0x00, 0x01, 0x38, 0x40, 0x00, 0x00, 0x7c, 0x09, 0x03, 0xa6, 0x41, 0x9c, 0x00, 0x14, 38 | 0x48, 0x00, 0x00, 0x18, 0x7c, 0xa4, 0x10, 0xae, 0x7c, 0xa3, 0x11, 0xae, 0x38, 0x42, 0x00, 0x01, 39 | 0x42, 0x00, 0xff, 0xf4, 0x4e, 0x80, 0x00, 0x20, 0x41, 0xbd, 0x00, 0x18, 0x4e, 0x80, 0x00, 0x20, 40 | 0x38, 0xa5, 0xff, 0xff, 0x7c, 0x44, 0x28, 0xae, 0x7c, 0x43, 0x29, 0xae, 0x48, 0x00, 0x00, 0x0c, 41 | 0x38, 0xc5, 0x00, 0x01, 0x7c, 0xc9, 0x03, 0xa6, 0x42, 0x00, 0xff, 0xe8, 0x4e, 0x80, 0x00, 0x20 42 | 43 | }; 44 | const int stub_size = sizeof(stub); 45 | -------------------------------------------------------------------------------- /source/stub.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file was autogenerated by raw2c. 3 | Visit http://www.devkitpro.org 4 | */ 5 | 6 | //--------------------------------------------------------------------------------- 7 | #ifndef _stub_h_ 8 | #define _stub_h_ 9 | //--------------------------------------------------------------------------------- 10 | extern const unsigned char stub[]; 11 | extern const int stub_size; 12 | //--------------------------------------------------------------------------------- 13 | #endif //_stub_h_ 14 | //--------------------------------------------------------------------------------- 15 | -------------------------------------------------------------------------------- /source/sysconf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef GBI_SYSCONF_H 10 | #define GBI_SYSCONF_H 11 | 12 | #include 13 | #include 14 | 15 | #ifdef HW_DOL 16 | #define SYSCONF_VIDEO_NTSC SYS_VIDEO_NTSC 17 | #define SYSCONF_VIDEO_PAL SYS_VIDEO_PAL 18 | #define SYSCONF_VIDEO_MPAL SYS_VIDEO_MPAL 19 | 20 | #define SYSCONF_GetProgressiveScan() SYS_GetProgressiveScan() 21 | #define SYSCONF_GetVideoMode() SYS_GetVideoMode() 22 | #define SYSCONF_GetEuRGB60() SYS_GetEuRGB60() 23 | #define SYSCONF_GetDisplayOffsetH() SYS_GetDisplayOffsetH() 24 | #else 25 | #define SYSCONF_VIDEO_NTSC CONF_VIDEO_NTSC 26 | #define SYSCONF_VIDEO_PAL CONF_VIDEO_PAL 27 | #define SYSCONF_VIDEO_MPAL CONF_VIDEO_MPAL 28 | 29 | #define SYSCONF_GetProgressiveScan() CONF_GetProgressiveScan() > CONF_ERR_OK 30 | #define SYSCONF_GetVideoMode() CONF_GetVideo() 31 | #define SYSCONF_GetEuRGB60() CONF_GetEuRGB60() > CONF_ERR_OK 32 | #define SYSCONF_GetDisplayOffsetH() ({ int8_t offset = 0; CONF_GetDisplayOffsetH(&offset); offset; }) 33 | #endif 34 | 35 | #endif /* GBI_SYSCONF_H */ 36 | -------------------------------------------------------------------------------- /source/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef GBI_UTIL_H 10 | #define GBI_UTIL_H 11 | 12 | #include 13 | #include 14 | 15 | #define ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0])) 16 | #define SWAP(a, b) { typeof(a) tmp = b; b = a; a = tmp; } 17 | 18 | #define LERP(a, b, c, d) ((a * (d - (c)) + b * (c)) / d) 19 | #define LSHIFT(a, b) ((b) >= 0 ? (a) * (1 << (b)) : (a) / (1 << -(b))) 20 | 21 | typedef union { 22 | uint8_t u8[2]; 23 | int8_t s8[2]; 24 | uint16_t u16; 25 | int16_t s16; 26 | } hword_t; 27 | 28 | typedef union { 29 | uint8_t u8[4]; 30 | int8_t s8[4]; 31 | uint16_t u16[2]; 32 | int8_t s16[2]; 33 | uint32_t u32; 34 | int32_t s32; 35 | float_t f32; 36 | } word_t; 37 | 38 | typedef union { 39 | uint8_t u8[8]; 40 | int8_t s8[8]; 41 | uint16_t u16[4]; 42 | int16_t s16[4]; 43 | uint32_t u32[2]; 44 | int32_t s32[2]; 45 | float_t f32[2]; 46 | uint64_t u64; 47 | int64_t s64; 48 | double_t f64; 49 | } dword_t; 50 | 51 | #endif /* GBI_UTIL_H */ 52 | -------------------------------------------------------------------------------- /source/video.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include 11 | #include "state.h" 12 | #include "util.h" 13 | #include "video.h" 14 | 15 | static void *xfb[3]; 16 | static uint32_t xfb_index; 17 | 18 | GXRModeObj rmode; 19 | rect_t viewport, screen; 20 | timing_t viclock = { .reset = true, .hz = NAN }; 21 | 22 | static void vblank_cb(uint32_t retrace) 23 | { 24 | ClockTick(&viclock, 1); 25 | } 26 | 27 | void VideoSetup(uint32_t tvMode, uint32_t viMode, uint32_t xfbMode) 28 | { 29 | rmode.viTVMode = VI_TVMODE(tvMode, viMode); 30 | 31 | rmode.viXOrigin = 0; 32 | rmode.viYOrigin = 0; 33 | 34 | switch (tvMode) { 35 | case VI_HDCUSTOM: 36 | case VI_HD48: 37 | case VI_HD50: 38 | case VI_HD60: 39 | if ((viMode & VI_ENHANCED) == VI_ENHANCED) { 40 | rmode.viWidth = VI_MAX_WIDTH_FHD; 41 | rmode.viHeight = VI_MAX_HEIGHT_FHD; 42 | } else { 43 | rmode.viWidth = VI_MAX_WIDTH_HD; 44 | rmode.viHeight = VI_MAX_HEIGHT_HD; 45 | } 46 | break; 47 | case VI_DEBUG_PAL: 48 | case VI_PAL: 49 | rmode.viWidth = VI_MAX_WIDTH_PAL; 50 | rmode.viHeight = VI_MAX_HEIGHT_PAL; 51 | break; 52 | default: 53 | rmode.viWidth = VI_MAX_WIDTH_NTSC; 54 | rmode.viHeight = VI_MAX_HEIGHT_NTSC; 55 | } 56 | 57 | rmode.fbWidth = rmode.viWidth; 58 | rmode.efbHeight = 59 | rmode.xfbHeight = rmode.viHeight; 60 | 61 | if ((viMode & VI_STEREO) == VI_STEREO) { 62 | rmode.viXOrigin *= 2; 63 | rmode.viWidth *= 2; 64 | 65 | if (rmode.fbWidth <= 682) 66 | rmode.fbWidth *= 2; 67 | } 68 | 69 | if (xfbMode > VI_XFBMODE_SF) { 70 | rmode.efbHeight *= 2; 71 | rmode.xfbHeight *= 2; 72 | } 73 | 74 | if ((viMode & VI_ENHANCED) == VI_STANDARD) { 75 | rmode.efbHeight /= 2; 76 | rmode.xfbHeight /= 2; 77 | } 78 | 79 | viewport.x = (684 - rmode.fbWidth) / 4 * 2; 80 | viewport.y = (684 - rmode.efbHeight) / 4 * 2; 81 | 82 | viewport.w = rmode.fbWidth; 83 | viewport.h = rmode.efbHeight; 84 | 85 | rmode.efbHeight = rmode.efbHeight / (1 + rmode.efbHeight / 525); 86 | 87 | rmode.xfbMode = xfbMode; 88 | rmode.field_rendering = (viMode & VI_NON_INTERLACE) == VI_INTERLACE && xfbMode == VI_XFBMODE_SF; 89 | rmode.aa = GX_FALSE; 90 | 91 | if (tvMode < VI_HD60 && (viMode & VI_NON_INTERLACE) == VI_INTERLACE && xfbMode == VI_XFBMODE_DF) { 92 | rmode.vfilter[0] = 8; 93 | rmode.vfilter[1] = 8; 94 | rmode.vfilter[2] = 10; 95 | rmode.vfilter[3] = 12; 96 | rmode.vfilter[4] = 10; 97 | rmode.vfilter[5] = 8; 98 | rmode.vfilter[6] = 8; 99 | } else { 100 | rmode.vfilter[0] = 0; 101 | rmode.vfilter[1] = 0; 102 | rmode.vfilter[2] = 21; 103 | rmode.vfilter[3] = 22; 104 | rmode.vfilter[4] = 21; 105 | rmode.vfilter[5] = 0; 106 | rmode.vfilter[6] = 0; 107 | } 108 | #ifdef HW_RVL 109 | if (viMode == (VI_MONO | VI_NON_INTERLACE | VI_STANDARD | VI_CLOCK_27MHZ)) 110 | VIDEO_SetTrapFilter(true); 111 | #endif 112 | VIDEO_SetAdjustingValues(0, 0); 113 | VIDEO_Configure(&rmode); 114 | rmode.fbWidth = VIDEO_PadFramebufferWidth(rmode.fbWidth); 115 | 116 | for (int i = 0; i < ARRAY_ELEMS(xfb); i++) 117 | xfb[i] = SYS_AllocateFramebuffer(&rmode); 118 | VIDEO_SetNextFramebuffer(xfb[xfb_index]); 119 | 120 | VIDEO_SetBlack(false); 121 | VIDEO_Flush(); 122 | VIDEO_WaitVSync(); 123 | VIDEO_WaitVSync(); 124 | 125 | switch (VIDEO_GetCurrentTvMode()) { 126 | case VI_HDCUSTOM: 127 | case VI_HD48: 128 | case VI_HD50: 129 | case VI_HD60: 130 | screen.x = 0; 131 | screen.y = 0; 132 | 133 | if (state.aspect.h > state.aspect.w) { 134 | screen.w = 540; 135 | screen.h = 540 * state.aspect.h / state.aspect.w; 136 | } else { 137 | screen.w = 540 * state.aspect.w / state.aspect.h; 138 | screen.h = 540; 139 | } 140 | break; 141 | case VI_DEBUG_PAL: 142 | case VI_PAL: 143 | screen.x = 0; 144 | screen.y = 0; 145 | 146 | if (state.aspect.h > state.aspect.w) { 147 | screen.w = 576; 148 | screen.h = 576 * state.aspect.h / state.aspect.w; 149 | } else { 150 | screen.w = 576 * state.aspect.w / state.aspect.h; 151 | screen.h = 576; 152 | } 153 | break; 154 | default: 155 | screen.x = 0; 156 | screen.y = 3; 157 | 158 | if (state.aspect.h > state.aspect.w) { 159 | screen.w = 480; 160 | screen.h = 480 * state.aspect.h / state.aspect.w; 161 | } else { 162 | screen.w = 480 * state.aspect.w / state.aspect.h; 163 | screen.h = 480; 164 | } 165 | } 166 | 167 | screen.w = (screen.w + 1) & ~1; 168 | screen.h = (screen.h + 1) & ~1; 169 | 170 | VIDEO_SetPreRetraceCallback(vblank_cb); 171 | } 172 | 173 | void VideoBlackOut(void) 174 | { 175 | VIDEO_SetBlack(true); 176 | VIDEO_Flush(); 177 | VIDEO_WaitVSync(); 178 | VIDEO_WaitVSync(); 179 | } 180 | 181 | void *VideoGetFramebuffer(uint32_t *index) 182 | { 183 | do { 184 | xfb_index = (xfb_index + 1) % ARRAY_ELEMS(xfb); 185 | } while (xfb[xfb_index] == VIDEO_GetCurrentFramebuffer()); 186 | 187 | if (index) 188 | *index = xfb_index; 189 | return xfb[xfb_index]; 190 | } 191 | 192 | void VideoSetFramebuffer(uint32_t index) 193 | { 194 | VIDEO_SetNextFramebuffer(xfb[index % ARRAY_ELEMS(xfb)]); 195 | VIDEO_Flush(); 196 | } 197 | -------------------------------------------------------------------------------- /source/video.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef GBI_VIDEO_H 10 | #define GBI_VIDEO_H 11 | 12 | #include 13 | #include 14 | #include "clock.h" 15 | 16 | typedef struct { 17 | int16_t x, y; 18 | uint16_t w, h; 19 | } rect_t; 20 | 21 | extern GXRModeObj rmode; 22 | extern rect_t viewport, screen; 23 | extern timing_t viclock; 24 | 25 | void VideoSetup(uint32_t tvMode, uint32_t viMode, uint32_t xfbMode); 26 | void VideoBlackOut(void); 27 | void *VideoGetFramebuffer(uint32_t *index); 28 | void VideoSetFramebuffer(uint32_t index); 29 | 30 | #endif /* GBI_VIDEO_H */ 31 | -------------------------------------------------------------------------------- /source/vm/dsi_handler.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | 11 | .section .text.dsi_exceptionhandler,"ax",@progbits 12 | .globl dsi_exceptionhandler 13 | dsi_exceptionhandler: 14 | stw %r11,GPR1_OFFSET(%sp) 15 | stmw %r13,GPR13_OFFSET(%sp) 16 | psq_st %f0,PSR0_OFFSET(%sp),0,0 17 | stfd %f0,FPR0_OFFSET(%sp) 18 | psq_st %f1,PSR1_OFFSET(%sp),0,0 19 | stfd %f1,FPR1_OFFSET(%sp) 20 | psq_st %f2,PSR2_OFFSET(%sp),0,0 21 | stfd %f2,FPR2_OFFSET(%sp) 22 | psq_st %f3,PSR3_OFFSET(%sp),0,0 23 | stfd %f3,FPR3_OFFSET(%sp) 24 | psq_st %f4,PSR4_OFFSET(%sp),0,0 25 | stfd %f4,FPR4_OFFSET(%sp) 26 | psq_st %f5,PSR5_OFFSET(%sp),0,0 27 | stfd %f5,FPR5_OFFSET(%sp) 28 | psq_st %f6,PSR6_OFFSET(%sp),0,0 29 | stfd %f6,FPR6_OFFSET(%sp) 30 | psq_st %f7,PSR7_OFFSET(%sp),0,0 31 | stfd %f7,FPR7_OFFSET(%sp) 32 | psq_st %f8,PSR8_OFFSET(%sp),0,0 33 | stfd %f8,FPR8_OFFSET(%sp) 34 | psq_st %f9,PSR9_OFFSET(%sp),0,0 35 | stfd %f9,FPR9_OFFSET(%sp) 36 | psq_st %f10,PSR10_OFFSET(%sp),0,0 37 | stfd %f10,FPR10_OFFSET(%sp) 38 | psq_st %f11,PSR11_OFFSET(%sp),0,0 39 | stfd %f11,FPR11_OFFSET(%sp) 40 | psq_st %f12,PSR12_OFFSET(%sp),0,0 41 | stfd %f12,FPR12_OFFSET(%sp) 42 | psq_st %f13,PSR13_OFFSET(%sp),0,0 43 | stfd %f13,FPR13_OFFSET(%sp) 44 | mffs %f0 45 | stfd %f0,FPSCR_OFFSET(%sp) 46 | 47 | mfdsisr %r3 48 | mfdar %r4 49 | bl vm_dsi_handler 50 | cmpwi %r3,0 51 | bne 1f 52 | 53 | mr %r3,%sp 54 | b c_default_exceptionhandler 55 | 56 | 1: lfd %f0,FPSCR_OFFSET(%sp) 57 | mtfsf 0xFF,%f0 58 | psq_l %f13,PSR13_OFFSET(%sp),0,0 59 | lfd %f13,FPR13_OFFSET(%sp) 60 | psq_l %f12,PSR12_OFFSET(%sp),0,0 61 | lfd %f12,FPR12_OFFSET(%sp) 62 | psq_l %f11,PSR11_OFFSET(%sp),0,0 63 | lfd %f11,FPR11_OFFSET(%sp) 64 | psq_l %f10,PSR10_OFFSET(%sp),0,0 65 | lfd %f10,FPR10_OFFSET(%sp) 66 | psq_l %f9,PSR9_OFFSET(%sp),0,0 67 | lfd %f9,FPR9_OFFSET(%sp) 68 | psq_l %f8,PSR8_OFFSET(%sp),0,0 69 | lfd %f8,FPR8_OFFSET(%sp) 70 | psq_l %f7,PSR7_OFFSET(%sp),0,0 71 | lfd %f7,FPR7_OFFSET(%sp) 72 | psq_l %f6,PSR6_OFFSET(%sp),0,0 73 | lfd %f6,FPR6_OFFSET(%sp) 74 | psq_l %f5,PSR5_OFFSET(%sp),0,0 75 | lfd %f5,FPR5_OFFSET(%sp) 76 | psq_l %f4,PSR4_OFFSET(%sp),0,0 77 | lfd %f4,FPR4_OFFSET(%sp) 78 | psq_l %f3,PSR3_OFFSET(%sp),0,0 79 | lfd %f3,FPR3_OFFSET(%sp) 80 | psq_l %f2,PSR2_OFFSET(%sp),0,0 81 | lfd %f2,FPR2_OFFSET(%sp) 82 | psq_l %f1,PSR1_OFFSET(%sp),0,0 83 | lfd %f1,FPR1_OFFSET(%sp) 84 | psq_l %f0,PSR0_OFFSET(%sp),0,0 85 | lfd %f0,FPR0_OFFSET(%sp) 86 | lwz %r0,CR_OFFSET(%sp) 87 | mtcr %r0 88 | lwz %r0,LR_OFFSET(%sp) 89 | mtlr %r0 90 | lwz %r0,CTR_OFFSET(%sp) 91 | mtctr %r0 92 | lwz %r0,XER_OFFSET(%sp) 93 | mtxer %r0 94 | lwz %r0,SRR0_OFFSET(%sp) 95 | mtsrr0 %r0 96 | lwz %r0,SRR1_OFFSET(%sp) 97 | mtsrr1 %r0 98 | lwz %r12,GPR12_OFFSET(%sp) 99 | lwz %r11,GPR11_OFFSET(%sp) 100 | lwz %r10,GPR10_OFFSET(%sp) 101 | lwz %r9,GPR9_OFFSET(%sp) 102 | lwz %r8,GPR8_OFFSET(%sp) 103 | lwz %r7,GPR7_OFFSET(%sp) 104 | lwz %r6,GPR6_OFFSET(%sp) 105 | lwz %r5,GPR5_OFFSET(%sp) 106 | lwz %r4,GPR4_OFFSET(%sp) 107 | lwz %r3,GPR3_OFFSET(%sp) 108 | lwz %r2,GPR2_OFFSET(%sp) 109 | lwz %r0,GPR0_OFFSET(%sp) 110 | addi %sp,%sp,EXCEPTION_FRAME_END 111 | rfi 112 | -------------------------------------------------------------------------------- /source/vm/vm.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 tueidj All Rights Reserved 2 | * This code may not be used in any project 3 | * without explicit permission from the author. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "vm.h" 13 | 14 | #include 15 | 16 | // maximum virtual memory size 17 | #define MAX_VM_SIZE (256*1024*1024) 18 | // maximum physical memory size 19 | #define MAX_MEM_SIZE ( 8*1024*1024) 20 | // minimum physical memory size 21 | #define MIN_MEM_SIZE (256*1024) 22 | // page size as defined by hardware 23 | #define PAGE_SIZE 4096 24 | #define PAGE_MASK (~(PAGE_SIZE-1)) 25 | 26 | #define VM_VSID 0 27 | #define VM_SEGMENT 0x70000000 28 | 29 | // use 64KB for PTEs 30 | #define HTABMASK 0 31 | #define PTE_SIZE ((HTABMASK+1)*65536) 32 | #define PTE_COUNT (PTE_SIZE>>3) 33 | 34 | #define VM_FILENAME "/tmp/pagefile.sys" 35 | 36 | // keeps a record of each currently mapped page 37 | typedef union 38 | { 39 | u32 data; 40 | struct 41 | { 42 | // is this a valid physical page? 43 | u32 valid : 1; 44 | // can this page be flushed? 45 | u32 locked : 1; 46 | // does this page contain changes? 47 | u32 dirty : 1; 48 | // PTE for this physical page 49 | u32 pte_index : 13; 50 | // virtual page index for this physical page 51 | u32 page_index : 16; 52 | }; 53 | } p_map; 54 | 55 | // maps VM addresses to mapped pages 56 | typedef struct 57 | { 58 | // data must be fetched when paging in? 59 | u16 committed : 1; 60 | // physical page index for this virtual page 61 | u16 p_map_index: 12; 62 | } vm_map; 63 | 64 | typedef union 65 | { 66 | u32 data[2]; 67 | struct 68 | { 69 | u32 valid : 1; 70 | u32 VSID : 24; 71 | u32 hash : 1; 72 | u32 API : 6; 73 | 74 | u32 RPN : 20; 75 | u32 pad0 : 3; 76 | u32 R : 1; 77 | u32 C : 1; 78 | u32 WIMG : 4; 79 | u32 pad1 : 1; 80 | u32 PP : 2; 81 | }; 82 | } PTE; 83 | typedef PTE* PTEG; 84 | 85 | typedef u8 vm_page[PAGE_SIZE]; 86 | 87 | static p_map phys_map[2048]; 88 | static vm_map virt_map[65536]; 89 | static u16 pmap_max, pmap_head; 90 | 91 | static PTE* HTABORG; 92 | static vm_page* VM_Base; 93 | static vm_page* MEM_Base = NULL; 94 | 95 | static ARQRequest vm_request; 96 | static mutex_t vm_mutex = LWP_MUTEX_NULL; 97 | static u32 vm_initialized = 0; 98 | 99 | static __inline__ void tlbie(void* p) 100 | { 101 | asm volatile("tlbie %0" :: "r"(p)); 102 | } 103 | 104 | static u16 locate_oldest(void) 105 | { 106 | u16 head = pmap_head; 107 | 108 | for(;;++head) 109 | { 110 | PTE *p; 111 | 112 | if (head >= pmap_max) 113 | head = 0; 114 | 115 | if (!phys_map[head].valid || phys_map[head].locked) 116 | continue; 117 | 118 | p = HTABORG+phys_map[head].pte_index; 119 | tlbie(VM_Base+phys_map[head].page_index); 120 | 121 | if (p->C) 122 | { 123 | p->C = 0; 124 | phys_map[head].dirty = 1; 125 | continue; 126 | } 127 | 128 | if (p->R) 129 | { 130 | p->R = 0; 131 | continue; 132 | } 133 | 134 | p->data[0] = 0; 135 | 136 | pmap_head = head+1; 137 | return head; 138 | } 139 | } 140 | 141 | static PTE* StorePTE(PTEG pteg, u32 virtual, u32 physical, u8 WIMG, u8 PP, int secondary) 142 | { 143 | int i; 144 | PTE p = {{0}}; 145 | 146 | p.valid = 1; 147 | p.VSID = VM_VSID; 148 | p.hash = secondary ? 1:0; 149 | p.API = virtual >> 22; 150 | p.RPN = physical >> 12; 151 | p.WIMG = WIMG; 152 | p.PP = PP; 153 | 154 | for (i=0; i < 8; i++) 155 | { 156 | if (pteg[i].data[0] == p.data[0]) 157 | { 158 | // printf("Error: address %08x already had a PTE entry\n", virtual); 159 | // abort(); 160 | } 161 | else if (pteg[i].valid) 162 | continue; 163 | 164 | asm volatile("tlbie %0" : : "r"(virtual)); 165 | pteg[i].data[1] = p.data[1]; 166 | pteg[i].data[0] = p.data[0]; 167 | // if (i || secondary) 168 | // printf("PTE for address %08x/%08x in PTEG %p index %d (%s)\n", virtual, physical, pteg, i, secondary ? "secondary" : "primary"); 169 | return pteg+i; 170 | } 171 | 172 | return NULL; 173 | } 174 | 175 | static PTEG CalcPTEG(u32 virtual, int secondary) 176 | { 177 | uint32_t segment_index = (virtual >> 12) & 0xFFFF; 178 | u32 ptr = MEM_VIRTUAL_TO_PHYSICAL(HTABORG); 179 | u32 hash = segment_index ^ VM_VSID; 180 | 181 | if (secondary) hash = ~hash; 182 | 183 | hash &= (HTABMASK << 10) | 0x3FF; 184 | ptr |= hash << 6; 185 | 186 | return (PTEG)MEM_PHYSICAL_TO_K0(ptr); 187 | } 188 | 189 | static PTE* insert_pte(u16 index, u32 physical, u8 WIMG, u8 PP) 190 | { 191 | PTE *pte; 192 | int i; 193 | u32 virtual = (u32)(VM_Base+index); 194 | 195 | for (i=0; i < 2; i++) 196 | { 197 | PTEG pteg = CalcPTEG(virtual, i); 198 | pte = StorePTE(pteg, virtual, physical, WIMG, PP, i); 199 | if (pte) 200 | return pte; 201 | } 202 | 203 | // printf("Failed to insert PTE for %p\n", VM_Base+index); 204 | // abort(); 205 | 206 | return NULL; 207 | } 208 | 209 | static void tlbia(void) 210 | { 211 | int i; 212 | for (i=0; i < 64; i++) 213 | asm volatile("tlbie %0" :: "r" (i*PAGE_SIZE)); 214 | } 215 | 216 | void __exception_sethandler(u32 nExcept, void (*pHndl)()); 217 | extern void default_exceptionhandler(); 218 | extern void dsi_exceptionhandler(); 219 | 220 | void* VM_Init(size_t VMSize, size_t MEMSize) 221 | { 222 | u32 i; 223 | u16 index, v_index; 224 | 225 | if (vm_initialized) 226 | { 227 | vm_initialized++; 228 | return VM_Base; 229 | } 230 | 231 | // parameter checking 232 | if (VMSize>MAX_VM_SIZE || MEMSizeMAX_MEM_SIZE) 233 | { 234 | errno = EINVAL; 235 | return NULL; 236 | } 237 | 238 | VMSize = (VMSize+PAGE_SIZE-1)&PAGE_MASK; 239 | MEMSize = (MEMSize+PAGE_SIZE-1)&PAGE_MASK; 240 | VM_Base = (vm_page*)(0x80000000 - VMSize); 241 | pmap_max = MEMSize / PAGE_SIZE; 242 | 243 | // printf("VMSize %08x MEMSize %08x VM_Base %p pmap_max %u\n", VMSize, MEMSize, VM_Base, pmap_max); 244 | 245 | if (VMSize <= MEMSize) 246 | { 247 | errno = EINVAL; 248 | return NULL; 249 | } 250 | 251 | if (LWP_MutexInit(&vm_mutex, 0) != 0) 252 | { 253 | errno = ENOLCK; 254 | return NULL; 255 | } 256 | 257 | MEM_Base = (vm_page*)SYS_AllocArenaMemHi(MEMSize, PAGE_SIZE); 258 | 259 | // printf("MEM_Base: %p\n", MEM_Base); 260 | 261 | tlbia(); 262 | DCZeroRange(MEM_Base, MEMSize); 263 | HTABORG = (PTE*)(((u32)MEM_Base+0xFFFF)&~0xFFFF); 264 | // printf("HTABORG: %p\n", HTABORG); 265 | 266 | // initial commit: map pmap_max pages to fill PTEs with valid RPNs 267 | for (index=0,v_index=0; index %u\n", index, index+(PTE_SIZE/PAGE_SIZE)); 272 | for (i=0; i<(PTE_SIZE/PAGE_SIZE); ++i,++index) 273 | phys_map[index].valid = 0; 274 | 275 | --index, --v_index; 276 | continue; 277 | } 278 | 279 | phys_map[index].valid = 1; 280 | phys_map[index].locked = 0; 281 | phys_map[index].dirty = 0; 282 | phys_map[index].page_index = v_index; 283 | phys_map[index].pte_index = insert_pte(v_index, MEM_VIRTUAL_TO_PHYSICAL(MEM_Base+index), 0, 0b10) - HTABORG; 284 | virt_map[v_index].committed = 0; 285 | virt_map[v_index].p_map_index = index; 286 | } 287 | 288 | // all indexes up to 65536 289 | for (; v_index; ++v_index) 290 | { 291 | virt_map[v_index].committed = 0; 292 | virt_map[v_index].p_map_index = pmap_max; 293 | } 294 | 295 | pmap_head = 0; 296 | 297 | // set SDR1 298 | mtspr(25, MEM_VIRTUAL_TO_PHYSICAL(HTABORG)|HTABMASK); 299 | // printf("SDR1: %08x\n", MEM_VIRTUAL_TO_PHYSICAL(HTABORG)); 300 | // enable SR 301 | asm volatile("mtsrin %0,%1" :: "r"(VM_VSID), "r"(VM_Base)); 302 | // hook DSI 303 | __exception_sethandler(EX_DSI, dsi_exceptionhandler); 304 | 305 | vm_initialized = 1; 306 | 307 | return VM_Base; 308 | } 309 | 310 | void VM_Deinit(void) 311 | { 312 | if (--vm_initialized) 313 | return; 314 | 315 | // disable SR 316 | asm volatile("mtsrin %0,%1" :: "r"(0x80000000), "r"(VM_Base)); 317 | // restore default DSI handler 318 | __exception_sethandler(EX_DSI, default_exceptionhandler); 319 | 320 | if (vm_mutex != LWP_MUTEX_NULL) 321 | { 322 | LWP_MutexDestroy(vm_mutex); 323 | vm_mutex = LWP_MUTEX_NULL; 324 | } 325 | } 326 | 327 | void VM_InvalidateAll(void) 328 | { 329 | u16 index; 330 | u32 irq; 331 | 332 | if (!vm_initialized) 333 | return; 334 | 335 | LWP_MutexLock(vm_mutex); 336 | 337 | _CPU_ISR_Disable(irq); 338 | 339 | tlbia(); 340 | 341 | for (index=0; index < pmap_max; index++) 342 | { 343 | if (phys_map[index].valid) 344 | { 345 | PTE *p = HTABORG+phys_map[index].pte_index; 346 | 347 | // clear reference bits 348 | p->R = 0; 349 | p->C = 0; 350 | // unlock 351 | phys_map[index].locked = 0; 352 | // page is clean 353 | phys_map[index].dirty = 0; 354 | // clear physical memory 355 | DCZeroRange(MEM_Base+index, PAGE_SIZE); 356 | } 357 | 358 | virt_map[index].committed = 0; 359 | } 360 | 361 | for (; index; index++) 362 | virt_map[index].committed = 0; 363 | 364 | pmap_head = 0; 365 | 366 | _CPU_ISR_Restore(irq); 367 | 368 | // printf("VM was invalidated\n"); 369 | 370 | LWP_MutexUnlock(vm_mutex); 371 | } 372 | 373 | int vm_dsi_handler(u32 DSISR, u32 DAR) 374 | { 375 | u16 virt_index; 376 | u16 phys_index; 377 | u16 flush_v_index; 378 | 379 | if (DAR<(u32)VM_Base || DAR>=0x80000000) 380 | return 0; 381 | if ((DSISR&~0x02000000)!=0x40000000) 382 | return 0; 383 | if (!vm_initialized) 384 | return 0; 385 | 386 | LWP_MutexLock(vm_mutex); 387 | 388 | DAR &= ~0xFFF; 389 | virt_index = (vm_page*)DAR - VM_Base; 390 | 391 | phys_index = locate_oldest(); 392 | 393 | // purge phys_index if it's dirty 394 | if (phys_map[phys_index].dirty) 395 | { 396 | unsigned int pages_to_flush; 397 | 398 | flush_v_index = phys_map[phys_index].page_index; 399 | virt_map[flush_v_index].committed = 1; 400 | // mark this virtual page as unmapped 401 | virt_map[flush_v_index].p_map_index = pmap_max; 402 | phys_map[phys_index].dirty = 0; 403 | 404 | // optimize by flushing up to four dirty pages at once 405 | for (pages_to_flush=1; pages_to_flush < 4; pages_to_flush++) 406 | { 407 | PTE *p; 408 | 409 | // check for end of physical mem 410 | if (phys_index+pages_to_flush >= pmap_max) 411 | break; 412 | 413 | // check for p_map hole 414 | if (!phys_map[pmap_head].valid) 415 | break; 416 | 417 | // make sure physical pages hold consecutive virtual pages 418 | if (phys_map[pmap_head].page_index != flush_v_index+pages_to_flush) 419 | break; 420 | 421 | // page should only be flushed if it's dirty 422 | p = HTABORG+phys_map[pmap_head].pte_index; 423 | if (p->C) 424 | { 425 | tlbie(VM_Base+phys_map[pmap_head].page_index); 426 | p->C = 0; 427 | } 428 | else if (!phys_map[pmap_head].dirty) 429 | break; 430 | 431 | virt_map[flush_v_index+pages_to_flush].committed = 1; 432 | phys_map[pmap_head].dirty = 0; 433 | pmap_head++; 434 | } 435 | 436 | DCFlushRange(MEM_Base+phys_index, PAGE_SIZE*pages_to_flush); 437 | ARQ_PostRequest(&vm_request, VM_VSID, ARQ_MRAMTOARAM, ARQ_PRIO_LO, flush_v_index*PAGE_SIZE, MEM_Base+phys_index, PAGE_SIZE*pages_to_flush); 438 | // printf("VM page %d was purged (%d)\n", phys_map[phys_index].page_index, pages_to_flush); 439 | } 440 | 441 | // fetch virtual_index if it has been previously committed 442 | if (virt_map[virt_index].committed) 443 | { 444 | ARQ_PostRequest(&vm_request, VM_VSID, ARQ_ARAMTOMRAM, ARQ_PRIO_HI, virt_index*PAGE_SIZE, MEM_Base+phys_index, PAGE_SIZE); 445 | DCInvalidateRange(MEM_Base+phys_index, PAGE_SIZE); 446 | // printf("VM page %d was fetched\n", virt_index); 447 | } 448 | else 449 | DCZeroRange(MEM_Base+phys_index, PAGE_SIZE); 450 | 451 | // printf("VM page %u (0x%08x) replaced page %u (%p) @ %p\n", virt_index, DAR, phys_map[phys_index].page_index, VM_Base+phys_map[phys_index].page_index, MEM_Base+phys_index); 452 | 453 | virt_map[virt_index].p_map_index = phys_index; 454 | phys_map[phys_index].page_index = virt_index; 455 | phys_map[phys_index].pte_index = insert_pte(virt_index, MEM_VIRTUAL_TO_PHYSICAL(MEM_Base+phys_index), 0, 0b10) - HTABORG; 456 | 457 | LWP_MutexUnlock(vm_mutex); 458 | 459 | return 1; 460 | } 461 | -------------------------------------------------------------------------------- /source/vm/vm.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 tueidj All Rights Reserved 2 | * This code may not be used in any project 3 | * without explicit permission from the author. 4 | */ 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | void* VM_Init(size_t VMSize, size_t MEMSize); 11 | void VM_Deinit(void); 12 | 13 | // clears entire VM range to zero, unlocks any locked pages 14 | void VM_InvalidateAll(void); 15 | 16 | #ifdef __cplusplus 17 | } 18 | #endif -------------------------------------------------------------------------------- /source/wiiload.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "gba_mb.h" 16 | #include "gx.h" 17 | #include "network.h" 18 | #include "state.h" 19 | #include "stub.h" 20 | #include "wiiload.h" 21 | 22 | #ifdef HW_DOL 23 | #define STUB_ADDR 0x80001000 24 | #define STUB_STACK 0x80003000 25 | #else 26 | #define STUB_ADDR 0x90000000 27 | #define STUB_STACK 0x90000800 28 | #endif 29 | 30 | static lwp_t thread = LWP_THREAD_NULL; 31 | 32 | wiiload_state_t wiiload = { 33 | .sv.sd = INVALID_SOCKET, 34 | .sv.sin.sin_family = AF_INET, 35 | .sv.sin.sin_port = 4299, 36 | .sv.sin.sin_addr.s_addr = INADDR_ANY, 37 | .sv.sinlen = sizeof(struct sockaddr), 38 | 39 | .cl.sd = INVALID_SOCKET, 40 | .cl.sinlen = sizeof(struct sockaddr) 41 | }; 42 | 43 | static bool wiiload_read_file(int fd, int size) 44 | { 45 | void *buf = wiiload.task.buf; 46 | wiiload.task.buf = NULL; 47 | wiiload.task.bufpos = 0; 48 | wiiload.task.buflen = size; 49 | buf = realloc(buf, size); 50 | 51 | if (!buf) 52 | goto fail; 53 | if (read(fd, buf, size) < size) 54 | goto fail; 55 | 56 | wiiload.task.buf = buf; 57 | return true; 58 | 59 | fail: 60 | free(buf); 61 | return false; 62 | } 63 | 64 | static bool wiiload_read(int sd, int insize, int outsize) 65 | { 66 | Byte inbuf[4096]; 67 | z_stream zstream = {0}; 68 | int ret, len = 0; 69 | 70 | void *buf = wiiload.task.buf; 71 | wiiload.task.buf = NULL; 72 | wiiload.task.bufpos = 0; 73 | wiiload.task.buflen = outsize; 74 | buf = realloc(buf, outsize); 75 | 76 | if (!buf) 77 | goto fail; 78 | if (inflateInit(&zstream) < 0) 79 | goto fail; 80 | 81 | zstream.next_out = buf; 82 | zstream.avail_out = outsize; 83 | 84 | while (len < insize) { 85 | ret = tcp_read(sd, inbuf, MIN(insize - len, sizeof(inbuf)), 1); 86 | if (ret < 0) goto fail; 87 | else len += ret; 88 | 89 | zstream.next_in = inbuf; 90 | zstream.avail_in = ret; 91 | ret = inflate(&zstream, Z_NO_FLUSH); 92 | wiiload.task.bufpos = zstream.total_out; 93 | if (ret < 0) goto fail; 94 | } 95 | 96 | inflateEnd(&zstream); 97 | 98 | wiiload.task.buf = buf; 99 | return true; 100 | 101 | fail: 102 | inflateEnd(&zstream); 103 | 104 | free(buf); 105 | return false; 106 | } 107 | 108 | static bool wiiload_read_args(int sd, int size) 109 | { 110 | void *arg = wiiload.task.arg; 111 | wiiload.task.arg = NULL; 112 | wiiload.task.arglen = size; 113 | arg = realloc(arg, size); 114 | 115 | if (!arg) 116 | goto fail; 117 | if (tcp_read_complete(sd, arg, size) < size) 118 | goto fail; 119 | 120 | wiiload.task.arg = arg; 121 | return true; 122 | 123 | fail: 124 | free(arg); 125 | return false; 126 | } 127 | 128 | static bool is_type_tpl(void *buffer, int size) 129 | { 130 | tpl_header_t *header = buffer; 131 | 132 | if (size < sizeof(*header)) 133 | return false; 134 | if (header->version != 2142000) 135 | return false; 136 | if (header->count == 0) 137 | return false; 138 | if (header->size != sizeof(*header)) 139 | return false; 140 | 141 | return true; 142 | } 143 | 144 | static bool is_type_mb(void *buffer, int size) 145 | { 146 | if (size < 0xC0 || size > 0x40000) 147 | return false; 148 | if (memcmp(buffer + 0x04, gba_mb + 0x04, 0x9C)) 149 | return false; 150 | 151 | return true; 152 | } 153 | 154 | static bool is_type_gci(void *buffer, int size) 155 | { 156 | gci_header_t *header = buffer; 157 | 158 | if (size < sizeof(*header)) 159 | return false; 160 | if (size != sizeof(*header) + header->length * 8192) 161 | return false; 162 | if (header->length < 1 || header->length > 2043) 163 | return false; 164 | if (header->padding0 != 0xFF || header->padding1 != 0xFFFF) 165 | return false; 166 | if (header->icon_offset != -1 && header->icon_offset > 512) 167 | return false; 168 | if (header->comment_offset != -1 && header->comment_offset > 8128) 169 | return false; 170 | 171 | for (int i = 0; i < 4; i++) 172 | if (!isalnum(header->gamecode[i])) 173 | return false; 174 | 175 | for (int i = 0; i < 2; i++) 176 | if (!isalnum(header->company[i])) 177 | return false; 178 | 179 | return true; 180 | } 181 | 182 | static bool is_type_dol(void *buffer, int size) 183 | { 184 | dol_header_t *header = buffer; 185 | 186 | if (size < sizeof(*header)) 187 | return false; 188 | 189 | for (int i = 0; i < 7; i++) 190 | if (header->padding[i]) 191 | return false; 192 | 193 | for (int i = 0; i < 7; i++) { 194 | if (header->text_size[i]) { 195 | if (header->text_offset[i] < sizeof(*header)) 196 | return false; 197 | if ((header->text_address[i] & SYS_BASE_UNCACHED) != SYS_BASE_CACHED) 198 | return false; 199 | } 200 | } 201 | 202 | for (int i = 0; i < 11; i++) { 203 | if (header->data_size[i]) { 204 | if (header->data_offset[i] < sizeof(*header)) 205 | return false; 206 | if ((header->data_address[i] & SYS_BASE_UNCACHED) != SYS_BASE_CACHED) 207 | return false; 208 | } 209 | } 210 | 211 | if (header->bss_size) { 212 | if ((header->bss_address & SYS_BASE_UNCACHED) != SYS_BASE_CACHED) 213 | return false; 214 | } 215 | 216 | if ((header->entrypoint & SYS_BASE_UNCACHED) != SYS_BASE_CACHED) 217 | return false; 218 | 219 | return true; 220 | } 221 | 222 | static bool handle_conn(int sd) 223 | { 224 | wiiload_header_t header; 225 | 226 | if (tcp_read_complete(sd, &header, sizeof(header)) < sizeof(header)) 227 | return false; 228 | if (header.magic != 'HAXX') 229 | return false; 230 | if (header.version != 5) 231 | return false; 232 | 233 | wiiload.task.type = TYPE_NONE; 234 | 235 | if (wiiload_read(sd, header.deflate_size, header.inflate_size) && 236 | wiiload_read_args(sd, header.args_size)) { 237 | 238 | if (is_type_tpl(wiiload.task.buf, wiiload.task.buflen)) { 239 | wiiload.task.type = TYPE_TPL; 240 | GXOverlayReadMem(wiiload.task.buf, wiiload.task.buflen, state.overlay_id); 241 | } else if (is_type_mb(wiiload.task.buf, wiiload.task.buflen)) { 242 | wiiload.task.type = TYPE_MB; 243 | if (state.draw_osd) state.reset = true; 244 | } else if (is_type_gci(wiiload.task.buf, wiiload.task.buflen)) { 245 | wiiload.task.type = TYPE_GCI; 246 | } else if (is_type_dol(wiiload.task.buf, wiiload.task.buflen)) { 247 | wiiload.task.type = TYPE_DOL; 248 | if (state.draw_osd) state.quit |= KEY_QUIT; 249 | } 250 | } 251 | 252 | return true; 253 | } 254 | 255 | static void *thread_func(void *arg) 256 | { 257 | wiiload.sv.sd = net_socket(AF_INET, SOCK_STREAM, IPPROTO_IP); 258 | 259 | if (wiiload.sv.sd == INVALID_SOCKET) 260 | goto fail; 261 | if (net_bind(wiiload.sv.sd, (struct sockaddr *)&wiiload.sv.sin, wiiload.sv.sinlen) < 0) 262 | goto fail; 263 | if (net_listen(wiiload.sv.sd, 0) < 0) 264 | goto fail; 265 | 266 | do { 267 | #ifdef HW_DOL 268 | fd_set readset; 269 | struct timeval tv; 270 | 271 | FD_ZERO(&readset); 272 | FD_SET(wiiload.sv.sd, &readset); 273 | 274 | tv.tv_sec = 1; tv.tv_usec = 0; 275 | net_select(FD_SETSIZE, &readset, NULL, NULL, &tv); 276 | 277 | if (FD_ISSET(wiiload.sv.sd, &readset)) { 278 | #else 279 | struct pollsd psd = {wiiload.sv.sd, POLLIN, 0}; 280 | net_poll(&psd, 1, 1000); 281 | 282 | if (psd.revents & POLLIN) { 283 | #endif 284 | wiiload.cl.sd = net_accept(wiiload.sv.sd, (struct sockaddr *)&wiiload.cl.sin, &wiiload.cl.sinlen); 285 | 286 | if (wiiload.cl.sd != INVALID_SOCKET) { 287 | handle_conn(wiiload.cl.sd); 288 | net_close(wiiload.cl.sd); 289 | wiiload.cl.sd = INVALID_SOCKET; 290 | } 291 | } 292 | } while (!state.quit); 293 | 294 | fail: 295 | net_close(wiiload.sv.sd); 296 | wiiload.sv.sd = INVALID_SOCKET; 297 | return NULL; 298 | } 299 | 300 | void WIILOADInit(void) 301 | { 302 | LWP_CreateThread(&thread, thread_func, NULL, NULL, 0, LWP_PRIO_NORMAL); 303 | } 304 | 305 | void WIILOADLoad(void) 306 | { 307 | LWP_JoinThread(thread, NULL); 308 | thread = LWP_THREAD_NULL; 309 | 310 | #ifdef HW_DOL 311 | if (wiiload.task.type != TYPE_DOL) 312 | WIILOADReadFile("/AUTOEXEC.DOL"); 313 | #endif 314 | 315 | if (wiiload.task.type == TYPE_DOL) { 316 | memcpy((void *)STUB_ADDR, stub, stub_size); 317 | DCStoreRange((void *)STUB_ADDR, stub_size); 318 | 319 | SYS_ResetSystem(SYS_SHUTDOWN); 320 | SYS_SwitchFiber((intptr_t)wiiload.task.buf, wiiload.task.buflen, 321 | (intptr_t)wiiload.task.arg, wiiload.task.arglen, 322 | STUB_ADDR, STUB_STACK); 323 | } 324 | } 325 | 326 | bool WIILOADReadFile(const char *file) 327 | { 328 | int fd = open(file, O_RDONLY); 329 | struct stat st; 330 | 331 | if (fd < 0) 332 | return false; 333 | if (fstat(fd, &st) < 0) 334 | return false; 335 | 336 | wiiload.task.type = TYPE_NONE; 337 | 338 | if (wiiload_read_file(fd, st.st_size)) 339 | wiiload.task.type = TYPE_DOL; 340 | 341 | close(fd); 342 | return true; 343 | } 344 | -------------------------------------------------------------------------------- /source/wiiload.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2025, Extrems' Corner.org 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 7 | */ 8 | 9 | #ifndef GBI_WIILOAD_H 10 | #define GBI_WIILOAD_H 11 | 12 | #include 13 | 14 | typedef struct { 15 | struct { 16 | int sd; 17 | struct sockaddr_in sin; 18 | socklen_t sinlen; 19 | } sv; 20 | 21 | struct { 22 | int sd; 23 | struct sockaddr_in sin; 24 | socklen_t sinlen; 25 | } cl; 26 | 27 | struct { 28 | void *buf; 29 | uint32_t bufpos; 30 | uint32_t buflen; 31 | void *arg; 32 | uint32_t arglen; 33 | 34 | enum { 35 | TYPE_NONE = 0, 36 | TYPE_BIN, 37 | TYPE_DOL, 38 | TYPE_ELF, 39 | TYPE_GCI, 40 | TYPE_MB, 41 | TYPE_TPL, 42 | TYPE_ZIP, 43 | } type; 44 | } task; 45 | } wiiload_state_t; 46 | 47 | extern wiiload_state_t wiiload; 48 | 49 | typedef struct { 50 | uint32_t magic; 51 | uint16_t version; 52 | uint16_t args_size; 53 | uint32_t deflate_size; 54 | uint32_t inflate_size; 55 | } ATTRIBUTE_PACKED wiiload_header_t; 56 | 57 | typedef struct { 58 | uint32_t text_offset[7]; 59 | uint32_t data_offset[11]; 60 | uint32_t text_address[7]; 61 | uint32_t data_address[11]; 62 | uint32_t text_size[7]; 63 | uint32_t data_size[11]; 64 | uint32_t bss_address; 65 | uint32_t bss_size; 66 | uint32_t entrypoint; 67 | uint32_t padding[7]; 68 | } ATTRIBUTE_PACKED dol_header_t; 69 | 70 | typedef struct { 71 | uint8_t gamecode[4]; 72 | uint8_t company[2]; 73 | uint8_t padding0; 74 | uint8_t banner_format; 75 | uint8_t filename[32]; 76 | uint32_t time; 77 | uint32_t icon_offset; 78 | uint16_t icon_format; 79 | uint16_t icon_speed; 80 | uint8_t permissions; 81 | uint8_t copies; 82 | uint16_t block; 83 | uint16_t length; 84 | uint16_t padding1; 85 | uint32_t comment_offset; 86 | } ATTRIBUTE_PACKED gci_header_t; 87 | 88 | typedef struct { 89 | uint32_t version; 90 | uint32_t count; 91 | uint32_t size; 92 | } ATTRIBUTE_PACKED tpl_header_t; 93 | 94 | void WIILOADInit(void); 95 | void WIILOADLoad(void); 96 | bool WIILOADReadFile(const char *file); 97 | 98 | #endif /* GBI_WIILOAD_H */ 99 | --------------------------------------------------------------------------------