mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-09-01 15:15:51 +00:00
Vendor Teakra
This commit is contained in:
parent
90323164dc
commit
d65617356f
125 changed files with 37732 additions and 4 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -46,9 +46,6 @@
|
|||
[submodule "third_party/miniaudio"]
|
||||
path = third_party/miniaudio
|
||||
url = https://github.com/mackron/miniaudio
|
||||
[submodule "third_party/teakra"]
|
||||
path = third_party/teakra
|
||||
url = https://github.com/wwylele/teakra
|
||||
[submodule "third_party/boost"]
|
||||
path = third_party/boost
|
||||
url = https://github.com/Panda3DS-emu/ext-boost
|
||||
|
|
1
third_party/teakra
vendored
1
third_party/teakra
vendored
|
@ -1 +0,0 @@
|
|||
Subproject commit e34a86799efd65e3c44b915a4d65b3514d34df4f
|
88
third_party/teakra/.clang-format
vendored
Normal file
88
third_party/teakra/.clang-format
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlinesLeft: false
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
ColumnLimit: 100
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
|
||||
IncludeCategories:
|
||||
- Regex: '^\<[^Q][^/.>]*\>'
|
||||
Priority: -2
|
||||
- Regex: '^\<'
|
||||
Priority: -1
|
||||
- Regex: '^\"'
|
||||
Priority: 0
|
||||
IndentCaseLabels: false
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 150
|
||||
PointerAlignment: Left
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Cpp11
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
...
|
33
third_party/teakra/.github/workflows/build.yaml
vendored
Normal file
33
third_party/teakra/.github/workflows/build.yaml
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
# SPDX-FileCopyrightText: 2025 Andrea Pappacoda <andrea@pappacoda.it>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ 'ubuntu-latest', 'windows-latest', 'macos-latest' ]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Configure
|
||||
run: cmake -B build
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build --parallel
|
||||
|
||||
- name: Test
|
||||
working-directory: build
|
||||
run: ctest --verbose
|
39
third_party/teakra/.gitignore
vendored
Normal file
39
third_party/teakra/.gitignore
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Build directory
|
||||
[Bb]uild/
|
||||
cmake-build/
|
||||
doc-build/
|
||||
|
||||
test/
|
||||
|
||||
# Generated source files
|
||||
src/common/scm_rev.cpp
|
||||
|
||||
# Project/editor files
|
||||
*.swp
|
||||
.idea/
|
||||
.vs/
|
||||
.vscode/
|
||||
|
||||
# *nix related
|
||||
# Common convention for backup or temporary files
|
||||
*~
|
||||
|
||||
# Visual Studio CMake settings
|
||||
CMakeSettings.json
|
||||
|
||||
# OSX global filetypes
|
||||
# Created by Finder or Spotlight in directories for various OS functionality (indexing, etc)
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
|
||||
# Windows global filetypes
|
||||
Thumbs.db
|
||||
|
||||
teakra.out
|
||||
teakra.out.*
|
||||
|
||||
# cmake and ctest temporary files
|
||||
/Testing
|
110
third_party/teakra/CMakeLists.txt
vendored
Normal file
110
third_party/teakra/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,110 @@
|
|||
cmake_minimum_required(VERSION 3.14)
|
||||
project(teakra CXX)
|
||||
|
||||
# Determine if we're built as a subproject (using add_subdirectory)
|
||||
# or if this is the master project.
|
||||
set(MASTER_PROJECT OFF)
|
||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
set(MASTER_PROJECT ON)
|
||||
endif()
|
||||
|
||||
# Useful built-in modules
|
||||
include(CMakeDependentOption)
|
||||
include(CTest)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
option(TEAKRA_WARNINGS_AS_ERRORS "Warnings as errors" ${MASTER_PROJECT})
|
||||
option(TEAKRA_BUILD_TOOLS "Build tools" ${MASTER_PROJECT})
|
||||
cmake_dependent_option(TEAKRA_BUILD_UNIT_TESTS "Build unit tests" "${MASTER_PROJECT}" "BUILD_TESTING" OFF)
|
||||
option(TEAKRA_RUN_TESTS "Run Teakra accuracy tests" OFF)
|
||||
|
||||
# Set hard requirements for C++
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
# Warn on CMake API deprecations
|
||||
set(CMAKE_WARN_DEPRECATED ON)
|
||||
|
||||
# Disable in-source builds
|
||||
set(CMAKE_DISABLE_SOURCE_CHANGES ON)
|
||||
set(CMAKE_DISABLE_IN_SOURCE_BUILD ON)
|
||||
if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
|
||||
message(SEND_ERROR "In-source builds are not allowed.")
|
||||
endif()
|
||||
|
||||
# Add the module directory to the list of paths
|
||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules")
|
||||
|
||||
# Compiler flags
|
||||
if (MSVC)
|
||||
set(TEAKRA_CXX_FLAGS
|
||||
/std:c++latest # CMAKE_CXX_STANDARD as no effect on MSVC until CMake 3.10.
|
||||
/W3
|
||||
/permissive- # Stricter C++ standards conformance
|
||||
/MP
|
||||
/Zi
|
||||
/Zo
|
||||
/EHsc
|
||||
/Zc:throwingNew # Assumes new never returns null
|
||||
/Zc:inline # Omits inline functions from object-file output
|
||||
/DNOMINMAX
|
||||
/D_CRT_SECURE_NO_WARNINGS)
|
||||
|
||||
if (TEAKRA_WARNINGS_AS_ERRORS)
|
||||
list(APPEND TEAKRA_CXX_FLAGS
|
||||
/WX)
|
||||
endif()
|
||||
else()
|
||||
set(TEAKRA_CXX_FLAGS
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wcast-qual
|
||||
-pedantic
|
||||
-pedantic-errors
|
||||
-Wfatal-errors
|
||||
-Wno-missing-braces
|
||||
-Wno-unused-parameter)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
list(APPEND TEAKRA_CXX_FLAGS
|
||||
-Wno-error=maybe-uninitialized)
|
||||
endif()
|
||||
|
||||
if (TEAKRA_WARNINGS_AS_ERRORS)
|
||||
list(APPEND TEAKRA_CXX_FLAGS
|
||||
-Werror)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Prefer the -pthread flag on Linux.
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
if (NOT TEAKRA_TEST_ASSETS_DIR)
|
||||
set(TEAKRA_TEST_ASSETS_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
|
||||
# External libraries
|
||||
add_subdirectory(externals)
|
||||
|
||||
# Teakra project files
|
||||
add_subdirectory(src)
|
||||
|
||||
# Teakra tests
|
||||
if (TEAKRA_BUILD_UNIT_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
# Installation
|
||||
install(TARGETS teakra teakra_c EXPORT teakraTargets)
|
||||
install(EXPORT teakraTargets
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/teakra"
|
||||
NAMESPACE "${PROJECT_NAME}::"
|
||||
FILE "teakraConfig.cmake"
|
||||
)
|
||||
|
||||
install(DIRECTORY
|
||||
"include/teakra"
|
||||
TYPE INCLUDE
|
||||
)
|
17
third_party/teakra/CMakeModules/CreateDirectoryGroups.cmake
vendored
Normal file
17
third_party/teakra/CMakeModules/CreateDirectoryGroups.cmake
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
# This function should be passed a name of an existing target. It will automatically generate
|
||||
# file groups following the directory hierarchy, so that the layout of the files in IDEs matches the
|
||||
# one in the filesystem.
|
||||
function(create_target_directory_groups target_name)
|
||||
# Place any files that aren't in the source list in a separate group so that they don't get in
|
||||
# the way.
|
||||
source_group("Other Files" REGULAR_EXPRESSION ".")
|
||||
|
||||
get_target_property(target_sources "${target_name}" SOURCES)
|
||||
|
||||
foreach(file_name IN LISTS target_sources)
|
||||
get_filename_component(dir_name "${file_name}" PATH)
|
||||
# Group names use '\' as a separator even though the entire rest of CMake uses '/'...
|
||||
string(REPLACE "/" "\\" group_name "${dir_name}")
|
||||
source_group("${group_name}" FILES "${file_name}")
|
||||
endforeach()
|
||||
endfunction()
|
21
third_party/teakra/LICENSE
vendored
Normal file
21
third_party/teakra/LICENSE
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018 Weiyi Wang
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
21
third_party/teakra/README.md
vendored
Normal file
21
third_party/teakra/README.md
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Teakra
|
||||
|
||||
[](https://github.com/wwylele/teakra/actions/workflows/build.yaml)
|
||||
[](https://ci.appveyor.com/project/wwylele/teakra/branch/master)
|
||||
|
||||
Emulator, (dis-)assembler, tools and documentation for XpertTeak, the DSP used by DSi/3DS.
|
||||
|
||||
Many thanks to Martin Korth and many other contributers for their help and their [excellent GBATEK doc](http://problemkaputt.de/gbatek.htm#dsixpertteakdsp)!
|
||||
|
||||
## Contents
|
||||
Please refer to README.md in the following directories for their detail.
|
||||
- `src` contains main source code for compiling portable libraries/tools. [Detailed documentation](src/README.md) for the Teak architecture and for peripherals is also here.
|
||||
- `include` contains the header for the emulator and the disassembler libraries.
|
||||
- `dsptester` contains the source code of a 3DS tool that tests processor instructions and registers
|
||||
- `dspmemorytester` contains the source code of another 3DS tool that tests memory read/write, MMIO and DMA.
|
||||
|
||||
## General Information of the XpertTeak
|
||||
|
||||
The XpertTeak DSP consists of a Teak-family architecture processor, and peripheral components including DMA, interrupt controller and audio input/output ports etc. The exact architecture of the processor is still unclear. GBATEK states that the architecture is TeakLite II, the successor of the TeakLite architecture. Their evidence is the TeakLite II disassembler bundled in RealView Developer Suite. However, a Teak family debugger from [here](https://www.lauterbach.com) shows that the "TEAK(-REVA, -REVB, DEV-A0, -RTL2_0)" contains very similar registers and instruction set described in GBATEK, while the "TeakLite-II" contains very different registers and instructions. This shows that the architecture is likely the original Teak, introduced along with TeakLite as a "non-Lite" expansion to it.
|
||||
|
||||
DSi and 3DS both include XpertTeak. However, their uses of XpertTeak are pretty different. Most DSi games don't use it at all. It's used by the "Nintendo DSi Sound" and "Nintendo Zone" system utilities, and by the "Cooking Coach" cartridge (according to GBATEK), where it appears to be intended for audio/video decoding. On the contrary, 3DS games all use XpertTeak for audio decoding and output.
|
32
third_party/teakra/appveyor.yml
vendored
Normal file
32
third_party/teakra/appveyor.yml
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
# shallow clone
|
||||
clone_depth: 5
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
cmake_generator: "Visual Studio 15 2017 Win64"
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
configuration:
|
||||
- Release
|
||||
|
||||
install:
|
||||
- git submodule update --init --recursive
|
||||
|
||||
before_build:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -G "%cmake_generator%" -DTEAKRA_TEST_ASSETS_DIR="%USERPROFILE%\assets" -DTEAKRA_RUN_TESTS=ON
|
||||
- cd ..
|
||||
|
||||
cache:
|
||||
- '%USERPROFILE%\assets'
|
||||
|
||||
build:
|
||||
project: build/teakra.sln
|
||||
parallel: true
|
||||
|
||||
test_script:
|
||||
- cd build && ctest -VV -C Release && cd ..
|
5
third_party/teakra/externals/CMakeLists.txt
vendored
Normal file
5
third_party/teakra/externals/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
if (TEAKRA_BUILD_UNIT_TESTS)
|
||||
add_library(catch INTERFACE)
|
||||
target_include_directories(catch INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/catch>)
|
||||
endif()
|
17965
third_party/teakra/externals/catch/catch.hpp
vendored
Normal file
17965
third_party/teakra/externals/catch/catch.hpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
20
third_party/teakra/hwtest/dspapbptester/.gitignore
vendored
Normal file
20
third_party/teakra/hwtest/dspapbptester/.gitignore
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
*.3dsx
|
||||
*.elf
|
||||
*.cxi
|
||||
*.cia
|
||||
build/
|
||||
hide/
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Mac crap
|
||||
.DS_Store
|
||||
|
171
third_party/teakra/hwtest/dspapbptester/Makefile
vendored
Normal file
171
third_party/teakra/hwtest/dspapbptester/Makefile
vendored
Normal file
|
@ -0,0 +1,171 @@
|
|||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
# ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional)
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
DATA := data
|
||||
INCLUDES := include
|
||||
#ROMFS := romfs
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
|
||||
|
||||
CFLAGS := -g -Wall -O2 -mword-relocations \
|
||||
-fomit-frame-pointer -ffunction-sections \
|
||||
$(ARCH)
|
||||
|
||||
CFLAGS += $(INCLUDE) -DARM11 -D_3DS
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lctru -lm
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(CTRULIB)
|
||||
|
||||
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica)))
|
||||
SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(TARGET).cxi $(TARGET).cia $(TARGET).elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(OUTPUT).3dsx $(OUTPUT).cia
|
||||
|
||||
ifeq ($(strip $(NO_SMDH)),)
|
||||
$(OUTPUT).3dsx : $(OUTPUT).elf $(OUTPUT).smdh
|
||||
else
|
||||
$(OUTPUT).3dsx : $(OUTPUT).elf
|
||||
endif
|
||||
|
||||
$(OUTPUT).cia : $(OUTPUT).cxi
|
||||
@makerom -f cia -o $(OUTPUT).cia -target t -i $(OUTPUT).cxi:0:0
|
||||
@echo built ... $(OUTPUT).cia
|
||||
|
||||
$(OUTPUT).cxi : $(OUTPUT).elf $(OUTPUT).smdh $(TOPDIR)/rsf.rsf $(TOPDIR)/banner.bnr
|
||||
@makerom -o $(OUTPUT).cxi -rsf $(TOPDIR)/rsf.rsf -target t -elf $(OUTPUT).elf -icon $(OUTPUT).smdh -banner $(TOPDIR)/banner.bnr
|
||||
@echo built ... $(OUTPUT).cxi
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# rules for assembling GPU shaders
|
||||
#---------------------------------------------------------------------------------
|
||||
define shader-as
|
||||
$(eval CURBIN := $(patsubst %.shbin.o,%.shbin,$(notdir $@)))
|
||||
picasso -o $(CURBIN) $1
|
||||
bin2s $(CURBIN) | $(AS) -o $@
|
||||
echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h
|
||||
echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h
|
||||
echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h
|
||||
endef
|
||||
|
||||
%.shbin.o : %.v.pica %.g.pica
|
||||
@echo $(notdir $^)
|
||||
@$(call shader-as,$^)
|
||||
|
||||
%.shbin.o : %.v.pica
|
||||
@echo $(notdir $<)
|
||||
@$(call shader-as,$<)
|
||||
|
||||
%.shbin.o : %.shlist
|
||||
@echo $(notdir $<)
|
||||
@$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file)))
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
BIN
third_party/teakra/hwtest/dspapbptester/banner.bnr
vendored
Normal file
BIN
third_party/teakra/hwtest/dspapbptester/banner.bnr
vendored
Normal file
Binary file not shown.
BIN
third_party/teakra/hwtest/dspapbptester/data/cdc.bin
vendored
Normal file
BIN
third_party/teakra/hwtest/dspapbptester/data/cdc.bin
vendored
Normal file
Binary file not shown.
116
third_party/teakra/hwtest/dspapbptester/firm/source
vendored
Normal file
116
third_party/teakra/hwtest/dspapbptester/firm/source
vendored
Normal file
|
@ -0,0 +1,116 @@
|
|||
segment p 0000
|
||||
br 0x0000$0800 always // reset vector
|
||||
reti always
|
||||
data 0000
|
||||
reti always
|
||||
data 0000
|
||||
br 0x0000$5000 always // int0
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
reti always // int1
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
reti always // int2
|
||||
|
||||
segment p 0800 // init
|
||||
mov 0x$f000 sp // set stack pointer
|
||||
mov 0x$0180 mod3 // enable interrupt
|
||||
load 0x0082u8 page // configure ICU
|
||||
mov 0x$4000 r0
|
||||
mov r0 [page:0x000eu8]
|
||||
mov r0 [page:0x0010u8]
|
||||
mov r0 [page:0x0006u8] // enable apbp as int0
|
||||
br 0x0000$1000 always
|
||||
|
||||
segment p 1000 // Main loop
|
||||
load 0x0000u8 page
|
||||
|
||||
mov 0x$80CC r0 // transfer APBP register
|
||||
mov [r0++] r1
|
||||
mov r1 [page:0x0005u8]
|
||||
modr [r0++]
|
||||
mov [r0++] r1
|
||||
mov r1 [page:0x0006u8]
|
||||
modr [r0++]
|
||||
mov [r0++] r1
|
||||
mov r1 [page:0x0007u8]
|
||||
modr [r0++]
|
||||
mov [r0++] r1
|
||||
mov r1 [page:0x0008u8]
|
||||
modr [r0++]
|
||||
mov [r0++] r1
|
||||
mov r1 [page:0x0009u8]
|
||||
modr [r0++]
|
||||
mov [r0++] r1
|
||||
mov r1 [page:0x000au8]
|
||||
modr [r0++]
|
||||
mov [r0++] r1
|
||||
mov r1 [page:0x000bu8]
|
||||
modr [r0++]
|
||||
|
||||
|
||||
mov [page:0x0000u8] b0l // get signal
|
||||
br 0x0000$1000 eq // loop back if no signal
|
||||
|
||||
mov [page:0x0001u8] b0l // get command type
|
||||
|
||||
cmpv 0x$0000 b0l
|
||||
br 0x0000$2000 eq
|
||||
|
||||
cmpv 0x$0001 b0l
|
||||
br 0x0000$3000 eq
|
||||
|
||||
br 0x0000$4000 always
|
||||
|
||||
segment p 2000 // read
|
||||
mov [page:0x0002u8] r0
|
||||
mov [r0] r1
|
||||
mov r1 [page:0x0003u8]
|
||||
br 0x0000$4000 always
|
||||
|
||||
segment p 3000 // write
|
||||
mov [page:0x0002u8] r0
|
||||
mov [page:0x0003u8] r1
|
||||
mov r1 [r0]
|
||||
br 0x0000$4000 always
|
||||
|
||||
segment p 4000 // end
|
||||
clr b0 always
|
||||
mov b0l [page:0x0000u8] // clear signal
|
||||
br 0x0000$1000 always
|
||||
|
||||
segment p 5000 // interrupt handler
|
||||
mov 0x$4000 r5
|
||||
mov 0x$8202 r4
|
||||
mov r5 [r4] // ack
|
||||
mov 0x$0004 r4
|
||||
mov [r4] r5
|
||||
modr [r5++]
|
||||
mov r5 [r4]
|
||||
reti always
|
||||
|
||||
|
||||
|
||||
|
||||
segment d 0000 // signal area
|
||||
data 0000 // 0, Start signal
|
||||
data 0000 // 1, Operation type
|
||||
data 0000 // 2, Address
|
||||
data 0000 // 3, Data
|
||||
data 0000 // 4, interrupt counter
|
||||
data 0000 // 5, 80CC
|
||||
data 0000 // 6, 80CE
|
||||
data 0000 // 7, 80D0
|
||||
data 0000 // 8, 80D2
|
||||
data 0000 // 9, 80D4
|
||||
data 0000 // A, 80D6
|
||||
data 0000 // B, 80D8
|
228
third_party/teakra/hwtest/dspapbptester/rsf.rsf
vendored
Executable file
228
third_party/teakra/hwtest/dspapbptester/rsf.rsf
vendored
Executable file
|
@ -0,0 +1,228 @@
|
|||
BasicInfo:
|
||||
Title : SVRT
|
||||
ProductCode : CTR-P-SVRT
|
||||
Logo : Nintendo # Nintendo / Licensed / Distributed / iQue / iQueForSystem
|
||||
|
||||
RomFs:
|
||||
# Specifies the root path of the read only file system to include in the ROM.
|
||||
#RootPath : romfs
|
||||
|
||||
TitleInfo:
|
||||
Category : Application
|
||||
UniqueId : 0xff557
|
||||
|
||||
Option:
|
||||
UseOnSD : true # true if App is to be installed to SD
|
||||
FreeProductCode : true # Removes limitations on ProductCode
|
||||
MediaFootPadding : false # If true CCI files are created with padding
|
||||
EnableCrypt : false # Enables encryption for NCCH and CIA
|
||||
EnableCompress : true # Compresses where applicable (currently only exefs:/.code)
|
||||
|
||||
AccessControlInfo:
|
||||
CoreVersion : 2
|
||||
|
||||
# Exheader Format Version
|
||||
DescVersion : 2
|
||||
|
||||
# Minimum Required Kernel Version (below is for 4.5.0)
|
||||
ReleaseKernelMajor : "02"
|
||||
ReleaseKernelMinor : "33"
|
||||
|
||||
# ExtData
|
||||
UseExtSaveData : false # enables ExtData
|
||||
#ExtSaveDataId : 0x300 # only set this when the ID is different to the UniqueId
|
||||
|
||||
# FS:USER Archive Access Permissions
|
||||
# Uncomment as required
|
||||
FileSystemAccess:
|
||||
- CategorySystemApplication
|
||||
- CategoryHardwareCheck
|
||||
- CategoryFileSystemTool
|
||||
- Debug
|
||||
#- TwlCardBackup
|
||||
#- TwlNandData
|
||||
#- Boss
|
||||
- DirectSdmc
|
||||
#- Core
|
||||
#- CtrNandRo
|
||||
#- CtrNandRw
|
||||
#- CtrNandRoWrite
|
||||
#- CategorySystemSettings
|
||||
#- CardBoard
|
||||
#- ExportImportIvs
|
||||
#- DirectSdmcWrite
|
||||
#- SwitchCleanup
|
||||
#- SaveDataMove
|
||||
#- Shop
|
||||
#- Shell
|
||||
#- CategoryHomeMenu
|
||||
|
||||
# Process Settings
|
||||
MemoryType : Application # Application/System/Base
|
||||
SystemMode : 64MB # 64MB(Default)/96MB/80MB/72MB/32MB
|
||||
IdealProcessor : 0
|
||||
AffinityMask : 1
|
||||
Priority : 16
|
||||
MaxCpu : 0 # Let system decide
|
||||
HandleTableSize : 0x200
|
||||
DisableDebug : false
|
||||
EnableForceDebug : false
|
||||
CanWriteSharedPage : false
|
||||
CanUsePrivilegedPriority : true
|
||||
CanUseNonAlphabetAndNumber : true
|
||||
PermitMainFunctionArgument : true
|
||||
CanShareDeviceMemory : true
|
||||
RunnableOnSleep : false
|
||||
SpecialMemoryArrange : false
|
||||
|
||||
# New3DS Exclusive Process Settings
|
||||
SystemModeExt : Legacy # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode
|
||||
CpuSpeed : 268MHz # 268MHz(Default)/804MHz
|
||||
EnableL2Cache : false # false(default)/true
|
||||
CanAccessCore2 : false
|
||||
|
||||
# Virtual Address Mappings
|
||||
IORegisterMapping:
|
||||
- 1ed03000-1ed03fff # DSP registers
|
||||
- 1ff00000-1ff7ffff # DSP memory
|
||||
MemoryMapping:
|
||||
- 1f000000-1f5fffff:r # VRAM
|
||||
|
||||
# Accessible SVCs, <Name>:<ID>
|
||||
SystemCallAccess:
|
||||
InvalidateProcessDataCache: 82
|
||||
FlushProcessDataCache: 84
|
||||
ArbitrateAddress: 34
|
||||
Break: 60
|
||||
CancelTimer: 28
|
||||
ClearEvent: 25
|
||||
ClearTimer: 29
|
||||
CloseHandle: 35
|
||||
ConnectToPort: 45
|
||||
ControlMemory: 1
|
||||
CreateAddressArbiter: 33
|
||||
CreateEvent: 23
|
||||
CreateMemoryBlock: 30
|
||||
CreateMutex: 19
|
||||
CreateSemaphore: 21
|
||||
CreateThread: 8
|
||||
CreateTimer: 26
|
||||
DuplicateHandle: 39
|
||||
ExitProcess: 3
|
||||
ExitThread: 9
|
||||
GetCurrentProcessorNumber: 17
|
||||
GetHandleInfo: 41
|
||||
GetProcessId: 53
|
||||
GetProcessIdOfThread: 54
|
||||
GetProcessIdealProcessor: 6
|
||||
GetProcessInfo: 43
|
||||
GetResourceLimit: 56
|
||||
GetResourceLimitCurrentValues: 58
|
||||
GetResourceLimitLimitValues: 57
|
||||
GetSystemInfo: 42
|
||||
GetSystemTick: 40
|
||||
GetThreadContext: 59
|
||||
GetThreadId: 55
|
||||
GetThreadIdealProcessor: 15
|
||||
GetThreadInfo: 44
|
||||
GetThreadPriority: 11
|
||||
MapMemoryBlock: 31
|
||||
OutputDebugString: 61
|
||||
QueryMemory: 2
|
||||
ReleaseMutex: 20
|
||||
ReleaseSemaphore: 22
|
||||
SendSyncRequest1: 46
|
||||
SendSyncRequest2: 47
|
||||
SendSyncRequest3: 48
|
||||
SendSyncRequest4: 49
|
||||
SendSyncRequest: 50
|
||||
SetThreadPriority: 12
|
||||
SetTimer: 27
|
||||
SignalEvent: 24
|
||||
SleepThread: 10
|
||||
UnmapMemoryBlock: 32
|
||||
WaitSynchronization1: 36
|
||||
WaitSynchronizationN: 37
|
||||
Backdoor: 123
|
||||
BindInterrupt: 80
|
||||
|
||||
# Service List
|
||||
# Maximum 34 services (32 if firmware is prior to 9.3.0)
|
||||
ServiceAccessControl:
|
||||
- cfg:u
|
||||
- fs:USER
|
||||
- gsp::Gpu
|
||||
- hid:USER
|
||||
- ndm:u
|
||||
- pxi:dev
|
||||
- APT:U
|
||||
- ac:u
|
||||
- act:u
|
||||
- am:net
|
||||
- boss:U
|
||||
- cam:u
|
||||
- cecd:u
|
||||
- csnd:SND
|
||||
- frd:u
|
||||
- http:C
|
||||
- ir:USER
|
||||
- ir:u
|
||||
- ir:rst
|
||||
- ldr:ro
|
||||
- mic:u
|
||||
- news:u
|
||||
- nfc:u
|
||||
- nim:aoc
|
||||
- nwm::UDS
|
||||
- ptm:u
|
||||
- qtm:u
|
||||
- soc:U
|
||||
- ssl:C
|
||||
- y2r:u
|
||||
- ps:ps
|
||||
|
||||
|
||||
SystemControlInfo:
|
||||
SaveDataSize: 512K
|
||||
RemasterVersion: 0
|
||||
StackSize: 0x40000
|
||||
|
||||
# Modules that run services listed above should be included below
|
||||
# Maximum 48 dependencies
|
||||
# If a module is listed that isn't present on the 3DS, the title will get stuck at the logo (3ds waves)
|
||||
# So act, nfc and qtm are commented for 4.x support. Uncomment if you need these.
|
||||
# <module name>:<module titleid>
|
||||
Dependency:
|
||||
ac: 0x0004013000002402
|
||||
#act: 0x0004013000003802
|
||||
am: 0x0004013000001502
|
||||
boss: 0x0004013000003402
|
||||
camera: 0x0004013000001602
|
||||
cecd: 0x0004013000002602
|
||||
cfg: 0x0004013000001702
|
||||
codec: 0x0004013000001802
|
||||
csnd: 0x0004013000002702
|
||||
dlp: 0x0004013000002802
|
||||
dsp: 0x0004013000001a02
|
||||
friends: 0x0004013000003202
|
||||
gpio: 0x0004013000001b02
|
||||
gsp: 0x0004013000001c02
|
||||
hid: 0x0004013000001d02
|
||||
http: 0x0004013000002902
|
||||
i2c: 0x0004013000001e02
|
||||
ir: 0x0004013000003302
|
||||
mcu: 0x0004013000001f02
|
||||
mic: 0x0004013000002002
|
||||
ndm: 0x0004013000002b02
|
||||
news: 0x0004013000003502
|
||||
#nfc: 0x0004013000004002
|
||||
nim: 0x0004013000002c02
|
||||
nwm: 0x0004013000002d02
|
||||
pdn: 0x0004013000002102
|
||||
ps: 0x0004013000003102
|
||||
ptm: 0x0004013000002202
|
||||
#qtm: 0x0004013020004202
|
||||
ro: 0x0004013000003702
|
||||
socket: 0x0004013000002e02
|
||||
spi: 0x0004013000002302
|
||||
ssl: 0x0004013000002f02
|
28
third_party/teakra/hwtest/dspapbptester/send.py
vendored
Executable file
28
third_party/teakra/hwtest/dspapbptester/send.py
vendored
Executable file
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import socket
|
||||
import struct
|
||||
|
||||
for UDP_IP in ["192.168.1.158", "127.0.0.1"]:
|
||||
UDP_PORT = 8888
|
||||
MESSAGE = struct.pack('<H', 0xD592)
|
||||
|
||||
for i in range(1, len(sys.argv)):
|
||||
inst = sys.argv[i]
|
||||
if inst == "r":
|
||||
MESSAGE += struct.pack('<H', 0)
|
||||
continue
|
||||
elif inst == "w":
|
||||
MESSAGE += struct.pack('<H', 1)
|
||||
continue
|
||||
elif inst == "hr":
|
||||
MESSAGE += struct.pack('<H', 2)
|
||||
continue
|
||||
elif inst == "hw":
|
||||
MESSAGE += struct.pack('<H', 3)
|
||||
continue
|
||||
MESSAGE += struct.pack('<H', int(inst, 16))
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))
|
308
third_party/teakra/hwtest/dspapbptester/source/main.cpp
vendored
Normal file
308
third_party/teakra/hwtest/dspapbptester/source/main.cpp
vendored
Normal file
|
@ -0,0 +1,308 @@
|
|||
#include <atomic>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <3ds.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "cdc_bin.h"
|
||||
|
||||
PrintConsole topScreen, bottomScreen;
|
||||
|
||||
void MoveCursor(unsigned row, unsigned col) {
|
||||
printf("\x1b[%u;%uH", row + 1, col + 1);
|
||||
}
|
||||
|
||||
enum Color {
|
||||
Reset = 0,
|
||||
Black = 30,
|
||||
Red = 31,
|
||||
Green = 32,
|
||||
Yellow = 33,
|
||||
Blue = 34,
|
||||
Magnenta = 35,
|
||||
Cyan = 36,
|
||||
White = 37,
|
||||
};
|
||||
void SetColor(Color color, Color background) {
|
||||
printf("\x1b[%dm\x1b[%dm", (int)color, (int)background + 10);
|
||||
}
|
||||
|
||||
void FlushCache(volatile void* ptr, u32 size) {
|
||||
svcFlushProcessDataCache(CUR_PROCESS_HANDLE, (void*)ptr, size);
|
||||
}
|
||||
|
||||
void InvalidateCache(volatile void* ptr, u32 size) {
|
||||
svcInvalidateProcessDataCache(CUR_PROCESS_HANDLE, (void*)ptr, size);
|
||||
}
|
||||
|
||||
vu16* dspP = (vu16*)0x1FF00000;
|
||||
vu16* dspD = (vu16*)0x1FF40000;
|
||||
|
||||
std::atomic<int> interrupt_counter;
|
||||
|
||||
void PrintAll() {
|
||||
consoleSelect(&topScreen);
|
||||
|
||||
MoveCursor(0, 0);
|
||||
printf("DSP registers:\n");
|
||||
|
||||
InvalidateCache(dspD + 4, 2 * 8);
|
||||
|
||||
printf("SetSem = %04X\n", dspD[5]);
|
||||
printf("MskSem = %04X\n", dspD[6]);
|
||||
printf("AckSem = %04X\n", dspD[7]);
|
||||
printf("GetSem = %04X\n", dspD[8]);
|
||||
printf("IrqMsk = %04X\n", dspD[9]);
|
||||
printf("Status = %04X\n", dspD[10]);
|
||||
printf("?????? = %04X\n", dspD[11]);
|
||||
printf("interrupt = %d\n", dspD[4]);
|
||||
|
||||
printf("\nCPU registers:\n");
|
||||
printf("PCFG = %04X\n", *(vu16*)0x1ed03008);
|
||||
printf("PSTS = %04X\n", *(vu16*)0x1ed0300C);
|
||||
printf("PSEM = %04X\n", *(vu16*)0x1ed03010);
|
||||
printf("PMASK = %04X\n", *(vu16*)0x1ed03014);
|
||||
printf("PCLEAR = %04X\n", *(vu16*)0x1ed03018);
|
||||
printf("SEM = %04X\n", *(vu16*)0x1ed0301C);
|
||||
printf("interrupt = %d\n", (int)interrupt_counter);
|
||||
|
||||
consoleSelect(&bottomScreen);
|
||||
}
|
||||
|
||||
int udp_s;
|
||||
int udp_s_broadcast;
|
||||
|
||||
void UdpInit() {
|
||||
#define SOC_ALIGN 0x1000
|
||||
#define SOC_BUFFERSIZE 0x100000
|
||||
static u32* SOC_buffer;
|
||||
// allocate buffer for SOC service
|
||||
SOC_buffer = (u32*)memalign(SOC_ALIGN, SOC_BUFFERSIZE);
|
||||
if (SOC_buffer == NULL) {
|
||||
printf("memalign: failed to allocate\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Result ret;
|
||||
if ((ret = socInit(SOC_buffer, SOC_BUFFERSIZE)) != 0) {
|
||||
printf("socInit: 0x%08lX\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
sockaddr_in si_me;
|
||||
|
||||
// create a UDP socket
|
||||
if ((udp_s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
|
||||
printf("socket() failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// zero out the structure
|
||||
memset(&si_me, 0, sizeof(si_me));
|
||||
|
||||
si_me.sin_family = AF_INET;
|
||||
si_me.sin_port = htons(8888);
|
||||
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
// bind socket to port
|
||||
if (bind(udp_s, (sockaddr*)&si_me, sizeof(si_me)) == -1) {
|
||||
printf("bind() failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// create a UDP broadcast socket
|
||||
if ((udp_s_broadcast = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
|
||||
printf("socket()(broadcast) failed\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr unsigned BUFLEN = 512;
|
||||
char buf[BUFLEN];
|
||||
|
||||
void Fire() {
|
||||
dspD[0] = 1;
|
||||
FlushCache((void*)dspD, 8);
|
||||
while (true) {
|
||||
InvalidateCache((void*)dspD, 8);
|
||||
if (dspD[0] == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckPackage() {
|
||||
sockaddr_in si_other;
|
||||
socklen_t slen = sizeof(si_other);
|
||||
int recv_len;
|
||||
if ((recv_len = recvfrom(udp_s, buf, BUFLEN, MSG_DONTWAIT, (sockaddr*)&si_other, &slen)) < 4)
|
||||
return;
|
||||
u16 magic;
|
||||
memcpy(&magic, buf, 2);
|
||||
if (magic == 0xD592) {
|
||||
std::vector<u16> command_package((recv_len - 2) / 2);
|
||||
printf("Command received\n");
|
||||
memcpy(command_package.data(), buf + 2, command_package.size() * 2);
|
||||
switch (command_package[0]) {
|
||||
case 0: {
|
||||
if (command_package.size() != 2) {
|
||||
printf("Wrong length for Read\n");
|
||||
break;
|
||||
}
|
||||
u16 addr = command_package[1];
|
||||
printf("Read [%04X] -> ", addr);
|
||||
dspD[1] = 0;
|
||||
dspD[2] = addr;
|
||||
dspD[3] = 0xCCCC;
|
||||
|
||||
Fire();
|
||||
|
||||
printf("%04X\n", dspD[3]);
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
if (command_package.size() != 3) {
|
||||
printf("Wrong length for Write\n");
|
||||
break;
|
||||
}
|
||||
u16 addr = command_package[1];
|
||||
u16 value = command_package[2];
|
||||
printf("Write [%04X] <- %04X", addr, value);
|
||||
dspD[1] = 1;
|
||||
dspD[2] = addr;
|
||||
dspD[3] = value;
|
||||
|
||||
Fire();
|
||||
|
||||
printf(" OK\n");
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if (command_package.size() != 3) {
|
||||
printf("Wrong length for CPU Read\n");
|
||||
break;
|
||||
}
|
||||
u32 addr = command_package[1] | ((u32)command_package[2] << 16);
|
||||
printf("Read CPU [%08lX] -> ", addr);
|
||||
// FlushCache((vu16*)addr, 2);
|
||||
printf("%04X\n", *(vu16*)(addr));
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
if (command_package.size() != 4) {
|
||||
printf("Wrong length for CPU Write\n");
|
||||
break;
|
||||
}
|
||||
u32 addr = command_package[1] | ((u32)command_package[2] << 16);
|
||||
u16 value = command_package[3];
|
||||
printf("Write CPU [%08lX] <- %04X", addr, value);
|
||||
// InvalidateCache((vu16*)addr, 2);
|
||||
*(vu16*)(addr) = value;
|
||||
printf(" OK\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Handle pmHandle;
|
||||
Result pmInit_(void) {
|
||||
Result res = srvGetServiceHandle(&pmHandle, "pm:app");
|
||||
return res;
|
||||
}
|
||||
void pmExit_(void) {
|
||||
svcCloseHandle(pmHandle);
|
||||
}
|
||||
|
||||
Result PM_TerminateTitle(u64 tid, u64 timeout) {
|
||||
Result ret = 0;
|
||||
u32* cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x4, 4, 0);
|
||||
cmdbuf[1] = tid & 0xFFFFFFFF;
|
||||
cmdbuf[2] = tid >> 32;
|
||||
cmdbuf[3] = timeout & 0xffffffff;
|
||||
cmdbuf[4] = (timeout >> 32) & 0xffffffff;
|
||||
|
||||
if (R_FAILED(ret = svcSendSyncRequest(pmHandle)))
|
||||
return ret;
|
||||
|
||||
return (Result)cmdbuf[1];
|
||||
}
|
||||
|
||||
Handle dsp_interrupt;
|
||||
Handle threadA;
|
||||
u32 threadA_stack[0x400];
|
||||
|
||||
void threadA_entry(void*) {
|
||||
while (true) {
|
||||
svcWaitSynchronization(dsp_interrupt, INT64_MAX);
|
||||
++interrupt_counter;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
aptInit();
|
||||
gfxInitDefault();
|
||||
|
||||
consoleInit(GFX_TOP, &topScreen);
|
||||
consoleInit(GFX_BOTTOM, &bottomScreen);
|
||||
|
||||
consoleSelect(&bottomScreen);
|
||||
printf("Hello!\n");
|
||||
|
||||
UdpInit();
|
||||
|
||||
printf("dspInit: %08lX\n", dspInit());
|
||||
bool loaded = false;
|
||||
printf("DSP_LoadComponent: %08lX\n",
|
||||
DSP_LoadComponent(cdc_bin, cdc_bin_size, 0xFF, 0xFF, &loaded));
|
||||
printf("loaded = %d\n", loaded);
|
||||
|
||||
svcSleepThread(1000000000);
|
||||
char hostname[100];
|
||||
gethostname(hostname, 100);
|
||||
printf("IP: %s port: 8888\n", hostname);
|
||||
|
||||
pmInit_();
|
||||
printf("PM_TerminateTitle(DSP): %08lX\n", PM_TerminateTitle(0x0004013000001a02, 0));
|
||||
pmExit_();
|
||||
|
||||
svcCreateEvent(&dsp_interrupt, ResetType::RESET_ONESHOT);
|
||||
interrupt_counter = 0;
|
||||
svcCreateThread(&threadA, threadA_entry, 0x0, threadA_stack + 0x400, 4, 0xFFFFFFFE);
|
||||
printf("BindInterrupt: %08lX\n", svcBindInterrupt(0x4A, dsp_interrupt, 4, 0));
|
||||
|
||||
// Main loop
|
||||
while (aptMainLoop()) {
|
||||
hidScanInput();
|
||||
|
||||
u32 kDown = hidKeysDown();
|
||||
|
||||
if (kDown & KEY_START)
|
||||
break;
|
||||
|
||||
if (kDown & KEY_A)
|
||||
printf("hello\n");
|
||||
|
||||
CheckPackage();
|
||||
|
||||
PrintAll();
|
||||
|
||||
// Flush and swap framebuffers
|
||||
gfxFlushBuffers();
|
||||
gfxSwapBuffers();
|
||||
|
||||
// Wait for VBlank
|
||||
gspWaitForVBlank();
|
||||
}
|
||||
socExit();
|
||||
dspExit();
|
||||
gfxExit();
|
||||
aptExit();
|
||||
return 0;
|
||||
}
|
20
third_party/teakra/hwtest/dspmemorytester/.gitignore
vendored
Normal file
20
third_party/teakra/hwtest/dspmemorytester/.gitignore
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
*.3dsx
|
||||
*.elf
|
||||
*.cxi
|
||||
*.cia
|
||||
build/
|
||||
hide/
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Mac crap
|
||||
.DS_Store
|
||||
|
171
third_party/teakra/hwtest/dspmemorytester/Makefile
vendored
Normal file
171
third_party/teakra/hwtest/dspmemorytester/Makefile
vendored
Normal file
|
@ -0,0 +1,171 @@
|
|||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
# ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional)
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
DATA := data
|
||||
INCLUDES := include
|
||||
#ROMFS := romfs
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
|
||||
|
||||
CFLAGS := -g -Wall -O2 -mword-relocations \
|
||||
-fomit-frame-pointer -ffunction-sections \
|
||||
$(ARCH)
|
||||
|
||||
CFLAGS += $(INCLUDE) -DARM11 -D_3DS
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lctru -lm
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(CTRULIB)
|
||||
|
||||
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica)))
|
||||
SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(TARGET).cxi $(TARGET).cia $(TARGET).elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(OUTPUT).3dsx $(OUTPUT).cia
|
||||
|
||||
ifeq ($(strip $(NO_SMDH)),)
|
||||
$(OUTPUT).3dsx : $(OUTPUT).elf $(OUTPUT).smdh
|
||||
else
|
||||
$(OUTPUT).3dsx : $(OUTPUT).elf
|
||||
endif
|
||||
|
||||
$(OUTPUT).cia : $(OUTPUT).cxi
|
||||
@makerom -f cia -o $(OUTPUT).cia -target t -i $(OUTPUT).cxi:0:0
|
||||
@echo built ... $(OUTPUT).cia
|
||||
|
||||
$(OUTPUT).cxi : $(OUTPUT).elf $(OUTPUT).smdh $(TOPDIR)/rsf.rsf $(TOPDIR)/banner.bnr
|
||||
@makerom -o $(OUTPUT).cxi -rsf $(TOPDIR)/rsf.rsf -target t -elf $(OUTPUT).elf -icon $(OUTPUT).smdh -banner $(TOPDIR)/banner.bnr
|
||||
@echo built ... $(OUTPUT).cxi
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# rules for assembling GPU shaders
|
||||
#---------------------------------------------------------------------------------
|
||||
define shader-as
|
||||
$(eval CURBIN := $(patsubst %.shbin.o,%.shbin,$(notdir $@)))
|
||||
picasso -o $(CURBIN) $1
|
||||
bin2s $(CURBIN) | $(AS) -o $@
|
||||
echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h
|
||||
echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h
|
||||
echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h
|
||||
endef
|
||||
|
||||
%.shbin.o : %.v.pica %.g.pica
|
||||
@echo $(notdir $^)
|
||||
@$(call shader-as,$^)
|
||||
|
||||
%.shbin.o : %.v.pica
|
||||
@echo $(notdir $<)
|
||||
@$(call shader-as,$<)
|
||||
|
||||
%.shbin.o : %.shlist
|
||||
@echo $(notdir $<)
|
||||
@$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file)))
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
BIN
third_party/teakra/hwtest/dspmemorytester/banner.bnr
vendored
Normal file
BIN
third_party/teakra/hwtest/dspmemorytester/banner.bnr
vendored
Normal file
Binary file not shown.
BIN
third_party/teakra/hwtest/dspmemorytester/data/cdc.bin
vendored
Normal file
BIN
third_party/teakra/hwtest/dspmemorytester/data/cdc.bin
vendored
Normal file
Binary file not shown.
64
third_party/teakra/hwtest/dspmemorytester/firm/source
vendored
Normal file
64
third_party/teakra/hwtest/dspmemorytester/firm/source
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
segment p 0000
|
||||
br 0x0000$0800 always// reset vector
|
||||
br 0x0000$3000 always
|
||||
br 0x0000$3000 always
|
||||
br 0x0000$3000 always // int0
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
br 0x0000$3000 always // int1
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
br 0x0000$3000 always // int2
|
||||
|
||||
segment p 0800 // pre-write shutdown sequence
|
||||
load 0x0080u8 page
|
||||
clr b0 always
|
||||
mov b0l [page:0x00c8u8] // T_REPLY2
|
||||
br 0x0000$1000 always // jump to epilogue first to dump the initial register value
|
||||
|
||||
segment p 1000 // Main loop
|
||||
load 0x0000u8 page
|
||||
mov [page:0x0000u8] b0l // get signal
|
||||
br 0x0000$1000 eq // loop back if no signal
|
||||
|
||||
mov [page:0x0001u8] b0l // get command type
|
||||
|
||||
cmpv 0x$0000 b0l
|
||||
br 0x0000$2000 eq
|
||||
|
||||
cmpv 0x$0001 b0l
|
||||
br 0x0000$3000 eq
|
||||
|
||||
br 0x0000$4000 always
|
||||
|
||||
segment p 2000 // read
|
||||
mov [page:0x0002u8] r0
|
||||
mov [r0] r1
|
||||
mov r1 [page:0x0003u8]
|
||||
br 0x0000$4000 always
|
||||
|
||||
segment p 3000 // write
|
||||
mov [page:0x0002u8] r0
|
||||
mov [page:0x0003u8] r1
|
||||
mov r1 [r0]
|
||||
br 0x0000$4000 always
|
||||
|
||||
segment p 4000 // end
|
||||
clr b0 always
|
||||
mov b0l [page:0x0000u8] // clear signal
|
||||
br 0x0000$1000 always // loop back
|
||||
|
||||
|
||||
segment d 0000 // signal area
|
||||
data 0000 // Start signal
|
||||
data 0000 // Operation type
|
||||
data 0000 // Address
|
||||
data 0000 // Data
|
226
third_party/teakra/hwtest/dspmemorytester/rsf.rsf
vendored
Executable file
226
third_party/teakra/hwtest/dspmemorytester/rsf.rsf
vendored
Executable file
|
@ -0,0 +1,226 @@
|
|||
BasicInfo:
|
||||
Title : SVRT
|
||||
ProductCode : CTR-P-SVRT
|
||||
Logo : Nintendo # Nintendo / Licensed / Distributed / iQue / iQueForSystem
|
||||
|
||||
RomFs:
|
||||
# Specifies the root path of the read only file system to include in the ROM.
|
||||
#RootPath : romfs
|
||||
|
||||
TitleInfo:
|
||||
Category : Application
|
||||
UniqueId : 0xff556
|
||||
|
||||
Option:
|
||||
UseOnSD : true # true if App is to be installed to SD
|
||||
FreeProductCode : true # Removes limitations on ProductCode
|
||||
MediaFootPadding : false # If true CCI files are created with padding
|
||||
EnableCrypt : false # Enables encryption for NCCH and CIA
|
||||
EnableCompress : true # Compresses where applicable (currently only exefs:/.code)
|
||||
|
||||
AccessControlInfo:
|
||||
CoreVersion : 2
|
||||
|
||||
# Exheader Format Version
|
||||
DescVersion : 2
|
||||
|
||||
# Minimum Required Kernel Version (below is for 4.5.0)
|
||||
ReleaseKernelMajor : "02"
|
||||
ReleaseKernelMinor : "33"
|
||||
|
||||
# ExtData
|
||||
UseExtSaveData : false # enables ExtData
|
||||
#ExtSaveDataId : 0x300 # only set this when the ID is different to the UniqueId
|
||||
|
||||
# FS:USER Archive Access Permissions
|
||||
# Uncomment as required
|
||||
FileSystemAccess:
|
||||
- CategorySystemApplication
|
||||
- CategoryHardwareCheck
|
||||
- CategoryFileSystemTool
|
||||
- Debug
|
||||
#- TwlCardBackup
|
||||
#- TwlNandData
|
||||
#- Boss
|
||||
- DirectSdmc
|
||||
#- Core
|
||||
#- CtrNandRo
|
||||
#- CtrNandRw
|
||||
#- CtrNandRoWrite
|
||||
#- CategorySystemSettings
|
||||
#- CardBoard
|
||||
#- ExportImportIvs
|
||||
#- DirectSdmcWrite
|
||||
#- SwitchCleanup
|
||||
#- SaveDataMove
|
||||
#- Shop
|
||||
#- Shell
|
||||
#- CategoryHomeMenu
|
||||
|
||||
# Process Settings
|
||||
MemoryType : Application # Application/System/Base
|
||||
SystemMode : 64MB # 64MB(Default)/96MB/80MB/72MB/32MB
|
||||
IdealProcessor : 0
|
||||
AffinityMask : 1
|
||||
Priority : 16
|
||||
MaxCpu : 0 # Let system decide
|
||||
HandleTableSize : 0x200
|
||||
DisableDebug : false
|
||||
EnableForceDebug : false
|
||||
CanWriteSharedPage : false
|
||||
CanUsePrivilegedPriority : false
|
||||
CanUseNonAlphabetAndNumber : true
|
||||
PermitMainFunctionArgument : true
|
||||
CanShareDeviceMemory : true
|
||||
RunnableOnSleep : false
|
||||
SpecialMemoryArrange : false
|
||||
|
||||
# New3DS Exclusive Process Settings
|
||||
SystemModeExt : Legacy # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode
|
||||
CpuSpeed : 268MHz # 268MHz(Default)/804MHz
|
||||
EnableL2Cache : false # false(default)/true
|
||||
CanAccessCore2 : false
|
||||
|
||||
# Virtual Address Mappings
|
||||
IORegisterMapping:
|
||||
- 1ff00000-1ff7ffff # DSP memory
|
||||
MemoryMapping:
|
||||
- 1f000000-1f5fffff:r # VRAM
|
||||
|
||||
# Accessible SVCs, <Name>:<ID>
|
||||
SystemCallAccess:
|
||||
InvalidateProcessDataCache: 82
|
||||
FlushProcessDataCache: 84
|
||||
ArbitrateAddress: 34
|
||||
Break: 60
|
||||
CancelTimer: 28
|
||||
ClearEvent: 25
|
||||
ClearTimer: 29
|
||||
CloseHandle: 35
|
||||
ConnectToPort: 45
|
||||
ControlMemory: 1
|
||||
CreateAddressArbiter: 33
|
||||
CreateEvent: 23
|
||||
CreateMemoryBlock: 30
|
||||
CreateMutex: 19
|
||||
CreateSemaphore: 21
|
||||
CreateThread: 8
|
||||
CreateTimer: 26
|
||||
DuplicateHandle: 39
|
||||
ExitProcess: 3
|
||||
ExitThread: 9
|
||||
GetCurrentProcessorNumber: 17
|
||||
GetHandleInfo: 41
|
||||
GetProcessId: 53
|
||||
GetProcessIdOfThread: 54
|
||||
GetProcessIdealProcessor: 6
|
||||
GetProcessInfo: 43
|
||||
GetResourceLimit: 56
|
||||
GetResourceLimitCurrentValues: 58
|
||||
GetResourceLimitLimitValues: 57
|
||||
GetSystemInfo: 42
|
||||
GetSystemTick: 40
|
||||
GetThreadContext: 59
|
||||
GetThreadId: 55
|
||||
GetThreadIdealProcessor: 15
|
||||
GetThreadInfo: 44
|
||||
GetThreadPriority: 11
|
||||
MapMemoryBlock: 31
|
||||
OutputDebugString: 61
|
||||
QueryMemory: 2
|
||||
ReleaseMutex: 20
|
||||
ReleaseSemaphore: 22
|
||||
SendSyncRequest1: 46
|
||||
SendSyncRequest2: 47
|
||||
SendSyncRequest3: 48
|
||||
SendSyncRequest4: 49
|
||||
SendSyncRequest: 50
|
||||
SetThreadPriority: 12
|
||||
SetTimer: 27
|
||||
SignalEvent: 24
|
||||
SleepThread: 10
|
||||
UnmapMemoryBlock: 32
|
||||
WaitSynchronization1: 36
|
||||
WaitSynchronizationN: 37
|
||||
Backdoor: 123
|
||||
|
||||
# Service List
|
||||
# Maximum 34 services (32 if firmware is prior to 9.3.0)
|
||||
ServiceAccessControl:
|
||||
- cfg:u
|
||||
- fs:USER
|
||||
- gsp::Gpu
|
||||
- hid:USER
|
||||
- ndm:u
|
||||
- pxi:dev
|
||||
- APT:U
|
||||
- ac:u
|
||||
- act:u
|
||||
- am:net
|
||||
- boss:U
|
||||
- cam:u
|
||||
- cecd:u
|
||||
- csnd:SND
|
||||
- frd:u
|
||||
- http:C
|
||||
- ir:USER
|
||||
- ir:u
|
||||
- ir:rst
|
||||
- ldr:ro
|
||||
- mic:u
|
||||
- news:u
|
||||
- nfc:u
|
||||
- nim:aoc
|
||||
- nwm::UDS
|
||||
- ptm:u
|
||||
- qtm:u
|
||||
- soc:U
|
||||
- ssl:C
|
||||
- y2r:u
|
||||
- ps:ps
|
||||
|
||||
|
||||
SystemControlInfo:
|
||||
SaveDataSize: 512K
|
||||
RemasterVersion: 0
|
||||
StackSize: 0x40000
|
||||
|
||||
# Modules that run services listed above should be included below
|
||||
# Maximum 48 dependencies
|
||||
# If a module is listed that isn't present on the 3DS, the title will get stuck at the logo (3ds waves)
|
||||
# So act, nfc and qtm are commented for 4.x support. Uncomment if you need these.
|
||||
# <module name>:<module titleid>
|
||||
Dependency:
|
||||
ac: 0x0004013000002402
|
||||
#act: 0x0004013000003802
|
||||
am: 0x0004013000001502
|
||||
boss: 0x0004013000003402
|
||||
camera: 0x0004013000001602
|
||||
cecd: 0x0004013000002602
|
||||
cfg: 0x0004013000001702
|
||||
codec: 0x0004013000001802
|
||||
csnd: 0x0004013000002702
|
||||
dlp: 0x0004013000002802
|
||||
dsp: 0x0004013000001a02
|
||||
friends: 0x0004013000003202
|
||||
gpio: 0x0004013000001b02
|
||||
gsp: 0x0004013000001c02
|
||||
hid: 0x0004013000001d02
|
||||
http: 0x0004013000002902
|
||||
i2c: 0x0004013000001e02
|
||||
ir: 0x0004013000003302
|
||||
mcu: 0x0004013000001f02
|
||||
mic: 0x0004013000002002
|
||||
ndm: 0x0004013000002b02
|
||||
news: 0x0004013000003502
|
||||
#nfc: 0x0004013000004002
|
||||
nim: 0x0004013000002c02
|
||||
nwm: 0x0004013000002d02
|
||||
pdn: 0x0004013000002102
|
||||
ps: 0x0004013000003102
|
||||
ptm: 0x0004013000002202
|
||||
#qtm: 0x0004013020004202
|
||||
ro: 0x0004013000003702
|
||||
socket: 0x0004013000002e02
|
||||
spi: 0x0004013000002302
|
||||
ssl: 0x0004013000002f02
|
22
third_party/teakra/hwtest/dspmemorytester/send.py
vendored
Executable file
22
third_party/teakra/hwtest/dspmemorytester/send.py
vendored
Executable file
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import socket
|
||||
import struct
|
||||
|
||||
for UDP_IP in ["192.168.1.158", "127.0.0.1"]:
|
||||
UDP_PORT = 8888
|
||||
MESSAGE = struct.pack('<H', 0xD592)
|
||||
|
||||
for i in range(1, len(sys.argv)):
|
||||
inst = sys.argv[i]
|
||||
if inst == "r":
|
||||
MESSAGE += struct.pack('<H', 0)
|
||||
continue
|
||||
elif inst == "w":
|
||||
MESSAGE += struct.pack('<H', 1)
|
||||
continue
|
||||
MESSAGE += struct.pack('<H', int(inst, 16))
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))
|
291
third_party/teakra/hwtest/dspmemorytester/source/main.cpp
vendored
Normal file
291
third_party/teakra/hwtest/dspmemorytester/source/main.cpp
vendored
Normal file
|
@ -0,0 +1,291 @@
|
|||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <3ds.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "cdc_bin.h"
|
||||
|
||||
|
||||
PrintConsole topScreen, bottomScreen;
|
||||
|
||||
void MoveCursor(unsigned row, unsigned col) {
|
||||
printf("\x1b[%u;%uH", row + 1, col + 1);
|
||||
}
|
||||
|
||||
enum Color {
|
||||
Reset = 0,
|
||||
Black = 30,
|
||||
Red = 31,
|
||||
Green = 32,
|
||||
Yellow = 33,
|
||||
Blue = 34,
|
||||
Magnenta = 35,
|
||||
Cyan = 36,
|
||||
White = 37,
|
||||
};
|
||||
void SetColor(Color color, Color background) {
|
||||
printf("\x1b[%dm\x1b[%dm", (int)color, (int)background + 10);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FlushCache(void* ptr, u32 size) {
|
||||
svcFlushProcessDataCache(CUR_PROCESS_HANDLE, ptr, size);
|
||||
}
|
||||
|
||||
void InvalidateCache(void* ptr, u32 size) {
|
||||
svcInvalidateProcessDataCache(CUR_PROCESS_HANDLE, ptr, size);
|
||||
}
|
||||
|
||||
|
||||
vu16* dspP = (vu16*)0x1FF00000;
|
||||
vu16* dspD = (vu16*)0x1FF40000;
|
||||
|
||||
u32 playground_size = 0x80;
|
||||
|
||||
vu8* dsp_playground = (vu8*)(dspD + 0x1000); // note: address = 0x1000 in dsp space
|
||||
vu8* fcram_playground;
|
||||
|
||||
void PrintAll() {
|
||||
consoleSelect(&topScreen);
|
||||
|
||||
MoveCursor(0, 0);
|
||||
printf("DSP @ 0x1000:\n");
|
||||
|
||||
InvalidateCache((void*)dsp_playground, playground_size);
|
||||
for (u32 i = 0; i < playground_size; ++i) {
|
||||
u8 v = dsp_playground[i];
|
||||
if (v != i) {
|
||||
SetColor(Green, Black);
|
||||
}
|
||||
printf("%02X", v);
|
||||
SetColor(Reset, Reset);
|
||||
if (i % 16 == 15) {
|
||||
printf("\n");
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
u32 addr = (u32)fcram_playground;
|
||||
if (addr < 0x1C000000) {
|
||||
addr = addr - 0x14000000 + 0x20000000;
|
||||
} else {
|
||||
addr = addr - 0x30000000 + 0x20000000;
|
||||
}
|
||||
printf("\nFCRAM @ 0x%08lX:\n", addr);
|
||||
|
||||
InvalidateCache((void*)fcram_playground, playground_size);
|
||||
for (u32 i = 0; i < playground_size; ++i) {
|
||||
u8 v = fcram_playground[i];
|
||||
if (v != (i | 0x80)) {
|
||||
SetColor(Green, Black);
|
||||
}
|
||||
printf("%02X", v);
|
||||
SetColor(Reset, Reset);
|
||||
if (i % 16 == 15) {
|
||||
printf("\n");
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
consoleSelect(&bottomScreen);
|
||||
}
|
||||
|
||||
void ResetDspPlayground() {
|
||||
InvalidateCache((void*)dsp_playground, playground_size);
|
||||
for (u32 i = 0; i < playground_size; ++i) {
|
||||
dsp_playground[i] = i;
|
||||
}
|
||||
FlushCache((void*)dsp_playground, playground_size);
|
||||
}
|
||||
|
||||
void ResetFcramPlayground() {
|
||||
InvalidateCache((void*)fcram_playground, playground_size);
|
||||
for (u32 i = 0; i < playground_size; ++i) {
|
||||
fcram_playground[i] = i | 0x80;
|
||||
}
|
||||
FlushCache((void*)fcram_playground, playground_size);
|
||||
}
|
||||
|
||||
int udp_s;
|
||||
int udp_s_broadcast;
|
||||
|
||||
void UdpInit() {
|
||||
#define SOC_ALIGN 0x1000
|
||||
#define SOC_BUFFERSIZE 0x100000
|
||||
static u32* SOC_buffer;
|
||||
// allocate buffer for SOC service
|
||||
SOC_buffer = (u32*)memalign(SOC_ALIGN, SOC_BUFFERSIZE);
|
||||
if (SOC_buffer == NULL) {
|
||||
printf("memalign: failed to allocate\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Result ret;
|
||||
if ((ret = socInit(SOC_buffer, SOC_BUFFERSIZE)) != 0) {
|
||||
printf("socInit: 0x%08lX\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
sockaddr_in si_me;
|
||||
|
||||
// create a UDP socket
|
||||
if ((udp_s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
|
||||
printf("socket() failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// zero out the structure
|
||||
memset(&si_me, 0, sizeof(si_me));
|
||||
|
||||
si_me.sin_family = AF_INET;
|
||||
si_me.sin_port = htons(8888);
|
||||
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
// bind socket to port
|
||||
if (bind(udp_s, (sockaddr*)&si_me, sizeof(si_me)) == -1) {
|
||||
printf("bind() failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// create a UDP broadcast socket
|
||||
if ((udp_s_broadcast = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
|
||||
printf("socket()(broadcast) failed\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr unsigned BUFLEN = 512;
|
||||
char buf[BUFLEN];
|
||||
|
||||
void Fire() {
|
||||
dspD[0] = 1;
|
||||
FlushCache((void*)dspD, 8);
|
||||
while(true) {
|
||||
InvalidateCache((void*)dspD, 8);
|
||||
if (dspD[0] == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckPackage() {
|
||||
sockaddr_in si_other;
|
||||
socklen_t slen = sizeof(si_other);
|
||||
int recv_len;
|
||||
if ((recv_len = recvfrom(udp_s, buf, BUFLEN, MSG_DONTWAIT, (sockaddr*)&si_other, &slen)) < 4)
|
||||
return;
|
||||
u16 magic;
|
||||
memcpy(&magic, buf, 2);
|
||||
if (magic == 0xD592) {
|
||||
std::vector<u16> command_package((recv_len - 2) / 2);
|
||||
printf("Command received\n");
|
||||
memcpy(command_package.data(), buf + 2, command_package.size() * 2);
|
||||
switch (command_package[0]) {
|
||||
case 0: {
|
||||
if (command_package.size() != 2) {
|
||||
printf("Wrong length for Read\n");
|
||||
break;
|
||||
}
|
||||
u16 addr = command_package[1];
|
||||
printf("Read [%04X] -> ", addr);
|
||||
dspD[1] = 0;
|
||||
dspD[2] = addr;
|
||||
dspD[3] = 0xCCCC;
|
||||
|
||||
Fire();
|
||||
|
||||
printf("%04X\n", dspD[3]);
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
if (command_package.size() != 3) {
|
||||
printf("Wrong length for Write\n");
|
||||
break;
|
||||
}
|
||||
u16 addr = command_package[1];
|
||||
u16 value = command_package[2];
|
||||
printf("Write [%04X] <- %04X", addr, value);
|
||||
dspD[1] = 1;
|
||||
dspD[2] = addr;
|
||||
dspD[3] = value;
|
||||
|
||||
Fire();
|
||||
|
||||
printf(" OK\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
fcram_playground = (vu8*)linearAlloc(playground_size);
|
||||
ResetDspPlayground();
|
||||
ResetFcramPlayground();
|
||||
aptInit();
|
||||
gfxInitDefault();
|
||||
|
||||
consoleInit(GFX_TOP, &topScreen);
|
||||
consoleInit(GFX_BOTTOM, &bottomScreen);
|
||||
|
||||
consoleSelect(&bottomScreen);
|
||||
printf("Hello!\n");
|
||||
|
||||
UdpInit();
|
||||
|
||||
printf("dspInit: %08lX\n", dspInit());
|
||||
bool loaded = false;
|
||||
printf("DSP_LoadComponent: %08lX\n",
|
||||
DSP_LoadComponent(cdc_bin, cdc_bin_size, 0xFF, 0xFF, &loaded));
|
||||
printf("loaded = %d\n", loaded);
|
||||
|
||||
svcSleepThread(1000000000);
|
||||
char hostname[100];
|
||||
gethostname(hostname, 100);
|
||||
printf("IP: %s port: 8888\n", hostname);
|
||||
|
||||
// Main loop
|
||||
while (aptMainLoop()) {
|
||||
hidScanInput();
|
||||
|
||||
u32 kDown = hidKeysDown();
|
||||
|
||||
if (kDown & KEY_START)
|
||||
break;
|
||||
|
||||
if (kDown & KEY_A) {
|
||||
ResetDspPlayground();
|
||||
printf("Reset DSP playground\n");
|
||||
}
|
||||
|
||||
if (kDown & KEY_B) {
|
||||
ResetFcramPlayground();
|
||||
printf("Reset FCRAM playground\n");
|
||||
}
|
||||
|
||||
CheckPackage();
|
||||
|
||||
PrintAll();
|
||||
|
||||
// Flush and swap framebuffers
|
||||
gfxFlushBuffers();
|
||||
gfxSwapBuffers();
|
||||
|
||||
// Wait for VBlank
|
||||
gspWaitForVBlank();
|
||||
}
|
||||
socExit();
|
||||
dspExit();
|
||||
gfxExit();
|
||||
aptExit();
|
||||
return 0;
|
||||
}
|
20
third_party/teakra/hwtest/dsptester/.gitignore
vendored
Normal file
20
third_party/teakra/hwtest/dsptester/.gitignore
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
*.3dsx
|
||||
*.elf
|
||||
*.cxi
|
||||
*.cia
|
||||
build/
|
||||
hide/
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Mac crap
|
||||
.DS_Store
|
||||
|
171
third_party/teakra/hwtest/dsptester/Makefile
vendored
Normal file
171
third_party/teakra/hwtest/dsptester/Makefile
vendored
Normal file
|
@ -0,0 +1,171 @@
|
|||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
# ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional)
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
DATA := data
|
||||
INCLUDES := include
|
||||
#ROMFS := romfs
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
|
||||
|
||||
CFLAGS := -g -Wall -O2 -mword-relocations \
|
||||
-fomit-frame-pointer -ffunction-sections \
|
||||
$(ARCH)
|
||||
|
||||
CFLAGS += $(INCLUDE) -DARM11 -D_3DS
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lctru -lm
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(CTRULIB)
|
||||
|
||||
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica)))
|
||||
SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(TARGET).cxi $(TARGET).cia $(TARGET).elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(OUTPUT).3dsx $(OUTPUT).cia
|
||||
|
||||
ifeq ($(strip $(NO_SMDH)),)
|
||||
$(OUTPUT).3dsx : $(OUTPUT).elf $(OUTPUT).smdh
|
||||
else
|
||||
$(OUTPUT).3dsx : $(OUTPUT).elf
|
||||
endif
|
||||
|
||||
$(OUTPUT).cia : $(OUTPUT).cxi
|
||||
@makerom -f cia -o $(OUTPUT).cia -target t -i $(OUTPUT).cxi:0:0
|
||||
@echo built ... $(OUTPUT).cia
|
||||
|
||||
$(OUTPUT).cxi : $(OUTPUT).elf $(OUTPUT).smdh $(TOPDIR)/rsf.rsf $(TOPDIR)/banner.bnr
|
||||
@makerom -o $(OUTPUT).cxi -rsf $(TOPDIR)/rsf.rsf -target t -elf $(OUTPUT).elf -icon $(OUTPUT).smdh -banner $(TOPDIR)/banner.bnr
|
||||
@echo built ... $(OUTPUT).cxi
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# rules for assembling GPU shaders
|
||||
#---------------------------------------------------------------------------------
|
||||
define shader-as
|
||||
$(eval CURBIN := $(patsubst %.shbin.o,%.shbin,$(notdir $@)))
|
||||
picasso -o $(CURBIN) $1
|
||||
bin2s $(CURBIN) | $(AS) -o $@
|
||||
echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h
|
||||
echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h
|
||||
echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h
|
||||
endef
|
||||
|
||||
%.shbin.o : %.v.pica %.g.pica
|
||||
@echo $(notdir $^)
|
||||
@$(call shader-as,$^)
|
||||
|
||||
%.shbin.o : %.v.pica
|
||||
@echo $(notdir $<)
|
||||
@$(call shader-as,$<)
|
||||
|
||||
%.shbin.o : %.shlist
|
||||
@echo $(notdir $<)
|
||||
@$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file)))
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
BIN
third_party/teakra/hwtest/dsptester/banner.bnr
vendored
Normal file
BIN
third_party/teakra/hwtest/dsptester/banner.bnr
vendored
Normal file
Binary file not shown.
BIN
third_party/teakra/hwtest/dsptester/data/cdc.bin
vendored
Normal file
BIN
third_party/teakra/hwtest/dsptester/data/cdc.bin
vendored
Normal file
Binary file not shown.
171
third_party/teakra/hwtest/dsptester/firm/source
vendored
Normal file
171
third_party/teakra/hwtest/dsptester/firm/source
vendored
Normal file
|
@ -0,0 +1,171 @@
|
|||
segment p 0000
|
||||
br 0x0000$0800 always// reset vector
|
||||
br 0x0000$3000 always
|
||||
br 0x0000$3000 always
|
||||
br 0x0000$3000 always // int0
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
br 0x0000$3000 always // int1
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
br 0x0000$3000 always // int2
|
||||
|
||||
segment p 0800 // pre-write shutdown sequence
|
||||
load 0x0080u8 page
|
||||
clr b0 always
|
||||
mov b0l [page:0x00c8u8] // T_REPLY2
|
||||
br 0x0000$1800 always // jump to epilogue first to dump the initial register value
|
||||
|
||||
segment p 1000 // signal handler & prologue
|
||||
load 0x0000u8 page
|
||||
|
||||
mov [page:0x0002u8] b0l
|
||||
brr 0x0003 eq
|
||||
clr b0 always
|
||||
mov b0l [page:0x0000u8]
|
||||
mov b0l [page:0x0002u8]
|
||||
|
||||
mov [page:0x0001u8] b0l
|
||||
brr 0x0003 eq
|
||||
mov b0l [page:0x0000u8]
|
||||
clr b0 always
|
||||
mov b0l [page:0x0001u8]
|
||||
|
||||
mov [page:0x0000u8] b0l
|
||||
br 0x0000$1000 eq
|
||||
|
||||
mov 0x$2000 sp
|
||||
|
||||
mov 0x$0003 mod0 // disable SAR and PS so a/b/p can be correctly loaded
|
||||
|
||||
mov 0x$0000 mod3 // clear mod3 from E000 to 0000. Game does this at the beginning so let it be the default env
|
||||
|
||||
pop r7
|
||||
pop r6
|
||||
pop r5
|
||||
pop r4
|
||||
pop r3
|
||||
pop r2
|
||||
pop r1
|
||||
pop r0
|
||||
pop a0l
|
||||
mov a0l mixp
|
||||
pop repc
|
||||
pop a0h
|
||||
mov a0h stepj0
|
||||
pop a0h
|
||||
mov a0h stepi0
|
||||
pop lc
|
||||
pop p1
|
||||
pop y1
|
||||
pop x1
|
||||
pop p0
|
||||
pop y0
|
||||
pop x0
|
||||
pop sv
|
||||
popa b1
|
||||
pop b1e
|
||||
popa b0
|
||||
pop b0e
|
||||
popa a1
|
||||
pop a1e
|
||||
popa a0
|
||||
pop a0e
|
||||
pop arp3
|
||||
pop arp2
|
||||
pop arp1
|
||||
pop arp0
|
||||
pop ar1
|
||||
pop ar0
|
||||
pop mod2
|
||||
pop mod1
|
||||
pop mod0
|
||||
pop stt2
|
||||
pop stt1
|
||||
pop stt0
|
||||
pop cfgj
|
||||
pop cfgi
|
||||
|
||||
mov 0x$1000 sp
|
||||
br 0x0000$2000 always
|
||||
|
||||
segment p 1800 // epilogue
|
||||
mov 0x$3000 sp
|
||||
|
||||
push cfgi
|
||||
push cfgj
|
||||
push stt0
|
||||
push stt1
|
||||
push stt2
|
||||
push mod0
|
||||
push mod1
|
||||
push mod2
|
||||
push ar0
|
||||
push ar1
|
||||
push arp0
|
||||
push arp1
|
||||
push arp2
|
||||
push arp3
|
||||
|
||||
mov 0x$0003 mod0 // disable SAR and PS so a/b/p can be correctly loaded
|
||||
|
||||
push a0e
|
||||
pusha a0
|
||||
push a1e
|
||||
pusha a1
|
||||
push b0e
|
||||
pusha b0
|
||||
push b1e
|
||||
pusha b1
|
||||
push sv
|
||||
push x0
|
||||
push y0
|
||||
push p0
|
||||
push x1
|
||||
push y1
|
||||
push p1
|
||||
push lc
|
||||
mov stepi0 a0h
|
||||
push a0h
|
||||
mov stepj0 a0h
|
||||
push a0h
|
||||
push repc
|
||||
mov mixp a0l
|
||||
push a0l
|
||||
push r0
|
||||
push r1
|
||||
push r2
|
||||
push r3
|
||||
push r4
|
||||
push r5
|
||||
push r6
|
||||
push r7
|
||||
|
||||
load 0x0000u8 page
|
||||
mov [page:0x0003u8] b0l
|
||||
brr 0x0003 eq
|
||||
clr b0 always
|
||||
mov b0l [page:0x0000u8]
|
||||
mov b0l [page:0x0003u8]
|
||||
|
||||
br 0x0000$1000 always
|
||||
|
||||
segment p 2000 // test area
|
||||
br 0x0000$1800 always
|
||||
|
||||
segment p 3000 // interrupt
|
||||
reti always
|
||||
|
||||
segment d 0000 // signal area
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
0
third_party/teakra/hwtest/dsptester/result-parse
vendored
Normal file
0
third_party/teakra/hwtest/dsptester/result-parse
vendored
Normal file
226
third_party/teakra/hwtest/dsptester/rsf.rsf
vendored
Executable file
226
third_party/teakra/hwtest/dsptester/rsf.rsf
vendored
Executable file
|
@ -0,0 +1,226 @@
|
|||
BasicInfo:
|
||||
Title : SVRS
|
||||
ProductCode : CTR-P-SVRS
|
||||
Logo : Nintendo # Nintendo / Licensed / Distributed / iQue / iQueForSystem
|
||||
|
||||
RomFs:
|
||||
# Specifies the root path of the read only file system to include in the ROM.
|
||||
#RootPath : romfs
|
||||
|
||||
TitleInfo:
|
||||
Category : Application
|
||||
UniqueId : 0xff555
|
||||
|
||||
Option:
|
||||
UseOnSD : true # true if App is to be installed to SD
|
||||
FreeProductCode : true # Removes limitations on ProductCode
|
||||
MediaFootPadding : false # If true CCI files are created with padding
|
||||
EnableCrypt : false # Enables encryption for NCCH and CIA
|
||||
EnableCompress : true # Compresses where applicable (currently only exefs:/.code)
|
||||
|
||||
AccessControlInfo:
|
||||
CoreVersion : 2
|
||||
|
||||
# Exheader Format Version
|
||||
DescVersion : 2
|
||||
|
||||
# Minimum Required Kernel Version (below is for 4.5.0)
|
||||
ReleaseKernelMajor : "02"
|
||||
ReleaseKernelMinor : "33"
|
||||
|
||||
# ExtData
|
||||
UseExtSaveData : false # enables ExtData
|
||||
#ExtSaveDataId : 0x300 # only set this when the ID is different to the UniqueId
|
||||
|
||||
# FS:USER Archive Access Permissions
|
||||
# Uncomment as required
|
||||
FileSystemAccess:
|
||||
- CategorySystemApplication
|
||||
- CategoryHardwareCheck
|
||||
- CategoryFileSystemTool
|
||||
- Debug
|
||||
#- TwlCardBackup
|
||||
#- TwlNandData
|
||||
#- Boss
|
||||
- DirectSdmc
|
||||
#- Core
|
||||
#- CtrNandRo
|
||||
#- CtrNandRw
|
||||
#- CtrNandRoWrite
|
||||
#- CategorySystemSettings
|
||||
#- CardBoard
|
||||
#- ExportImportIvs
|
||||
#- DirectSdmcWrite
|
||||
#- SwitchCleanup
|
||||
#- SaveDataMove
|
||||
#- Shop
|
||||
#- Shell
|
||||
#- CategoryHomeMenu
|
||||
|
||||
# Process Settings
|
||||
MemoryType : Application # Application/System/Base
|
||||
SystemMode : 64MB # 64MB(Default)/96MB/80MB/72MB/32MB
|
||||
IdealProcessor : 0
|
||||
AffinityMask : 1
|
||||
Priority : 16
|
||||
MaxCpu : 0 # Let system decide
|
||||
HandleTableSize : 0x200
|
||||
DisableDebug : false
|
||||
EnableForceDebug : false
|
||||
CanWriteSharedPage : false
|
||||
CanUsePrivilegedPriority : false
|
||||
CanUseNonAlphabetAndNumber : true
|
||||
PermitMainFunctionArgument : true
|
||||
CanShareDeviceMemory : true
|
||||
RunnableOnSleep : false
|
||||
SpecialMemoryArrange : false
|
||||
|
||||
# New3DS Exclusive Process Settings
|
||||
SystemModeExt : Legacy # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode
|
||||
CpuSpeed : 268MHz # 268MHz(Default)/804MHz
|
||||
EnableL2Cache : false # false(default)/true
|
||||
CanAccessCore2 : false
|
||||
|
||||
# Virtual Address Mappings
|
||||
IORegisterMapping:
|
||||
- 1ff00000-1ff7ffff # DSP memory
|
||||
MemoryMapping:
|
||||
- 1f000000-1f5fffff:r # VRAM
|
||||
|
||||
# Accessible SVCs, <Name>:<ID>
|
||||
SystemCallAccess:
|
||||
InvalidateProcessDataCache: 82
|
||||
FlushProcessDataCache: 84
|
||||
ArbitrateAddress: 34
|
||||
Break: 60
|
||||
CancelTimer: 28
|
||||
ClearEvent: 25
|
||||
ClearTimer: 29
|
||||
CloseHandle: 35
|
||||
ConnectToPort: 45
|
||||
ControlMemory: 1
|
||||
CreateAddressArbiter: 33
|
||||
CreateEvent: 23
|
||||
CreateMemoryBlock: 30
|
||||
CreateMutex: 19
|
||||
CreateSemaphore: 21
|
||||
CreateThread: 8
|
||||
CreateTimer: 26
|
||||
DuplicateHandle: 39
|
||||
ExitProcess: 3
|
||||
ExitThread: 9
|
||||
GetCurrentProcessorNumber: 17
|
||||
GetHandleInfo: 41
|
||||
GetProcessId: 53
|
||||
GetProcessIdOfThread: 54
|
||||
GetProcessIdealProcessor: 6
|
||||
GetProcessInfo: 43
|
||||
GetResourceLimit: 56
|
||||
GetResourceLimitCurrentValues: 58
|
||||
GetResourceLimitLimitValues: 57
|
||||
GetSystemInfo: 42
|
||||
GetSystemTick: 40
|
||||
GetThreadContext: 59
|
||||
GetThreadId: 55
|
||||
GetThreadIdealProcessor: 15
|
||||
GetThreadInfo: 44
|
||||
GetThreadPriority: 11
|
||||
MapMemoryBlock: 31
|
||||
OutputDebugString: 61
|
||||
QueryMemory: 2
|
||||
ReleaseMutex: 20
|
||||
ReleaseSemaphore: 22
|
||||
SendSyncRequest1: 46
|
||||
SendSyncRequest2: 47
|
||||
SendSyncRequest3: 48
|
||||
SendSyncRequest4: 49
|
||||
SendSyncRequest: 50
|
||||
SetThreadPriority: 12
|
||||
SetTimer: 27
|
||||
SignalEvent: 24
|
||||
SleepThread: 10
|
||||
UnmapMemoryBlock: 32
|
||||
WaitSynchronization1: 36
|
||||
WaitSynchronizationN: 37
|
||||
Backdoor: 123
|
||||
|
||||
# Service List
|
||||
# Maximum 34 services (32 if firmware is prior to 9.3.0)
|
||||
ServiceAccessControl:
|
||||
- cfg:u
|
||||
- fs:USER
|
||||
- gsp::Gpu
|
||||
- hid:USER
|
||||
- ndm:u
|
||||
- pxi:dev
|
||||
- APT:U
|
||||
- ac:u
|
||||
- act:u
|
||||
- am:net
|
||||
- boss:U
|
||||
- cam:u
|
||||
- cecd:u
|
||||
- csnd:SND
|
||||
- frd:u
|
||||
- http:C
|
||||
- ir:USER
|
||||
- ir:u
|
||||
- ir:rst
|
||||
- ldr:ro
|
||||
- mic:u
|
||||
- news:u
|
||||
- nfc:u
|
||||
- nim:aoc
|
||||
- nwm::UDS
|
||||
- ptm:u
|
||||
- qtm:u
|
||||
- soc:U
|
||||
- ssl:C
|
||||
- y2r:u
|
||||
- ps:ps
|
||||
|
||||
|
||||
SystemControlInfo:
|
||||
SaveDataSize: 512K
|
||||
RemasterVersion: 0
|
||||
StackSize: 0x40000
|
||||
|
||||
# Modules that run services listed above should be included below
|
||||
# Maximum 48 dependencies
|
||||
# If a module is listed that isn't present on the 3DS, the title will get stuck at the logo (3ds waves)
|
||||
# So act, nfc and qtm are commented for 4.x support. Uncomment if you need these.
|
||||
# <module name>:<module titleid>
|
||||
Dependency:
|
||||
ac: 0x0004013000002402
|
||||
#act: 0x0004013000003802
|
||||
am: 0x0004013000001502
|
||||
boss: 0x0004013000003402
|
||||
camera: 0x0004013000001602
|
||||
cecd: 0x0004013000002602
|
||||
cfg: 0x0004013000001702
|
||||
codec: 0x0004013000001802
|
||||
csnd: 0x0004013000002702
|
||||
dlp: 0x0004013000002802
|
||||
dsp: 0x0004013000001a02
|
||||
friends: 0x0004013000003202
|
||||
gpio: 0x0004013000001b02
|
||||
gsp: 0x0004013000001c02
|
||||
hid: 0x0004013000001d02
|
||||
http: 0x0004013000002902
|
||||
i2c: 0x0004013000001e02
|
||||
ir: 0x0004013000003302
|
||||
mcu: 0x0004013000001f02
|
||||
mic: 0x0004013000002002
|
||||
ndm: 0x0004013000002b02
|
||||
news: 0x0004013000003502
|
||||
#nfc: 0x0004013000004002
|
||||
nim: 0x0004013000002c02
|
||||
nwm: 0x0004013000002d02
|
||||
pdn: 0x0004013000002102
|
||||
ps: 0x0004013000003102
|
||||
ptm: 0x0004013000002202
|
||||
#qtm: 0x0004013020004202
|
||||
ro: 0x0004013000003702
|
||||
socket: 0x0004013000002e02
|
||||
spi: 0x0004013000002302
|
||||
ssl: 0x0004013000002f02
|
24
third_party/teakra/hwtest/dsptester/send-program.py
vendored
Executable file
24
third_party/teakra/hwtest/dsptester/send-program.py
vendored
Executable file
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import socket
|
||||
import struct
|
||||
|
||||
for UDP_IP in ["192.168.1.158", "127.0.0.1"]:
|
||||
UDP_PORT = 8888
|
||||
MESSAGE = struct.pack('<H', 0xD590)
|
||||
|
||||
for i in range(1, len(sys.argv)):
|
||||
if sys.argv[i] == '-1':
|
||||
MESSAGE = struct.pack('<H', 0xD591)
|
||||
continue
|
||||
inst = sys.argv[i]
|
||||
splitted = inst.split('+')
|
||||
code = int(splitted[0], 16)
|
||||
for param in splitted[1:]:
|
||||
v, p = param.split('@')
|
||||
code += int(v) << int(p)
|
||||
MESSAGE += struct.pack('<H', code)
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))
|
665
third_party/teakra/hwtest/dsptester/source/main.cpp
vendored
Normal file
665
third_party/teakra/hwtest/dsptester/source/main.cpp
vendored
Normal file
|
@ -0,0 +1,665 @@
|
|||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <3ds.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "cdc_bin.h"
|
||||
|
||||
#define COMMON_TYPE_3DS
|
||||
#include "../../src/test.h"
|
||||
|
||||
PrintConsole topScreen, bottomScreen;
|
||||
|
||||
void MoveCursor(unsigned row, unsigned col) {
|
||||
printf("\x1b[%u;%uH", row + 1, col + 1);
|
||||
}
|
||||
|
||||
enum Color {
|
||||
Reset = 0,
|
||||
Black = 30,
|
||||
Red = 31,
|
||||
Green = 32,
|
||||
Yellow = 33,
|
||||
Blue = 34,
|
||||
Magnenta = 35,
|
||||
Cyan = 36,
|
||||
White = 37,
|
||||
};
|
||||
void SetColor(Color color, Color background) {
|
||||
printf("\x1b[%dm\x1b[%dm", (int)color, (int)background + 10);
|
||||
}
|
||||
|
||||
class IReg {
|
||||
public:
|
||||
IReg(const std::string& name, const std::string& flags) : name(name), flags(flags) {}
|
||||
virtual ~IReg() = default;
|
||||
virtual unsigned GetLength() = 0;
|
||||
virtual unsigned GetSrcDigit(unsigned pos) = 0;
|
||||
virtual unsigned GetDstDigit(unsigned pos) = 0;
|
||||
virtual void SetSrcDigit(unsigned pos, unsigned value) = 0;
|
||||
virtual unsigned GetDigitRange() = 0;
|
||||
void Print(unsigned row, unsigned col, unsigned selected) {
|
||||
SetColor(Cyan, Black);
|
||||
MoveCursor(row, col - name.size());
|
||||
printf("%s", name.c_str());
|
||||
unsigned len = GetLength();
|
||||
for (unsigned i = 0; i < len; ++i) {
|
||||
unsigned src = GetSrcDigit(len - i - 1);
|
||||
unsigned dst = GetDstDigit(len - i - 1);
|
||||
MoveCursor(row, col + i);
|
||||
if (len - i - 1 == selected)
|
||||
SetColor(Black, White);
|
||||
else
|
||||
SetColor(White, Black);
|
||||
if (flags.empty())
|
||||
printf("%X", src);
|
||||
else
|
||||
printf("%c", src ? flags[i] : '-');
|
||||
MoveCursor(row + 1, col + i);
|
||||
SetColor(src == dst ? Magnenta : Green, Black);
|
||||
if (flags.empty())
|
||||
printf("%X", dst);
|
||||
else
|
||||
printf("%c", dst ? flags[i] : '-');
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name, flags;
|
||||
};
|
||||
|
||||
class HexReg : public IReg {
|
||||
public:
|
||||
HexReg(const std::string& name, u16& src, u16& dst) : IReg(name, ""), src(src), dst(dst) {
|
||||
src = dst;
|
||||
}
|
||||
unsigned GetLength() override {
|
||||
return 4;
|
||||
}
|
||||
unsigned GetSrcDigit(unsigned pos) override {
|
||||
return (src >> (pos * 4)) & 0xF;
|
||||
}
|
||||
unsigned GetDstDigit(unsigned pos) override {
|
||||
return (dst >> (pos * 4)) & 0xF;
|
||||
}
|
||||
void SetSrcDigit(unsigned pos, unsigned value) override {
|
||||
src &= ~(0xF << (pos * 4));
|
||||
src |= value << (pos * 4);
|
||||
}
|
||||
unsigned GetDigitRange() override {
|
||||
return 16;
|
||||
}
|
||||
|
||||
private:
|
||||
u16& src;
|
||||
u16& dst;
|
||||
};
|
||||
|
||||
class BinReg : public IReg {
|
||||
public:
|
||||
BinReg(const std::string& name, u16& src, u16& dst, const std::string flags)
|
||||
: IReg(name, flags), src(src), dst(dst) {
|
||||
src = dst;
|
||||
}
|
||||
unsigned GetLength() override {
|
||||
return 16;
|
||||
}
|
||||
unsigned GetSrcDigit(unsigned pos) override {
|
||||
return (src >> pos) & 1;
|
||||
}
|
||||
unsigned GetDstDigit(unsigned pos) override {
|
||||
return (dst >> pos) & 1;
|
||||
}
|
||||
void SetSrcDigit(unsigned pos, unsigned value) override {
|
||||
src &= ~(1 << pos);
|
||||
src |= value << pos;
|
||||
}
|
||||
unsigned GetDigitRange() override {
|
||||
return 2;
|
||||
}
|
||||
|
||||
private:
|
||||
u16& src;
|
||||
u16& dst;
|
||||
};
|
||||
|
||||
u16* dspP = (u16*)0x1FF00000;
|
||||
u16* dspD = (u16*)0x1FF40000;
|
||||
|
||||
u16* srcBase = &dspD[0x2000];
|
||||
u16* dstBase = &dspD[0x2FD0];
|
||||
|
||||
constexpr u32 reg_region_size = 0x60;
|
||||
|
||||
std::unordered_map<std::string, unsigned> reg_map;
|
||||
|
||||
IReg* MakeHexReg(const std::string& name, unsigned offset) {
|
||||
reg_map[name] = offset;
|
||||
return new HexReg(name, srcBase[offset], dstBase[offset]);
|
||||
}
|
||||
|
||||
IReg* MakeBinReg(const std::string& name, unsigned offset, const std::string flags = "") {
|
||||
reg_map[name] = offset;
|
||||
return new BinReg(name, srcBase[offset], dstBase[offset], flags);
|
||||
}
|
||||
|
||||
constexpr unsigned t_row = 15;
|
||||
constexpr unsigned t_col = 4;
|
||||
|
||||
IReg* grid[t_row][t_col] = {};
|
||||
|
||||
unsigned c_row = 0, c_col = 0, c_pos = 0;
|
||||
|
||||
void PrintGrid(unsigned row, unsigned col) {
|
||||
IReg* r = grid[row][col];
|
||||
if (!r)
|
||||
return;
|
||||
r->Print(row * 2, 4 + col * 9, row == c_row && col == c_col ? c_pos : 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
void PrintAll() {
|
||||
for (unsigned row = 0; row < t_row; ++row)
|
||||
for (unsigned col = 0; col < t_col; ++col)
|
||||
PrintGrid(row, col);
|
||||
}
|
||||
|
||||
void FlushCache(void* ptr, u32 size) {
|
||||
svcFlushProcessDataCache(CUR_PROCESS_HANDLE, ptr, size);
|
||||
}
|
||||
|
||||
void InvalidateCache(void* ptr, u32 size) {
|
||||
svcInvalidateProcessDataCache(CUR_PROCESS_HANDLE, ptr, size);
|
||||
}
|
||||
|
||||
void StartDspProgram() {
|
||||
dspD[1] = 1;
|
||||
FlushCache(&dspD[1], 2);
|
||||
while (dspD[1])
|
||||
InvalidateCache(&dspD[1], 2);
|
||||
}
|
||||
|
||||
void StopDspProgram() {
|
||||
dspD[2] = 1;
|
||||
FlushCache(&dspD[2], 2);
|
||||
while (dspD[2])
|
||||
InvalidateCache(&dspD[2], 2);
|
||||
}
|
||||
|
||||
void SetOnshotDspProgram() {
|
||||
dspD[3] = 1;
|
||||
FlushCache(&dspD[3], 2);
|
||||
}
|
||||
|
||||
void PulseDspProgram() {
|
||||
SetOnshotDspProgram();
|
||||
StartDspProgram();
|
||||
StopDspProgram();
|
||||
}
|
||||
|
||||
void ExecuteTestCases() {
|
||||
consoleSelect(&bottomScreen);
|
||||
printf("--------\nExecuting test cases...!\n");
|
||||
|
||||
FILE* fi = fopen("teaklite2_tests", "rb");
|
||||
if (!fi)
|
||||
printf("failed to open input file\n");
|
||||
FILE* fo = fopen("teaklite2_tests_result", "wb");
|
||||
if (!fo)
|
||||
printf("failed to open output file\n");
|
||||
|
||||
StopDspProgram();
|
||||
int i = 0;
|
||||
while (true) {
|
||||
TestCase test_case;
|
||||
if (!fread(&test_case, sizeof(test_case), 1, fi))
|
||||
break;
|
||||
|
||||
srcBase[reg_map.at("a0l")] = test_case.before.a[0] & 0xFFFF;
|
||||
srcBase[reg_map.at("a0h")] = (test_case.before.a[0] >> 16) & 0xFFFF;
|
||||
srcBase[reg_map.at("a0e")] = (test_case.before.a[0] >> 32) & 0xFFFF;
|
||||
srcBase[reg_map.at("a1l")] = test_case.before.a[1] & 0xFFFF;
|
||||
srcBase[reg_map.at("a1h")] = (test_case.before.a[1] >> 16) & 0xFFFF;
|
||||
srcBase[reg_map.at("a1e")] = (test_case.before.a[1] >> 32) & 0xFFFF;
|
||||
srcBase[reg_map.at("b0l")] = test_case.before.b[0] & 0xFFFF;
|
||||
srcBase[reg_map.at("b0h")] = (test_case.before.b[0] >> 16) & 0xFFFF;
|
||||
srcBase[reg_map.at("b0e")] = (test_case.before.b[0] >> 32) & 0xFFFF;
|
||||
srcBase[reg_map.at("b1l")] = test_case.before.b[1] & 0xFFFF;
|
||||
srcBase[reg_map.at("b1h")] = (test_case.before.b[1] >> 16) & 0xFFFF;
|
||||
srcBase[reg_map.at("b1e")] = (test_case.before.b[1] >> 32) & 0xFFFF;
|
||||
srcBase[reg_map.at("p0l")] = test_case.before.p[0] & 0xFFFF;
|
||||
srcBase[reg_map.at("p0h")] = (test_case.before.p[0] >> 16) & 0xFFFF;
|
||||
srcBase[reg_map.at("p1l")] = test_case.before.p[1] & 0xFFFF;
|
||||
srcBase[reg_map.at("p1h")] = (test_case.before.p[1] >> 16) & 0xFFFF;
|
||||
srcBase[reg_map.at("r0")] = test_case.before.r[0];
|
||||
srcBase[reg_map.at("r1")] = test_case.before.r[1];
|
||||
srcBase[reg_map.at("r2")] = test_case.before.r[2];
|
||||
srcBase[reg_map.at("r3")] = test_case.before.r[3];
|
||||
srcBase[reg_map.at("r4")] = test_case.before.r[4];
|
||||
srcBase[reg_map.at("r5")] = test_case.before.r[5];
|
||||
srcBase[reg_map.at("r6")] = test_case.before.r[6];
|
||||
srcBase[reg_map.at("r7")] = test_case.before.r[7];
|
||||
srcBase[reg_map.at("x0")] = test_case.before.x[0];
|
||||
srcBase[reg_map.at("y0")] = test_case.before.y[0];
|
||||
srcBase[reg_map.at("x1")] = test_case.before.x[1];
|
||||
srcBase[reg_map.at("y1")] = test_case.before.y[1];
|
||||
srcBase[reg_map.at("sti0")] = test_case.before.stepi0;
|
||||
srcBase[reg_map.at("stj0")] = test_case.before.stepj0;
|
||||
srcBase[reg_map.at("mixp")] = test_case.before.mixp;
|
||||
srcBase[reg_map.at("sv")] = test_case.before.sv;
|
||||
srcBase[reg_map.at("repc")] = test_case.before.repc;
|
||||
srcBase[reg_map.at("lc")] = test_case.before.lc;
|
||||
srcBase[reg_map.at("cfgi")] = test_case.before.cfgi;
|
||||
srcBase[reg_map.at("cfgj")] = test_case.before.cfgj;
|
||||
srcBase[reg_map.at("stt0")] = test_case.before.stt0;
|
||||
srcBase[reg_map.at("stt1")] = test_case.before.stt1;
|
||||
srcBase[reg_map.at("stt2")] = test_case.before.stt2;
|
||||
srcBase[reg_map.at("mod0")] = test_case.before.mod0;
|
||||
srcBase[reg_map.at("mod1")] = test_case.before.mod1;
|
||||
srcBase[reg_map.at("mod2")] = test_case.before.mod2;
|
||||
srcBase[reg_map.at("ar0")] = test_case.before.ar[0];
|
||||
srcBase[reg_map.at("ar1")] = test_case.before.ar[1];
|
||||
srcBase[reg_map.at("arp0")] = test_case.before.arp[0];
|
||||
srcBase[reg_map.at("arp1")] = test_case.before.arp[1];
|
||||
srcBase[reg_map.at("arp2")] = test_case.before.arp[2];
|
||||
srcBase[reg_map.at("arp3")] = test_case.before.arp[3];
|
||||
|
||||
for (u16 i = 0; i < TestSpaceSize; ++i) {
|
||||
dspD[TestSpaceX + i] = test_case.before.test_space_x[i];
|
||||
dspD[TestSpaceY + i] = test_case.before.test_space_y[i];
|
||||
}
|
||||
|
||||
InvalidateCache(&dspP[0x2000], 0x2000);
|
||||
|
||||
dspP[0x2000] = test_case.opcode;
|
||||
dspP[0x2001] = test_case.expand;
|
||||
dspP[0x2002] = 0x0000; // nop
|
||||
dspP[0x2003] = 0x0000; // nop
|
||||
dspP[0x2004] = 0x0000; // nop
|
||||
dspP[0x2005] = 0x4180; // br 0x1800
|
||||
dspP[0x2006] = 0x1800;
|
||||
|
||||
FlushCache(&dspP[0x2000], 0x2000);
|
||||
|
||||
FlushCache(&dspD[0], 0x20000);
|
||||
InvalidateCache(&dspD[0], 0x20000);
|
||||
|
||||
PulseDspProgram();
|
||||
|
||||
FlushCache(&dspD[0], 0x20000);
|
||||
InvalidateCache(&dspD[0], 0x20000);
|
||||
|
||||
test_case.after.a[0] = dstBase[reg_map.at("a0l")] |
|
||||
((u64)dstBase[reg_map.at("a0h")] << 16) |
|
||||
((u64)dstBase[reg_map.at("a0e")] << 32);
|
||||
test_case.after.a[1] = dstBase[reg_map.at("a1l")] |
|
||||
((u64)dstBase[reg_map.at("a1h")] << 16) |
|
||||
((u64)dstBase[reg_map.at("a1e")] << 32);
|
||||
test_case.after.b[0] = dstBase[reg_map.at("b0l")] |
|
||||
((u64)dstBase[reg_map.at("b0h")] << 16) |
|
||||
((u64)dstBase[reg_map.at("b0e")] << 32);
|
||||
test_case.after.b[1] = dstBase[reg_map.at("b1l")] |
|
||||
((u64)dstBase[reg_map.at("b1h")] << 16) |
|
||||
((u64)dstBase[reg_map.at("b1e")] << 32);
|
||||
test_case.after.p[0] = dstBase[reg_map.at("p0l")] | ((u64)dstBase[reg_map.at("p0h")] << 16);
|
||||
test_case.after.p[1] = dstBase[reg_map.at("p1l")] | ((u64)dstBase[reg_map.at("p1h")] << 16);
|
||||
test_case.after.r[0] = dstBase[reg_map.at("r0")];
|
||||
test_case.after.r[1] = dstBase[reg_map.at("r1")];
|
||||
test_case.after.r[2] = dstBase[reg_map.at("r2")];
|
||||
test_case.after.r[3] = dstBase[reg_map.at("r3")];
|
||||
test_case.after.r[4] = dstBase[reg_map.at("r4")];
|
||||
test_case.after.r[5] = dstBase[reg_map.at("r5")];
|
||||
test_case.after.r[6] = dstBase[reg_map.at("r6")];
|
||||
test_case.after.r[7] = dstBase[reg_map.at("r7")];
|
||||
test_case.after.x[0] = dstBase[reg_map.at("x0")];
|
||||
test_case.after.y[0] = dstBase[reg_map.at("y0")];
|
||||
test_case.after.x[1] = dstBase[reg_map.at("x1")];
|
||||
test_case.after.y[1] = dstBase[reg_map.at("y1")];
|
||||
test_case.after.stepi0 = dstBase[reg_map.at("sti0")];
|
||||
test_case.after.stepj0 = dstBase[reg_map.at("stj0")];
|
||||
test_case.after.mixp = dstBase[reg_map.at("mixp")];
|
||||
test_case.after.sv = dstBase[reg_map.at("sv")];
|
||||
test_case.after.repc = dstBase[reg_map.at("repc")];
|
||||
test_case.after.lc = dstBase[reg_map.at("lc")];
|
||||
test_case.after.cfgi = dstBase[reg_map.at("cfgi")];
|
||||
test_case.after.cfgj = dstBase[reg_map.at("cfgj")];
|
||||
test_case.after.stt0 = dstBase[reg_map.at("stt0")];
|
||||
test_case.after.stt1 = dstBase[reg_map.at("stt1")];
|
||||
test_case.after.stt2 = dstBase[reg_map.at("stt2")];
|
||||
test_case.after.mod0 = dstBase[reg_map.at("mod0")];
|
||||
test_case.after.mod1 = dstBase[reg_map.at("mod1")];
|
||||
test_case.after.mod2 = dstBase[reg_map.at("mod2")];
|
||||
test_case.after.ar[0] = dstBase[reg_map.at("ar0")];
|
||||
test_case.after.ar[1] = dstBase[reg_map.at("ar1")];
|
||||
test_case.after.arp[0] = dstBase[reg_map.at("arp0")];
|
||||
test_case.after.arp[1] = dstBase[reg_map.at("arp1")];
|
||||
test_case.after.arp[2] = dstBase[reg_map.at("arp2")];
|
||||
test_case.after.arp[3] = dstBase[reg_map.at("arp3")];
|
||||
|
||||
for (u16 i = 0; i < TestSpaceSize; ++i) {
|
||||
test_case.after.test_space_x[i] = dspD[TestSpaceX + i];
|
||||
test_case.after.test_space_y[i] = dspD[TestSpaceY + i];
|
||||
}
|
||||
|
||||
fwrite(&test_case, sizeof(test_case), 1, fo);
|
||||
++i;
|
||||
if (i % 100 == 0)
|
||||
printf("case %d\n", i);
|
||||
}
|
||||
|
||||
fclose(fi);
|
||||
fclose(fo);
|
||||
|
||||
printf("Finished!\n");
|
||||
consoleSelect(&topScreen);
|
||||
}
|
||||
|
||||
void UploadDspProgram(const std::vector<u16>& code, bool oneshot) {
|
||||
StopDspProgram();
|
||||
InvalidateCache(&dspP[0x2000], 0x2000);
|
||||
|
||||
memcpy(&dspP[0x2000], code.data(), code.size() * 2);
|
||||
unsigned end = 0x2000 + code.size();
|
||||
dspP[end + 0] = 0x0000; // nop
|
||||
dspP[end + 1] = 0x0000; // nop
|
||||
dspP[end + 2] = 0x0000; // nop
|
||||
dspP[end + 3] = 0x4180; // br 0x1800
|
||||
dspP[end + 4] = 0x1800;
|
||||
|
||||
FlushCache(&dspP[0x2000], 0x2000);
|
||||
|
||||
if (oneshot) {
|
||||
PulseDspProgram();
|
||||
} else {
|
||||
StartDspProgram();
|
||||
}
|
||||
}
|
||||
|
||||
int udp_s;
|
||||
int udp_s_broadcast;
|
||||
|
||||
void UdpInit() {
|
||||
#define SOC_ALIGN 0x1000
|
||||
#define SOC_BUFFERSIZE 0x100000
|
||||
static u32* SOC_buffer;
|
||||
// allocate buffer for SOC service
|
||||
SOC_buffer = (u32*)memalign(SOC_ALIGN, SOC_BUFFERSIZE);
|
||||
if (SOC_buffer == NULL) {
|
||||
printf("memalign: failed to allocate\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Result ret;
|
||||
if ((ret = socInit(SOC_buffer, SOC_BUFFERSIZE)) != 0) {
|
||||
printf("socInit: 0x%08lX\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
sockaddr_in si_me;
|
||||
|
||||
// create a UDP socket
|
||||
if ((udp_s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
|
||||
printf("socket() failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// zero out the structure
|
||||
memset(&si_me, 0, sizeof(si_me));
|
||||
|
||||
si_me.sin_family = AF_INET;
|
||||
si_me.sin_port = htons(8888);
|
||||
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
// bind socket to port
|
||||
if (bind(udp_s, (sockaddr*)&si_me, sizeof(si_me)) == -1) {
|
||||
printf("bind() failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// create a UDP broadcast socket
|
||||
if ((udp_s_broadcast = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
|
||||
printf("socket()(broadcast) failed\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckPackage() {
|
||||
constexpr unsigned BUFLEN = 512;
|
||||
char buf[BUFLEN];
|
||||
sockaddr_in si_other;
|
||||
socklen_t slen = sizeof(si_other);
|
||||
int recv_len;
|
||||
if ((recv_len = recvfrom(udp_s, buf, BUFLEN, MSG_DONTWAIT, (sockaddr*)&si_other, &slen)) < 4)
|
||||
return;
|
||||
u16 magic;
|
||||
memcpy(&magic, buf, 2);
|
||||
if (magic == 0xD590 || magic == 0xD591) {
|
||||
std::vector<u16> program_package((recv_len - 2) / 2);
|
||||
memcpy(program_package.data(), buf + 2, program_package.size() * 2);
|
||||
consoleSelect(&bottomScreen);
|
||||
printf("--------\nNew program received!\n");
|
||||
for (u16 code : program_package) {
|
||||
printf("%04X ", code);
|
||||
}
|
||||
UploadDspProgram(program_package, magic == 0xD591);
|
||||
printf("\nUploaded!\n");
|
||||
|
||||
consoleSelect(&topScreen);
|
||||
} else if (magic == 0x4352) {
|
||||
recv_len -= 2;
|
||||
if ((unsigned)recv_len > reg_region_size)
|
||||
recv_len = reg_region_size;
|
||||
consoleSelect(&bottomScreen);
|
||||
printf("--------\nRegister sync received!\n");
|
||||
memcpy(srcBase, buf + 2, recv_len);
|
||||
printf("\nSynced!\n");
|
||||
consoleSelect(&topScreen);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
aptInit();
|
||||
gfxInitDefault();
|
||||
|
||||
consoleInit(GFX_TOP, &topScreen);
|
||||
consoleInit(GFX_BOTTOM, &bottomScreen);
|
||||
|
||||
consoleSelect(&bottomScreen);
|
||||
printf("Hello!\n");
|
||||
|
||||
UdpInit();
|
||||
|
||||
printf("dspInit: %08lX\n", dspInit());
|
||||
bool loaded = false;
|
||||
printf("DSP_LoadComponent: %08lX\n",
|
||||
DSP_LoadComponent(cdc_bin, cdc_bin_size, 0xFF, 0xFF, &loaded));
|
||||
printf("loaded = %d\n", loaded);
|
||||
consoleSelect(&topScreen);
|
||||
|
||||
svcSleepThread(1000000000);
|
||||
|
||||
grid[7][0] = MakeHexReg("r7", 0);
|
||||
grid[6][0] = MakeHexReg("r6", 1);
|
||||
grid[5][0] = MakeHexReg("r5", 2);
|
||||
grid[4][0] = MakeHexReg("r4", 3);
|
||||
grid[3][0] = MakeHexReg("r3", 4);
|
||||
grid[2][0] = MakeHexReg("r2", 5);
|
||||
grid[1][0] = MakeHexReg("r1", 6);
|
||||
grid[0][0] = MakeHexReg("r0", 7);
|
||||
|
||||
grid[6][1] = MakeHexReg("mixp", 8);
|
||||
grid[7][1] = MakeHexReg("repc", 9);
|
||||
grid[5][1] = MakeHexReg("stj0", 0xA);
|
||||
grid[5][2] = MakeHexReg("sti0", 0xB);
|
||||
grid[7][2] = MakeHexReg("lc", 0xC);
|
||||
|
||||
grid[3][1] = MakeHexReg("p1h", 0xD);
|
||||
grid[3][2] = MakeHexReg("p1l", 0xE);
|
||||
grid[2][2] = MakeHexReg("y1", 0xF);
|
||||
grid[2][1] = MakeHexReg("x1", 0x10);
|
||||
grid[1][1] = MakeHexReg("p0h", 0x11);
|
||||
grid[1][2] = MakeHexReg("p0l", 0x12);
|
||||
grid[0][2] = MakeHexReg("y0", 0x13);
|
||||
grid[0][1] = MakeHexReg("x0", 0x14);
|
||||
|
||||
grid[6][2] = MakeHexReg("sv", 0x15);
|
||||
|
||||
grid[12][1] = MakeHexReg("b1h", 0x16);
|
||||
grid[12][2] = MakeHexReg("b1l", 0x17);
|
||||
grid[12][0] = MakeHexReg("b1e", 0x18);
|
||||
grid[11][1] = MakeHexReg("b0h", 0x19);
|
||||
grid[11][2] = MakeHexReg("b0l", 0x1A);
|
||||
grid[11][0] = MakeHexReg("b0e", 0x1B);
|
||||
grid[10][1] = MakeHexReg("a1h", 0x1C);
|
||||
grid[10][2] = MakeHexReg("a1l", 0x1D);
|
||||
grid[10][0] = MakeHexReg("a1e", 0x1E);
|
||||
grid[9][1] = MakeHexReg("a0h", 0x1F);
|
||||
grid[9][2] = MakeHexReg("a0l", 0x20);
|
||||
grid[9][0] = MakeHexReg("a0e", 0x21);
|
||||
|
||||
grid[13][3] = MakeBinReg("arp3", 0x22, "#RR#RRiiiiijjjjj");
|
||||
grid[12][3] = MakeBinReg("arp2", 0x23, "#RR#RRiiiiijjjjj");
|
||||
grid[11][3] = MakeBinReg("arp1", 0x24, "#RR#RRiiiiijjjjj");
|
||||
grid[10][3] = MakeBinReg("arp0", 0x25, "#RR#RRiiiiijjjjj");
|
||||
grid[9][3] = MakeBinReg("ar1", 0x26, "RRRRRRoosssoosss");
|
||||
grid[8][3] = MakeBinReg("ar0", 0x27, "RRRRRRoosssoosss");
|
||||
grid[7][3] = MakeBinReg("mod2", 0x28, "7654321m7654321M");
|
||||
grid[6][3] = MakeBinReg("mod1", 0x29, "jicB####pppppppp");
|
||||
grid[5][3] = MakeBinReg("mod0", 0x2A, "#QQ#PPooSYY###SS");
|
||||
grid[4][3] = MakeBinReg("stt2", 0x2B, "LBBB####mm##V21I");
|
||||
grid[3][3] = MakeBinReg("stt1", 0x2C, "QP#########R####");
|
||||
grid[2][3] = MakeBinReg("stt0", 0x2D, "####C###ZMNVCELL");
|
||||
grid[1][3] = MakeBinReg("cfgj", 0x2E, "mmmmmmmmmsssssss");
|
||||
grid[0][3] = MakeBinReg("cfgi", 0x2F, "mmmmmmmmmsssssss");
|
||||
|
||||
MoveCursor(28, 0);
|
||||
char hostname[100];
|
||||
gethostname(hostname, 100);
|
||||
printf("IP: %s port: 8888\n", hostname);
|
||||
printf("X: use sample program Y: Fill memory");
|
||||
|
||||
// Main loop
|
||||
while (aptMainLoop()) {
|
||||
hidScanInput();
|
||||
|
||||
u32 kDown = hidKeysDown();
|
||||
|
||||
if (kDown & KEY_START)
|
||||
break;
|
||||
|
||||
if (kDown & KEY_SELECT)
|
||||
ExecuteTestCases();
|
||||
|
||||
if (kDown & KEY_DOWN) {
|
||||
for (int next = (int)c_row + 1; next < (int)t_row; ++next) {
|
||||
if (grid[next][c_col]) {
|
||||
c_row = next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (kDown & KEY_UP) {
|
||||
for (int next = (int)c_row - 1; next >= 0; --next) {
|
||||
if (grid[next][c_col]) {
|
||||
c_row = next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (kDown & KEY_LEFT) {
|
||||
if (c_pos == grid[c_row][c_col]->GetLength() - 1) {
|
||||
for (int next = (int)c_col - 1; next >= 0; --next) {
|
||||
if (grid[c_row][next]) {
|
||||
c_col = next;
|
||||
c_pos = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
++c_pos;
|
||||
}
|
||||
}
|
||||
|
||||
if (kDown & KEY_RIGHT) {
|
||||
if (c_pos == 0) {
|
||||
for (int next = (int)c_col + 1; next < (int)t_col; ++next) {
|
||||
if (grid[c_row][next]) {
|
||||
c_col = next;
|
||||
c_pos = grid[c_row][c_col]->GetLength() - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
--c_pos;
|
||||
}
|
||||
}
|
||||
|
||||
if (kDown & KEY_A) {
|
||||
unsigned v = grid[c_row][c_col]->GetSrcDigit(c_pos);
|
||||
++v;
|
||||
if (v == grid[c_row][c_col]->GetDigitRange())
|
||||
v = 0;
|
||||
grid[c_row][c_col]->SetSrcDigit(c_pos, v);
|
||||
}
|
||||
|
||||
if (kDown & KEY_B) {
|
||||
unsigned v = grid[c_row][c_col]->GetSrcDigit(c_pos);
|
||||
if (v == 0)
|
||||
v = grid[c_row][c_col]->GetDigitRange();
|
||||
--v;
|
||||
grid[c_row][c_col]->SetSrcDigit(c_pos, v);
|
||||
}
|
||||
|
||||
if (kDown & KEY_X) {
|
||||
UploadDspProgram({0x86A0}, false); // add r0, a0
|
||||
}
|
||||
|
||||
if (kDown & KEY_Y) {
|
||||
FlushCache(&dspD[0x3000], 0x40000 - 0x6000);
|
||||
for (u32 i = 0x3000; i < 0x20000; ++i) {
|
||||
dspD[i] = i; // i >> 4;
|
||||
}
|
||||
InvalidateCache(&dspD[0x3000], 0x40000 - 0x6000);
|
||||
}
|
||||
|
||||
if (kDown & KEY_TOUCH) {
|
||||
consoleSelect(&bottomScreen);
|
||||
printf("--------\nSending register sync!\n");
|
||||
u8 buf[reg_region_size + 2];
|
||||
u16 magic = 0x4352;
|
||||
memcpy(buf, &magic, 2);
|
||||
memcpy(buf + 2, srcBase, reg_region_size);
|
||||
sockaddr_in addr{};
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(8888);
|
||||
addr.sin_addr.s_addr = 0xFFFFFFFF;
|
||||
sendto(udp_s_broadcast, buf, reg_region_size + 2, 0, (sockaddr*)&addr, sizeof(addr));
|
||||
printf("\nSynced!\n");
|
||||
consoleSelect(&topScreen);
|
||||
}
|
||||
|
||||
FlushCache(&dspD[0x2000], 0x1000);
|
||||
InvalidateCache(&dspD[0x2000], 0x2000);
|
||||
PrintAll();
|
||||
|
||||
CheckPackage();
|
||||
|
||||
// Flush and swap framebuffers
|
||||
gfxFlushBuffers();
|
||||
gfxSwapBuffers();
|
||||
|
||||
// Wait for VBlank
|
||||
gspWaitForVBlank();
|
||||
}
|
||||
socExit();
|
||||
dspExit();
|
||||
gfxExit();
|
||||
aptExit();
|
||||
return 0;
|
||||
}
|
20
third_party/teakra/hwtest/dspvictester/.gitignore
vendored
Normal file
20
third_party/teakra/hwtest/dspvictester/.gitignore
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
*.3dsx
|
||||
*.elf
|
||||
*.cxi
|
||||
*.cia
|
||||
build/
|
||||
hide/
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Mac crap
|
||||
.DS_Store
|
||||
|
171
third_party/teakra/hwtest/dspvictester/Makefile
vendored
Normal file
171
third_party/teakra/hwtest/dspvictester/Makefile
vendored
Normal file
|
@ -0,0 +1,171 @@
|
|||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
# ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional)
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
DATA := data
|
||||
INCLUDES := include
|
||||
#ROMFS := romfs
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
|
||||
|
||||
CFLAGS := -g -Wall -O2 -mword-relocations \
|
||||
-fomit-frame-pointer -ffunction-sections \
|
||||
$(ARCH)
|
||||
|
||||
CFLAGS += $(INCLUDE) -DARM11 -D_3DS
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lctru -lm
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level containing
|
||||
# include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS := $(CTRULIB)
|
||||
|
||||
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica)))
|
||||
SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(TARGET).cxi $(TARGET).cia $(TARGET).elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(OUTPUT).3dsx $(OUTPUT).cia
|
||||
|
||||
ifeq ($(strip $(NO_SMDH)),)
|
||||
$(OUTPUT).3dsx : $(OUTPUT).elf $(OUTPUT).smdh
|
||||
else
|
||||
$(OUTPUT).3dsx : $(OUTPUT).elf
|
||||
endif
|
||||
|
||||
$(OUTPUT).cia : $(OUTPUT).cxi
|
||||
@makerom -f cia -o $(OUTPUT).cia -target t -i $(OUTPUT).cxi:0:0
|
||||
@echo built ... $(OUTPUT).cia
|
||||
|
||||
$(OUTPUT).cxi : $(OUTPUT).elf $(OUTPUT).smdh $(TOPDIR)/rsf.rsf $(TOPDIR)/banner.bnr
|
||||
@makerom -o $(OUTPUT).cxi -rsf $(TOPDIR)/rsf.rsf -target t -elf $(OUTPUT).elf -icon $(OUTPUT).smdh -banner $(TOPDIR)/banner.bnr
|
||||
@echo built ... $(OUTPUT).cxi
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# rules for assembling GPU shaders
|
||||
#---------------------------------------------------------------------------------
|
||||
define shader-as
|
||||
$(eval CURBIN := $(patsubst %.shbin.o,%.shbin,$(notdir $@)))
|
||||
picasso -o $(CURBIN) $1
|
||||
bin2s $(CURBIN) | $(AS) -o $@
|
||||
echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h
|
||||
echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h
|
||||
echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h
|
||||
endef
|
||||
|
||||
%.shbin.o : %.v.pica %.g.pica
|
||||
@echo $(notdir $^)
|
||||
@$(call shader-as,$^)
|
||||
|
||||
%.shbin.o : %.v.pica
|
||||
@echo $(notdir $<)
|
||||
@$(call shader-as,$<)
|
||||
|
||||
%.shbin.o : %.shlist
|
||||
@echo $(notdir $<)
|
||||
@$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)/$(file)))
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
BIN
third_party/teakra/hwtest/dspvictester/banner.bnr
vendored
Normal file
BIN
third_party/teakra/hwtest/dspvictester/banner.bnr
vendored
Normal file
Binary file not shown.
BIN
third_party/teakra/hwtest/dspvictester/data/cdc.bin
vendored
Normal file
BIN
third_party/teakra/hwtest/dspvictester/data/cdc.bin
vendored
Normal file
Binary file not shown.
53
third_party/teakra/hwtest/dspvictester/firm/source
vendored
Normal file
53
third_party/teakra/hwtest/dspvictester/firm/source
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
segment p 0000
|
||||
br 0x0000$0800 always // reset vector
|
||||
reti always
|
||||
data 0000
|
||||
reti always
|
||||
data 0000
|
||||
reti always // int0
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
reti always // int1
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
data 0000
|
||||
reti always // int2
|
||||
|
||||
segment p 0800 // init
|
||||
mov 0x$f000 sp // set stack pointer
|
||||
mov 0x$0880 mod3 // enable interrupt
|
||||
load 0x0082u8 page // configure ICU
|
||||
mov 0x$1000 r0
|
||||
mov r0 [page:0x0014u8] // set IRQ0 vint address low
|
||||
mov 0x$8001 r0 // !mov 0x$0000 r0
|
||||
mov r0 [page:0x0012u8] // set IRQ0 vint address high & context
|
||||
mov 0x$0001 r0
|
||||
mov r0 [page:0x000cu8] // enable IRQ0 as vint
|
||||
load 0x00aau8 page
|
||||
cntx s
|
||||
load 0x00bbu8 page
|
||||
mov 0x$0001 r0
|
||||
mov 0x$8204 r1
|
||||
mov r0 [r1] // trigger IRQ0
|
||||
brr 0xffff always
|
||||
|
||||
segment p 11000 // interrup handler
|
||||
mov st1 r0
|
||||
load 0x0000u8 page
|
||||
mov r0 [page:0x0000u8]
|
||||
mov 0x$1234 r0
|
||||
mov r0 [page:0x0001u8]
|
||||
brr 0xffff always
|
||||
|
||||
|
||||
segment d 0000 // signal area
|
||||
data 0000
|
||||
data 0000
|
228
third_party/teakra/hwtest/dspvictester/rsf.rsf
vendored
Executable file
228
third_party/teakra/hwtest/dspvictester/rsf.rsf
vendored
Executable file
|
@ -0,0 +1,228 @@
|
|||
BasicInfo:
|
||||
Title : SVRT
|
||||
ProductCode : CTR-P-SVRT
|
||||
Logo : Nintendo # Nintendo / Licensed / Distributed / iQue / iQueForSystem
|
||||
|
||||
RomFs:
|
||||
# Specifies the root path of the read only file system to include in the ROM.
|
||||
#RootPath : romfs
|
||||
|
||||
TitleInfo:
|
||||
Category : Application
|
||||
UniqueId : 0xff557
|
||||
|
||||
Option:
|
||||
UseOnSD : true # true if App is to be installed to SD
|
||||
FreeProductCode : true # Removes limitations on ProductCode
|
||||
MediaFootPadding : false # If true CCI files are created with padding
|
||||
EnableCrypt : false # Enables encryption for NCCH and CIA
|
||||
EnableCompress : true # Compresses where applicable (currently only exefs:/.code)
|
||||
|
||||
AccessControlInfo:
|
||||
CoreVersion : 2
|
||||
|
||||
# Exheader Format Version
|
||||
DescVersion : 2
|
||||
|
||||
# Minimum Required Kernel Version (below is for 4.5.0)
|
||||
ReleaseKernelMajor : "02"
|
||||
ReleaseKernelMinor : "33"
|
||||
|
||||
# ExtData
|
||||
UseExtSaveData : false # enables ExtData
|
||||
#ExtSaveDataId : 0x300 # only set this when the ID is different to the UniqueId
|
||||
|
||||
# FS:USER Archive Access Permissions
|
||||
# Uncomment as required
|
||||
FileSystemAccess:
|
||||
- CategorySystemApplication
|
||||
- CategoryHardwareCheck
|
||||
- CategoryFileSystemTool
|
||||
- Debug
|
||||
#- TwlCardBackup
|
||||
#- TwlNandData
|
||||
#- Boss
|
||||
- DirectSdmc
|
||||
#- Core
|
||||
#- CtrNandRo
|
||||
#- CtrNandRw
|
||||
#- CtrNandRoWrite
|
||||
#- CategorySystemSettings
|
||||
#- CardBoard
|
||||
#- ExportImportIvs
|
||||
#- DirectSdmcWrite
|
||||
#- SwitchCleanup
|
||||
#- SaveDataMove
|
||||
#- Shop
|
||||
#- Shell
|
||||
#- CategoryHomeMenu
|
||||
|
||||
# Process Settings
|
||||
MemoryType : Application # Application/System/Base
|
||||
SystemMode : 64MB # 64MB(Default)/96MB/80MB/72MB/32MB
|
||||
IdealProcessor : 0
|
||||
AffinityMask : 1
|
||||
Priority : 16
|
||||
MaxCpu : 0 # Let system decide
|
||||
HandleTableSize : 0x200
|
||||
DisableDebug : false
|
||||
EnableForceDebug : false
|
||||
CanWriteSharedPage : false
|
||||
CanUsePrivilegedPriority : true
|
||||
CanUseNonAlphabetAndNumber : true
|
||||
PermitMainFunctionArgument : true
|
||||
CanShareDeviceMemory : true
|
||||
RunnableOnSleep : false
|
||||
SpecialMemoryArrange : false
|
||||
|
||||
# New3DS Exclusive Process Settings
|
||||
SystemModeExt : Legacy # Legacy(Default)/124MB/178MB Legacy:Use Old3DS SystemMode
|
||||
CpuSpeed : 268MHz # 268MHz(Default)/804MHz
|
||||
EnableL2Cache : false # false(default)/true
|
||||
CanAccessCore2 : false
|
||||
|
||||
# Virtual Address Mappings
|
||||
IORegisterMapping:
|
||||
- 1ed03000-1ed03fff # DSP registers
|
||||
- 1ff00000-1ff7ffff # DSP memory
|
||||
MemoryMapping:
|
||||
- 1f000000-1f5fffff:r # VRAM
|
||||
|
||||
# Accessible SVCs, <Name>:<ID>
|
||||
SystemCallAccess:
|
||||
InvalidateProcessDataCache: 82
|
||||
FlushProcessDataCache: 84
|
||||
ArbitrateAddress: 34
|
||||
Break: 60
|
||||
CancelTimer: 28
|
||||
ClearEvent: 25
|
||||
ClearTimer: 29
|
||||
CloseHandle: 35
|
||||
ConnectToPort: 45
|
||||
ControlMemory: 1
|
||||
CreateAddressArbiter: 33
|
||||
CreateEvent: 23
|
||||
CreateMemoryBlock: 30
|
||||
CreateMutex: 19
|
||||
CreateSemaphore: 21
|
||||
CreateThread: 8
|
||||
CreateTimer: 26
|
||||
DuplicateHandle: 39
|
||||
ExitProcess: 3
|
||||
ExitThread: 9
|
||||
GetCurrentProcessorNumber: 17
|
||||
GetHandleInfo: 41
|
||||
GetProcessId: 53
|
||||
GetProcessIdOfThread: 54
|
||||
GetProcessIdealProcessor: 6
|
||||
GetProcessInfo: 43
|
||||
GetResourceLimit: 56
|
||||
GetResourceLimitCurrentValues: 58
|
||||
GetResourceLimitLimitValues: 57
|
||||
GetSystemInfo: 42
|
||||
GetSystemTick: 40
|
||||
GetThreadContext: 59
|
||||
GetThreadId: 55
|
||||
GetThreadIdealProcessor: 15
|
||||
GetThreadInfo: 44
|
||||
GetThreadPriority: 11
|
||||
MapMemoryBlock: 31
|
||||
OutputDebugString: 61
|
||||
QueryMemory: 2
|
||||
ReleaseMutex: 20
|
||||
ReleaseSemaphore: 22
|
||||
SendSyncRequest1: 46
|
||||
SendSyncRequest2: 47
|
||||
SendSyncRequest3: 48
|
||||
SendSyncRequest4: 49
|
||||
SendSyncRequest: 50
|
||||
SetThreadPriority: 12
|
||||
SetTimer: 27
|
||||
SignalEvent: 24
|
||||
SleepThread: 10
|
||||
UnmapMemoryBlock: 32
|
||||
WaitSynchronization1: 36
|
||||
WaitSynchronizationN: 37
|
||||
Backdoor: 123
|
||||
BindInterrupt: 80
|
||||
|
||||
# Service List
|
||||
# Maximum 34 services (32 if firmware is prior to 9.3.0)
|
||||
ServiceAccessControl:
|
||||
- cfg:u
|
||||
- fs:USER
|
||||
- gsp::Gpu
|
||||
- hid:USER
|
||||
- ndm:u
|
||||
- pxi:dev
|
||||
- APT:U
|
||||
- ac:u
|
||||
- act:u
|
||||
- am:net
|
||||
- boss:U
|
||||
- cam:u
|
||||
- cecd:u
|
||||
- csnd:SND
|
||||
- frd:u
|
||||
- http:C
|
||||
- ir:USER
|
||||
- ir:u
|
||||
- ir:rst
|
||||
- ldr:ro
|
||||
- mic:u
|
||||
- news:u
|
||||
- nfc:u
|
||||
- nim:aoc
|
||||
- nwm::UDS
|
||||
- ptm:u
|
||||
- qtm:u
|
||||
- soc:U
|
||||
- ssl:C
|
||||
- y2r:u
|
||||
- ps:ps
|
||||
|
||||
|
||||
SystemControlInfo:
|
||||
SaveDataSize: 512K
|
||||
RemasterVersion: 0
|
||||
StackSize: 0x40000
|
||||
|
||||
# Modules that run services listed above should be included below
|
||||
# Maximum 48 dependencies
|
||||
# If a module is listed that isn't present on the 3DS, the title will get stuck at the logo (3ds waves)
|
||||
# So act, nfc and qtm are commented for 4.x support. Uncomment if you need these.
|
||||
# <module name>:<module titleid>
|
||||
Dependency:
|
||||
ac: 0x0004013000002402
|
||||
#act: 0x0004013000003802
|
||||
am: 0x0004013000001502
|
||||
boss: 0x0004013000003402
|
||||
camera: 0x0004013000001602
|
||||
cecd: 0x0004013000002602
|
||||
cfg: 0x0004013000001702
|
||||
codec: 0x0004013000001802
|
||||
csnd: 0x0004013000002702
|
||||
dlp: 0x0004013000002802
|
||||
dsp: 0x0004013000001a02
|
||||
friends: 0x0004013000003202
|
||||
gpio: 0x0004013000001b02
|
||||
gsp: 0x0004013000001c02
|
||||
hid: 0x0004013000001d02
|
||||
http: 0x0004013000002902
|
||||
i2c: 0x0004013000001e02
|
||||
ir: 0x0004013000003302
|
||||
mcu: 0x0004013000001f02
|
||||
mic: 0x0004013000002002
|
||||
ndm: 0x0004013000002b02
|
||||
news: 0x0004013000003502
|
||||
#nfc: 0x0004013000004002
|
||||
nim: 0x0004013000002c02
|
||||
nwm: 0x0004013000002d02
|
||||
pdn: 0x0004013000002102
|
||||
ps: 0x0004013000003102
|
||||
ptm: 0x0004013000002202
|
||||
#qtm: 0x0004013020004202
|
||||
ro: 0x0004013000003702
|
||||
socket: 0x0004013000002e02
|
||||
spi: 0x0004013000002302
|
||||
ssl: 0x0004013000002f02
|
28
third_party/teakra/hwtest/dspvictester/send.py
vendored
Executable file
28
third_party/teakra/hwtest/dspvictester/send.py
vendored
Executable file
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import socket
|
||||
import struct
|
||||
|
||||
for UDP_IP in ["192.168.1.158", "127.0.0.1"]:
|
||||
UDP_PORT = 8888
|
||||
MESSAGE = struct.pack('<H', 0xD592)
|
||||
|
||||
for i in range(1, len(sys.argv)):
|
||||
inst = sys.argv[i]
|
||||
if inst == "r":
|
||||
MESSAGE += struct.pack('<H', 0)
|
||||
continue
|
||||
elif inst == "w":
|
||||
MESSAGE += struct.pack('<H', 1)
|
||||
continue
|
||||
elif inst == "hr":
|
||||
MESSAGE += struct.pack('<H', 2)
|
||||
continue
|
||||
elif inst == "hw":
|
||||
MESSAGE += struct.pack('<H', 3)
|
||||
continue
|
||||
MESSAGE += struct.pack('<H', int(inst, 16))
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))
|
291
third_party/teakra/hwtest/dspvictester/source/main.cpp
vendored
Normal file
291
third_party/teakra/hwtest/dspvictester/source/main.cpp
vendored
Normal file
|
@ -0,0 +1,291 @@
|
|||
#include <atomic>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <3ds.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "cdc_bin.h"
|
||||
|
||||
PrintConsole topScreen, bottomScreen;
|
||||
|
||||
void MoveCursor(unsigned row, unsigned col) {
|
||||
printf("\x1b[%u;%uH", row + 1, col + 1);
|
||||
}
|
||||
|
||||
enum Color {
|
||||
Reset = 0,
|
||||
Black = 30,
|
||||
Red = 31,
|
||||
Green = 32,
|
||||
Yellow = 33,
|
||||
Blue = 34,
|
||||
Magnenta = 35,
|
||||
Cyan = 36,
|
||||
White = 37,
|
||||
};
|
||||
void SetColor(Color color, Color background) {
|
||||
printf("\x1b[%dm\x1b[%dm", (int)color, (int)background + 10);
|
||||
}
|
||||
|
||||
void FlushCache(volatile void* ptr, u32 size) {
|
||||
svcFlushProcessDataCache(CUR_PROCESS_HANDLE, (void*)ptr, size);
|
||||
}
|
||||
|
||||
void InvalidateCache(volatile void* ptr, u32 size) {
|
||||
svcInvalidateProcessDataCache(CUR_PROCESS_HANDLE, (void*)ptr, size);
|
||||
}
|
||||
|
||||
vu16* dspP = (vu16*)0x1FF00000;
|
||||
vu16* dspD = (vu16*)0x1FF40000;
|
||||
|
||||
std::atomic<int> interrupt_counter;
|
||||
|
||||
void PrintAll() {
|
||||
consoleSelect(&topScreen);
|
||||
|
||||
MoveCursor(0, 0);
|
||||
printf("DSP registers:\n");
|
||||
|
||||
InvalidateCache(dspD + 0, 2 * 2);
|
||||
|
||||
printf("st1 = %04X\n", dspD[0]);
|
||||
printf("mgc = %04X\n", dspD[1]);
|
||||
|
||||
consoleSelect(&bottomScreen);
|
||||
}
|
||||
|
||||
int udp_s;
|
||||
int udp_s_broadcast;
|
||||
|
||||
void UdpInit() {
|
||||
#define SOC_ALIGN 0x1000
|
||||
#define SOC_BUFFERSIZE 0x100000
|
||||
static u32* SOC_buffer;
|
||||
// allocate buffer for SOC service
|
||||
SOC_buffer = (u32*)memalign(SOC_ALIGN, SOC_BUFFERSIZE);
|
||||
if (SOC_buffer == NULL) {
|
||||
printf("memalign: failed to allocate\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Result ret;
|
||||
if ((ret = socInit(SOC_buffer, SOC_BUFFERSIZE)) != 0) {
|
||||
printf("socInit: 0x%08lX\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
sockaddr_in si_me;
|
||||
|
||||
// create a UDP socket
|
||||
if ((udp_s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
|
||||
printf("socket() failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// zero out the structure
|
||||
memset(&si_me, 0, sizeof(si_me));
|
||||
|
||||
si_me.sin_family = AF_INET;
|
||||
si_me.sin_port = htons(8888);
|
||||
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
// bind socket to port
|
||||
if (bind(udp_s, (sockaddr*)&si_me, sizeof(si_me)) == -1) {
|
||||
printf("bind() failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// create a UDP broadcast socket
|
||||
if ((udp_s_broadcast = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
|
||||
printf("socket()(broadcast) failed\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr unsigned BUFLEN = 512;
|
||||
char buf[BUFLEN];
|
||||
|
||||
void Fire() {
|
||||
dspD[0] = 1;
|
||||
FlushCache((void*)dspD, 8);
|
||||
while (true) {
|
||||
InvalidateCache((void*)dspD, 8);
|
||||
if (dspD[0] == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckPackage() {
|
||||
sockaddr_in si_other;
|
||||
socklen_t slen = sizeof(si_other);
|
||||
int recv_len;
|
||||
if ((recv_len = recvfrom(udp_s, buf, BUFLEN, MSG_DONTWAIT, (sockaddr*)&si_other, &slen)) < 4)
|
||||
return;
|
||||
u16 magic;
|
||||
memcpy(&magic, buf, 2);
|
||||
if (magic == 0xD592) {
|
||||
std::vector<u16> command_package((recv_len - 2) / 2);
|
||||
printf("Command received\n");
|
||||
memcpy(command_package.data(), buf + 2, command_package.size() * 2);
|
||||
switch (command_package[0]) {
|
||||
case 0: {
|
||||
if (command_package.size() != 2) {
|
||||
printf("Wrong length for Read\n");
|
||||
break;
|
||||
}
|
||||
u16 addr = command_package[1];
|
||||
printf("Read [%04X] -> ", addr);
|
||||
dspD[1] = 0;
|
||||
dspD[2] = addr;
|
||||
dspD[3] = 0xCCCC;
|
||||
|
||||
Fire();
|
||||
|
||||
printf("%04X\n", dspD[3]);
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
if (command_package.size() != 3) {
|
||||
printf("Wrong length for Write\n");
|
||||
break;
|
||||
}
|
||||
u16 addr = command_package[1];
|
||||
u16 value = command_package[2];
|
||||
printf("Write [%04X] <- %04X", addr, value);
|
||||
dspD[1] = 1;
|
||||
dspD[2] = addr;
|
||||
dspD[3] = value;
|
||||
|
||||
Fire();
|
||||
|
||||
printf(" OK\n");
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if (command_package.size() != 3) {
|
||||
printf("Wrong length for CPU Read\n");
|
||||
break;
|
||||
}
|
||||
u32 addr = command_package[1] | ((u32)command_package[2] << 16);
|
||||
printf("Read CPU [%08lX] -> ", addr);
|
||||
// FlushCache((vu16*)addr, 2);
|
||||
printf("%04X\n", *(vu16*)(addr));
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
if (command_package.size() != 4) {
|
||||
printf("Wrong length for CPU Write\n");
|
||||
break;
|
||||
}
|
||||
u32 addr = command_package[1] | ((u32)command_package[2] << 16);
|
||||
u16 value = command_package[3];
|
||||
printf("Write CPU [%08lX] <- %04X", addr, value);
|
||||
// InvalidateCache((vu16*)addr, 2);
|
||||
*(vu16*)(addr) = value;
|
||||
printf(" OK\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Handle pmHandle;
|
||||
Result pmInit_(void) {
|
||||
Result res = srvGetServiceHandle(&pmHandle, "pm:app");
|
||||
return res;
|
||||
}
|
||||
void pmExit_(void) {
|
||||
svcCloseHandle(pmHandle);
|
||||
}
|
||||
|
||||
Result PM_TerminateTitle(u64 tid, u64 timeout) {
|
||||
Result ret = 0;
|
||||
u32* cmdbuf = getThreadCommandBuffer();
|
||||
|
||||
cmdbuf[0] = IPC_MakeHeader(0x4, 4, 0);
|
||||
cmdbuf[1] = tid & 0xFFFFFFFF;
|
||||
cmdbuf[2] = tid >> 32;
|
||||
cmdbuf[3] = timeout & 0xffffffff;
|
||||
cmdbuf[4] = (timeout >> 32) & 0xffffffff;
|
||||
|
||||
if (R_FAILED(ret = svcSendSyncRequest(pmHandle)))
|
||||
return ret;
|
||||
|
||||
return (Result)cmdbuf[1];
|
||||
}
|
||||
|
||||
Handle dsp_interrupt;
|
||||
Handle threadA;
|
||||
u32 threadA_stack[0x400];
|
||||
|
||||
void threadA_entry(void*) {
|
||||
while (true) {
|
||||
svcWaitSynchronization(dsp_interrupt, INT64_MAX);
|
||||
++interrupt_counter;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
aptInit();
|
||||
gfxInitDefault();
|
||||
|
||||
consoleInit(GFX_TOP, &topScreen);
|
||||
consoleInit(GFX_BOTTOM, &bottomScreen);
|
||||
|
||||
consoleSelect(&bottomScreen);
|
||||
printf("Hello!\n");
|
||||
|
||||
UdpInit();
|
||||
|
||||
printf("dspInit: %08lX\n", dspInit());
|
||||
bool loaded = false;
|
||||
printf("DSP_LoadComponent: %08lX\n",
|
||||
DSP_LoadComponent(cdc_bin, cdc_bin_size, 0xFF, 0xFF, &loaded));
|
||||
printf("loaded = %d\n", loaded);
|
||||
|
||||
svcSleepThread(1000000000);
|
||||
char hostname[100];
|
||||
gethostname(hostname, 100);
|
||||
printf("IP: %s port: 8888\n", hostname);
|
||||
|
||||
pmInit_();
|
||||
printf("PM_TerminateTitle(DSP): %08lX\n", PM_TerminateTitle(0x0004013000001a02, 0));
|
||||
pmExit_();
|
||||
|
||||
svcCreateEvent(&dsp_interrupt, ResetType::RESET_ONESHOT);
|
||||
interrupt_counter = 0;
|
||||
svcCreateThread(&threadA, threadA_entry, 0x0, threadA_stack + 0x400, 4, 0xFFFFFFFE);
|
||||
printf("BindInterrupt: %08lX\n", svcBindInterrupt(0x4A, dsp_interrupt, 4, 0));
|
||||
|
||||
// Main loop
|
||||
while (aptMainLoop()) {
|
||||
hidScanInput();
|
||||
|
||||
u32 kDown = hidKeysDown();
|
||||
|
||||
if (kDown & KEY_START)
|
||||
break;
|
||||
|
||||
if (kDown & KEY_A)
|
||||
printf("hello\n");
|
||||
|
||||
PrintAll();
|
||||
|
||||
// Flush and swap framebuffers
|
||||
gfxFlushBuffers();
|
||||
gfxSwapBuffers();
|
||||
|
||||
// Wait for VBlank
|
||||
gspWaitForVBlank();
|
||||
}
|
||||
socExit();
|
||||
dspExit();
|
||||
gfxExit();
|
||||
aptExit();
|
||||
return 0;
|
||||
}
|
23
third_party/teakra/include/teakra/disassembler.h
vendored
Normal file
23
third_party/teakra/include/teakra/disassembler.h
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Teakra::Disassembler {
|
||||
|
||||
struct ArArpSettings {
|
||||
std::array<std::uint16_t, 2> ar;
|
||||
std::array<std::uint16_t, 4> arp;
|
||||
};
|
||||
|
||||
bool NeedExpansion(std::uint16_t opcode);
|
||||
bool NeedExpansion(std::uint16_t opcode);
|
||||
std::vector<std::string> GetTokenList(std::uint16_t opcode, std::uint16_t expansion = 0,
|
||||
std::optional<ArArpSettings> ar_arp = std::nullopt);
|
||||
std::string Do(std::uint16_t opcode, std::uint16_t expansion = 0,
|
||||
std::optional<ArArpSettings> ar_arp = std::nullopt);
|
||||
|
||||
} // namespace Teakra::Disassembler
|
18
third_party/teakra/include/teakra/disassembler_c.h
vendored
Normal file
18
third_party/teakra/include/teakra/disassembler_c.h
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool Teakra_Disasm_NeedExpansion(uint16_t opcode);
|
||||
|
||||
size_t Teakra_Disasm_Do(char* dst, size_t dstlen,
|
||||
uint16_t opcode, uint16_t expansion /*= 0*/);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
687
third_party/teakra/include/teakra/impl/register.h
vendored
Normal file
687
third_party/teakra/include/teakra/impl/register.h
vendored
Normal file
|
@ -0,0 +1,687 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "../../../src/common_types.h"
|
||||
#include "../../../src/crash.h"
|
||||
#include "../../../src/operand.h"
|
||||
|
||||
namespace Teakra {
|
||||
|
||||
struct RegisterState {
|
||||
void Reset() {
|
||||
*this = RegisterState();
|
||||
}
|
||||
|
||||
/** Program control unit **/
|
||||
|
||||
u32 pc = 0; // 18-bit, program counter
|
||||
u16 prpage = 0; // 4-bit, program page
|
||||
u16 cpc = 1; // 1-bit, change word order when push/pop pc
|
||||
|
||||
u16 repc = 0; // 16-bit rep loop counter
|
||||
u16 repcs = 0; // repc shadow
|
||||
bool rep = false; // true when in rep loop
|
||||
u16 crep = 1; // 1-bit. If clear, store/restore repc to shadows on context switch
|
||||
|
||||
u16 bcn = 0; // 3-bit, nest loop counter
|
||||
u16 lp = 0; // 1-bit, set when in a loop
|
||||
|
||||
struct BlockRepeatFrame {
|
||||
u32 start = 0;
|
||||
u32 end = 0;
|
||||
u16 lc = 0;
|
||||
};
|
||||
|
||||
std::array<BlockRepeatFrame, 4> bkrep_stack;
|
||||
u16& Lc() {
|
||||
if (lp)
|
||||
return bkrep_stack[bcn - 1].lc;
|
||||
return bkrep_stack[0].lc;
|
||||
}
|
||||
|
||||
/** Computation unit **/
|
||||
|
||||
// 40-bit 2's comp accumulators.
|
||||
// Use 64-bit 2's comp here. The upper 24 bits are always sign extension
|
||||
std::array<u64, 2> a{};
|
||||
std::array<u64, 2> b{};
|
||||
|
||||
u64 a1s = 0, b1s = 0; // shadows for a1 and b1
|
||||
u16 ccnta = 1; // 1-bit. If clear, store/restore a1/b1 to shadows on context switch
|
||||
|
||||
u16 sat = 0; // 1-bit, disable saturation when moving from acc
|
||||
u16 sata = 1; // 1-bit, disable saturation when moving to acc
|
||||
u16 s = 0; // 1-bit, shift mode. 0 - arithmetic, 1 - logic
|
||||
u16 sv = 0; // 16-bit two's complement shift value
|
||||
|
||||
// 1-bit flags
|
||||
u16 fz = 0; // zero flag
|
||||
u16 fm = 0; // negative flag
|
||||
u16 fn = 0; // normalized flag
|
||||
u16 fv = 0; // overflow flag
|
||||
u16 fe = 0; // extension flag
|
||||
u16 fc0 = 0; // carry flag
|
||||
u16 fc1 = 0; // another carry flag
|
||||
u16 flm = 0; // set on saturation
|
||||
u16 fvl = 0; // latching fv
|
||||
u16 fr = 0; // Rn zero flag
|
||||
|
||||
// Viterbi
|
||||
u16 vtr0 = 0;
|
||||
u16 vtr1 = 0;
|
||||
|
||||
/** Multiplication unit **/
|
||||
|
||||
std::array<u16, 2> x{}; // factor
|
||||
std::array<u16, 2> y{}; // factor
|
||||
u16 hwm = 0; // 2-bit, half word mode, modify y on multiplication
|
||||
std::array<u32, 2> p{}; // product
|
||||
std::array<u16, 2> pe{}; // 1-bit product extension
|
||||
std::array<u16, 2> ps{}; // 2-bit, product shift mode
|
||||
u16 p0h_cbs = 0; // 16-bit hidden state for codebook search (CBS) opcode
|
||||
|
||||
/** Address unit **/
|
||||
|
||||
std::array<u16, 8> r{}; // 16-bit general and address registers
|
||||
u16 mixp = 0; // 16-bit, stores result of min/max instructions
|
||||
u16 sp = 0; // 16-bit stack pointer
|
||||
u16 page = 0; // 8-bit, higher part of MemImm8 address
|
||||
u16 pcmhi = 0; // 2-bit, higher part of program address for movp/movd
|
||||
|
||||
// shadows for bank exchange;
|
||||
u16 r0b = 0, r1b = 0, r4b = 0, r7b = 0;
|
||||
|
||||
/** Address step/mod unit **/
|
||||
|
||||
// step/modulo
|
||||
u16 stepi = 0, stepj = 0; // 7-bit step
|
||||
u16 modi = 0, modj = 0; // 9-bit mod
|
||||
u16 stepi0 = 0, stepj0 = 0; // 16-bit step
|
||||
|
||||
// shadows for bank exchange
|
||||
u16 stepib = 0, stepjb = 0;
|
||||
u16 modib = 0, modjb = 0;
|
||||
u16 stepi0b = 0, stepj0b = 0;
|
||||
|
||||
std::array<u16, 8> m{}; // 1-bit each, enable modulo arithmetic for Rn
|
||||
std::array<u16, 8> br{}; // 1-bit each, use bit-reversed value from Rn as address
|
||||
u16 stp16 = 0; // 1 bit. If set, stepi0/j0 will be exchanged along with cfgi/j in banke, and use
|
||||
// stepi0/j0 for steping
|
||||
u16 cmd = 1; // 1-bit, step/mod method. 0 - Teak; 1 - TeakLite
|
||||
u16 epi = 0; // 1-bit. If set, cause r3 = 0 when steping r3
|
||||
u16 epj = 0; // 1-bit. If set, cause r7 = 0 when steping r7
|
||||
|
||||
/** Indirect address unit **/
|
||||
|
||||
// 3 bits each
|
||||
// 0: +0
|
||||
// 1: +1
|
||||
// 2: -1
|
||||
// 3: +s
|
||||
// 4: +2
|
||||
// 5: -2
|
||||
// 6: +2*
|
||||
// 7: -2*
|
||||
std::array<u16, 4> arstep{{1, 4, 5, 3}}, arpstepi{{1, 4, 5, 3}}, arpstepj{{1, 4, 5, 3}};
|
||||
|
||||
// 2 bits each
|
||||
// 0: +0
|
||||
// 1: +1
|
||||
// 2: -1
|
||||
// 3: -1*
|
||||
std::array<u16, 4> aroffset{{0, 1, 2, 0}}, arpoffseti{{0, 1, 2, 0}}, arpoffsetj{{0, 1, 2, 0}};
|
||||
|
||||
// 3 bits each, represent r0~r7
|
||||
std::array<u16, 4> arrn{{0, 4, 2, 6}};
|
||||
|
||||
// 2 bits each. for i represent r0~r3, for j represents r4~r7
|
||||
std::array<u16, 4> arprni{{0, 1, 2, 3}}, arprnj{{0, 1, 2, 3}};
|
||||
|
||||
/** Interrupt unit **/
|
||||
|
||||
// interrupt pending bit
|
||||
std::array<u16, 3> ip{};
|
||||
u16 ipv = 0;
|
||||
|
||||
// interrupt enable bit
|
||||
std::array<u16, 3> im{};
|
||||
u16 imv = 0;
|
||||
|
||||
// interrupt context switching bit
|
||||
std::array<u16, 3> ic{};
|
||||
u16 nimc = 0;
|
||||
|
||||
// interrupt enable master bit
|
||||
u16 ie = 0;
|
||||
|
||||
/** Extension unit **/
|
||||
|
||||
std::array<u16, 5> ou{}; // user output pins
|
||||
std::array<u16, 2> iu{}; // user input pins
|
||||
std::array<u16, 4> ext{};
|
||||
|
||||
u16 mod0_unk_const = 1; // 3-bit
|
||||
|
||||
/** Shadow registers **/
|
||||
|
||||
template <u16 RegisterState::* origin>
|
||||
class ShadowRegister {
|
||||
public:
|
||||
void Store(RegisterState* self) {
|
||||
shadow = self->*origin;
|
||||
}
|
||||
void Restore(RegisterState* self) {
|
||||
self->*origin = shadow;
|
||||
}
|
||||
|
||||
private:
|
||||
u16 shadow = 0;
|
||||
};
|
||||
|
||||
template <std::size_t size, std::array<u16, size> RegisterState::* origin>
|
||||
class ShadowArrayRegister {
|
||||
public:
|
||||
void Store(RegisterState* self) {
|
||||
shadow = self->*origin;
|
||||
}
|
||||
void Restore(RegisterState* self) {
|
||||
self->*origin = shadow;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<u16, size> shadow{};
|
||||
};
|
||||
|
||||
template <typename... ShadowRegisters>
|
||||
class ShadowRegisterList : private ShadowRegisters... {
|
||||
public:
|
||||
void Store(RegisterState* self) {
|
||||
(ShadowRegisters::Store(self), ...);
|
||||
}
|
||||
void Restore(RegisterState* self) {
|
||||
(ShadowRegisters::Restore(self), ...);
|
||||
}
|
||||
};
|
||||
|
||||
template <u16 RegisterState::* origin>
|
||||
class ShadowSwapRegister {
|
||||
public:
|
||||
void Swap(RegisterState* self) {
|
||||
std::swap(self->*origin, shadow);
|
||||
}
|
||||
|
||||
private:
|
||||
u16 shadow = 0;
|
||||
};
|
||||
|
||||
template <std::size_t size, std::array<u16, size> RegisterState::* origin>
|
||||
class ShadowSwapArrayRegister {
|
||||
public:
|
||||
void Swap(RegisterState* self) {
|
||||
std::swap(self->*origin, shadow);
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<u16, size> shadow{};
|
||||
};
|
||||
|
||||
template <typename... ShadowSwapRegisters>
|
||||
class ShadowSwapRegisterList : private ShadowSwapRegisters... {
|
||||
public:
|
||||
void Swap(RegisterState* self) {
|
||||
(ShadowSwapRegisters::Swap(self), ...);
|
||||
}
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
|
||||
ShadowRegisterList<
|
||||
ShadowRegister<&RegisterState::flm>,
|
||||
ShadowRegister<&RegisterState::fvl>,
|
||||
ShadowRegister<&RegisterState::fe>,
|
||||
ShadowRegister<&RegisterState::fc0>,
|
||||
ShadowRegister<&RegisterState::fc1>,
|
||||
ShadowRegister<&RegisterState::fv>,
|
||||
ShadowRegister<&RegisterState::fn>,
|
||||
ShadowRegister<&RegisterState::fm>,
|
||||
ShadowRegister<&RegisterState::fz>,
|
||||
ShadowRegister<&RegisterState::fr>
|
||||
> shadow_registers;
|
||||
|
||||
ShadowSwapRegisterList<
|
||||
ShadowSwapRegister<&RegisterState::pcmhi>,
|
||||
ShadowSwapRegister<&RegisterState::sat>,
|
||||
ShadowSwapRegister<&RegisterState::sata>,
|
||||
ShadowSwapRegister<&RegisterState::hwm>,
|
||||
ShadowSwapRegister<&RegisterState::s>,
|
||||
ShadowSwapArrayRegister<2, &RegisterState::ps>,
|
||||
ShadowSwapRegister<&RegisterState::page>,
|
||||
ShadowSwapRegister<&RegisterState::stp16>,
|
||||
ShadowSwapRegister<&RegisterState::cmd>,
|
||||
ShadowSwapArrayRegister<8, &RegisterState::m>,
|
||||
ShadowSwapArrayRegister<8, &RegisterState::br>,
|
||||
ShadowSwapArrayRegister<3, &RegisterState::im>, // ?
|
||||
ShadowSwapRegister<&RegisterState::imv>, // ?
|
||||
ShadowSwapRegister<&RegisterState::epi>,
|
||||
ShadowSwapRegister<&RegisterState::epj>
|
||||
> shadow_swap_registers;
|
||||
|
||||
// clang-format on
|
||||
|
||||
template <unsigned index>
|
||||
class ShadowSwapAr {
|
||||
public:
|
||||
void Swap(RegisterState* self) {
|
||||
std::swap(self->arrn[index * 2], rni);
|
||||
std::swap(self->arrn[index * 2 + 1], rnj);
|
||||
std::swap(self->arstep[index * 2], stepi);
|
||||
std::swap(self->arstep[index * 2 + 1], stepj);
|
||||
std::swap(self->aroffset[index * 2], offseti);
|
||||
std::swap(self->aroffset[index * 2 + 1], offsetj);
|
||||
}
|
||||
|
||||
private:
|
||||
u16 rni, rnj, stepi, stepj, offseti, offsetj;
|
||||
};
|
||||
|
||||
template <unsigned index>
|
||||
class ShadowSwapArp {
|
||||
public:
|
||||
void Swap(RegisterState* self) {
|
||||
std::swap(self->arprni[index], rni);
|
||||
std::swap(self->arprnj[index], rnj);
|
||||
std::swap(self->arpstepi[index], stepi);
|
||||
std::swap(self->arpstepj[index], stepj);
|
||||
std::swap(self->arpoffseti[index], offseti);
|
||||
std::swap(self->arpoffsetj[index], offsetj);
|
||||
}
|
||||
|
||||
private:
|
||||
u16 rni, rnj, stepi, stepj, offseti, offsetj;
|
||||
};
|
||||
|
||||
ShadowSwapAr<0> shadow_swap_ar0;
|
||||
ShadowSwapAr<1> shadow_swap_ar1;
|
||||
ShadowSwapArp<0> shadow_swap_arp0;
|
||||
ShadowSwapArp<1> shadow_swap_arp1;
|
||||
ShadowSwapArp<2> shadow_swap_arp2;
|
||||
ShadowSwapArp<3> shadow_swap_arp3;
|
||||
|
||||
void ShadowStore() {
|
||||
shadow_registers.Store(this);
|
||||
}
|
||||
|
||||
void ShadowRestore() {
|
||||
shadow_registers.Restore(this);
|
||||
}
|
||||
|
||||
void SwapAllArArp() {
|
||||
shadow_swap_ar0.Swap(this);
|
||||
shadow_swap_ar1.Swap(this);
|
||||
shadow_swap_arp0.Swap(this);
|
||||
shadow_swap_arp1.Swap(this);
|
||||
shadow_swap_arp2.Swap(this);
|
||||
shadow_swap_arp3.Swap(this);
|
||||
}
|
||||
|
||||
void ShadowSwap() {
|
||||
shadow_swap_registers.Swap(this);
|
||||
SwapAllArArp();
|
||||
}
|
||||
|
||||
void SwapAr(u16 index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
shadow_swap_ar0.Swap(this);
|
||||
break;
|
||||
case 1:
|
||||
shadow_swap_ar1.Swap(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SwapArp(u16 index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
shadow_swap_arp0.Swap(this);
|
||||
break;
|
||||
case 1:
|
||||
shadow_swap_arp1.Swap(this);
|
||||
break;
|
||||
case 2:
|
||||
shadow_swap_arp2.Swap(this);
|
||||
break;
|
||||
case 3:
|
||||
shadow_swap_arp3.Swap(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ConditionPass(Cond cond) const {
|
||||
switch (cond.GetName()) {
|
||||
case CondValue::True:
|
||||
return true;
|
||||
case CondValue::Eq:
|
||||
return fz == 1;
|
||||
case CondValue::Neq:
|
||||
return fz == 0;
|
||||
case CondValue::Gt:
|
||||
return fz == 0 && fm == 0;
|
||||
case CondValue::Ge:
|
||||
return fm == 0;
|
||||
case CondValue::Lt:
|
||||
return fm == 1;
|
||||
case CondValue::Le:
|
||||
return fm == 1 || fz == 1;
|
||||
case CondValue::Nn:
|
||||
return fn == 0;
|
||||
case CondValue::C:
|
||||
return fc0 == 1;
|
||||
case CondValue::V:
|
||||
return fv == 1;
|
||||
case CondValue::E:
|
||||
return fe == 1;
|
||||
case CondValue::L:
|
||||
return flm == 1 || fvl == 1;
|
||||
case CondValue::Nr:
|
||||
return fr == 0;
|
||||
case CondValue::Niu0:
|
||||
return iu[0] == 0;
|
||||
case CondValue::Iu0:
|
||||
return iu[0] == 1;
|
||||
case CondValue::Iu1:
|
||||
return iu[1] == 1;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename PseudoRegisterT>
|
||||
u16 Get() const {
|
||||
return PseudoRegisterT::Get(this);
|
||||
}
|
||||
|
||||
template <typename PseudoRegisterT>
|
||||
void Set(u16 value) {
|
||||
PseudoRegisterT::Set(this, value);
|
||||
}
|
||||
};
|
||||
|
||||
template <u16 RegisterState::* target>
|
||||
struct Redirector {
|
||||
static u16 Get(const RegisterState* self) {
|
||||
return self->*target;
|
||||
}
|
||||
static void Set(RegisterState* self, u16 value) {
|
||||
self->*target = value;
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t size, std::array<u16, size> RegisterState::* target, std::size_t index>
|
||||
struct ArrayRedirector {
|
||||
static u16 Get(const RegisterState* self) {
|
||||
return (self->*target)[index];
|
||||
}
|
||||
static void Set(RegisterState* self, u16 value) {
|
||||
(self->*target)[index] = value;
|
||||
}
|
||||
};
|
||||
|
||||
template <u16 RegisterState::* target0, u16 RegisterState::* target1>
|
||||
struct DoubleRedirector {
|
||||
static u16 Get(const RegisterState* self) {
|
||||
return self->*target0 | self->*target1;
|
||||
}
|
||||
static void Set(RegisterState* self, u16 value) {
|
||||
self->*target0 = self->*target1 = value;
|
||||
}
|
||||
};
|
||||
|
||||
template <u16 RegisterState::* target>
|
||||
struct RORedirector {
|
||||
static u16 Get(const RegisterState* self) {
|
||||
return self->*target;
|
||||
}
|
||||
static void Set(RegisterState*, u16) {
|
||||
// no
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t size, std::array<u16, size> RegisterState::* target, std::size_t index>
|
||||
struct ArrayRORedirector {
|
||||
static u16 Get(const RegisterState* self) {
|
||||
return (self->*target)[index];
|
||||
}
|
||||
static void Set(RegisterState*, u16) {
|
||||
// no
|
||||
}
|
||||
};
|
||||
|
||||
template <unsigned index>
|
||||
struct AccEProxy {
|
||||
static u16 Get(const RegisterState* self) {
|
||||
return (u16)((self->a[index] >> 32) & 0xF);
|
||||
}
|
||||
static void Set(RegisterState* self, u16 value) {
|
||||
u32 value32 = SignExtend<4>((u32)value);
|
||||
self->a[index] &= 0xFFFFFFFF;
|
||||
self->a[index] |= (u64)value32 << 32;
|
||||
}
|
||||
};
|
||||
|
||||
struct LPRedirector {
|
||||
static u16 Get(const RegisterState* self) {
|
||||
return self->lp;
|
||||
}
|
||||
static void Set(RegisterState* self, u16 value) {
|
||||
if (value != 0) {
|
||||
self->lp = 0;
|
||||
self->bcn = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Proxy, unsigned position, unsigned length>
|
||||
struct ProxySlot {
|
||||
using proxy = Proxy;
|
||||
static constexpr unsigned pos = position;
|
||||
static constexpr unsigned len = length;
|
||||
static_assert(length < 16, "Error");
|
||||
static_assert(position + length <= 16, "Error");
|
||||
static constexpr u16 mask = ((1 << length) - 1) << position;
|
||||
};
|
||||
|
||||
template <typename... ProxySlots>
|
||||
struct PseudoRegister {
|
||||
static_assert(NoOverlap<u16, ProxySlots::mask...>, "Error");
|
||||
static u16 Get(const RegisterState* self) {
|
||||
return ((ProxySlots::proxy::Get(self) << ProxySlots::pos) | ...);
|
||||
}
|
||||
static void Set(RegisterState* self, u16 value) {
|
||||
(ProxySlots::proxy::Set(self, (value >> ProxySlots::pos) & ((1 << ProxySlots::len) - 1)),
|
||||
...);
|
||||
}
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
|
||||
using cfgi = PseudoRegister<
|
||||
ProxySlot<Redirector<&RegisterState::stepi>, 0, 7>,
|
||||
ProxySlot<Redirector<&RegisterState::modi>, 7, 9>
|
||||
>;
|
||||
|
||||
using cfgj = PseudoRegister<
|
||||
ProxySlot<Redirector<&RegisterState::stepj>, 0, 7>,
|
||||
ProxySlot<Redirector<&RegisterState::modj>, 7, 9>
|
||||
>;
|
||||
|
||||
using stt0 = PseudoRegister<
|
||||
ProxySlot<Redirector<&RegisterState::flm>, 0, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::fvl>, 1, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::fe>, 2, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::fc0>, 3, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::fv>, 4, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::fn>, 5, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::fm>, 6, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::fz>, 7, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::fc1>, 11, 1>
|
||||
>;
|
||||
using stt1 = PseudoRegister<
|
||||
ProxySlot<Redirector<&RegisterState::fr>, 4, 1>,
|
||||
ProxySlot<ArrayRORedirector<2, &RegisterState::iu, 0>, 10, 1>,
|
||||
ProxySlot<ArrayRORedirector<2, &RegisterState::iu, 1>, 11, 1>,
|
||||
ProxySlot<ArrayRedirector<2, &RegisterState::pe, 0>, 14, 1>,
|
||||
ProxySlot<ArrayRedirector<2, &RegisterState::pe, 1>, 15, 1>
|
||||
>;
|
||||
using stt2 = PseudoRegister<
|
||||
ProxySlot<ArrayRORedirector<3, &RegisterState::ip, 0>, 0, 1>,
|
||||
ProxySlot<ArrayRORedirector<3, &RegisterState::ip, 1>, 1, 1>,
|
||||
ProxySlot<ArrayRORedirector<3, &RegisterState::ip, 2>, 2, 1>,
|
||||
ProxySlot<RORedirector<&RegisterState::ipv>, 3, 1>,
|
||||
|
||||
ProxySlot<Redirector<&RegisterState::pcmhi>, 6, 2>,
|
||||
|
||||
ProxySlot<RORedirector<&RegisterState::bcn>, 12, 3>,
|
||||
ProxySlot<LPRedirector, 15, 1>
|
||||
>;
|
||||
using mod0 = PseudoRegister<
|
||||
ProxySlot<Redirector<&RegisterState::sat>, 0, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::sata>, 1, 1>,
|
||||
ProxySlot<RORedirector<&RegisterState::mod0_unk_const>, 2, 3>,
|
||||
ProxySlot<Redirector<&RegisterState::hwm>, 5, 2>,
|
||||
ProxySlot<Redirector<&RegisterState::s>, 7, 1>,
|
||||
ProxySlot<ArrayRedirector<5, &RegisterState::ou, 0>, 8, 1>,
|
||||
ProxySlot<ArrayRedirector<5, &RegisterState::ou, 1>, 9, 1>,
|
||||
ProxySlot<ArrayRedirector<2, &RegisterState::ps, 0>, 10, 2>,
|
||||
|
||||
ProxySlot<ArrayRedirector<2, &RegisterState::ps, 1>, 13, 2>
|
||||
>;
|
||||
using mod1 = PseudoRegister<
|
||||
ProxySlot<Redirector<&RegisterState::page>, 0, 8>,
|
||||
|
||||
ProxySlot<Redirector<&RegisterState::stp16>, 12, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::cmd>, 13, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::epi>, 14, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::epj>, 15, 1>
|
||||
>;
|
||||
using mod2 = PseudoRegister<
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::m, 0>, 0, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::m, 1>, 1, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::m, 2>, 2, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::m, 3>, 3, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::m, 4>, 4, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::m, 5>, 5, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::m, 6>, 6, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::m, 7>, 7, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::br, 0>, 8, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::br, 1>, 9, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::br, 2>, 10, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::br, 3>, 11, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::br, 4>, 12, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::br, 5>, 13, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::br, 6>, 14, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::br, 7>, 15, 1>
|
||||
>;
|
||||
using mod3 = PseudoRegister<
|
||||
ProxySlot<Redirector<&RegisterState::nimc>, 0, 1>,
|
||||
ProxySlot<ArrayRedirector<3, &RegisterState::ic, 0>, 1, 1>,
|
||||
ProxySlot<ArrayRedirector<3, &RegisterState::ic, 1>, 2, 1>,
|
||||
ProxySlot<ArrayRedirector<3, &RegisterState::ic, 2>, 3, 1>,
|
||||
ProxySlot<ArrayRedirector<5, &RegisterState::ou, 2>, 4, 1>,
|
||||
ProxySlot<ArrayRedirector<5, &RegisterState::ou, 3>, 5, 1>,
|
||||
ProxySlot<ArrayRedirector<5, &RegisterState::ou, 4>, 6, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::ie>, 7, 1>,
|
||||
ProxySlot<ArrayRedirector<3, &RegisterState::im, 0>, 8, 1>,
|
||||
ProxySlot<ArrayRedirector<3, &RegisterState::im, 1>, 9, 1>,
|
||||
ProxySlot<ArrayRedirector<3, &RegisterState::im, 2>, 10, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::imv>, 11, 1>,
|
||||
|
||||
ProxySlot<Redirector<&RegisterState::ccnta>, 13, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::cpc>, 14, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::crep>, 15, 1>
|
||||
>;
|
||||
|
||||
using st0 = PseudoRegister<
|
||||
ProxySlot<Redirector<&RegisterState::sat>, 0, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::ie>, 1, 1>,
|
||||
ProxySlot<ArrayRedirector<3, &RegisterState::im, 0>, 2, 1>,
|
||||
ProxySlot<ArrayRedirector<3, &RegisterState::im, 1>, 3, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::fr>, 4, 1>,
|
||||
ProxySlot<DoubleRedirector<&RegisterState::flm, &RegisterState::fvl>, 5, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::fe>, 6, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::fc0>, 7, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::fv>, 8, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::fn>, 9, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::fm>, 10, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::fz>, 11, 1>,
|
||||
ProxySlot<AccEProxy<0>, 12, 4>
|
||||
>;
|
||||
using st1 = PseudoRegister<
|
||||
ProxySlot<Redirector<&RegisterState::page>, 0, 8>,
|
||||
// 8, 9: reserved
|
||||
ProxySlot<ArrayRedirector<2, &RegisterState::ps, 0>, 10, 2>,
|
||||
ProxySlot<AccEProxy<1>, 12, 4>
|
||||
>;
|
||||
using st2 = PseudoRegister<
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::m, 0>, 0, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::m, 1>, 1, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::m, 2>, 2, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::m, 3>, 3, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::m, 4>, 4, 1>,
|
||||
ProxySlot<ArrayRedirector<8, &RegisterState::m, 5>, 5, 1>,
|
||||
ProxySlot<ArrayRedirector<3, &RegisterState::im, 2>, 6, 1>,
|
||||
ProxySlot<Redirector<&RegisterState::s>, 7, 1>,
|
||||
ProxySlot<ArrayRedirector<5, &RegisterState::ou, 0>, 8, 1>,
|
||||
ProxySlot<ArrayRedirector<5, &RegisterState::ou, 1>, 9, 1>,
|
||||
ProxySlot<ArrayRORedirector<2, &RegisterState::iu, 0>, 10, 1>,
|
||||
ProxySlot<ArrayRORedirector<2, &RegisterState::iu, 1>, 11, 1>,
|
||||
// 12: reserved
|
||||
ProxySlot<ArrayRORedirector<3, &RegisterState::ip, 2>, 13, 1>, // Note the index order!
|
||||
ProxySlot<ArrayRORedirector<3, &RegisterState::ip, 0>, 14, 1>,
|
||||
ProxySlot<ArrayRORedirector<3, &RegisterState::ip, 1>, 15, 1>
|
||||
>;
|
||||
using icr = PseudoRegister<
|
||||
ProxySlot<Redirector<&RegisterState::nimc>, 0, 1>,
|
||||
ProxySlot<ArrayRedirector<3, &RegisterState::ic, 0>, 1, 1>,
|
||||
ProxySlot<ArrayRedirector<3, &RegisterState::ic, 1>, 2, 1>,
|
||||
ProxySlot<ArrayRedirector<3, &RegisterState::ic, 2>, 3, 1>,
|
||||
ProxySlot<LPRedirector, 4, 1>,
|
||||
ProxySlot<RORedirector<&RegisterState::bcn>, 5, 3>
|
||||
>;
|
||||
|
||||
template<unsigned index>
|
||||
using ar = PseudoRegister<
|
||||
ProxySlot<ArrayRedirector<4, &RegisterState::arstep, index * 2 + 1>, 0, 3>,
|
||||
ProxySlot<ArrayRedirector<4, &RegisterState::aroffset, index * 2 + 1>, 3, 2>,
|
||||
ProxySlot<ArrayRedirector<4, &RegisterState::arstep, index * 2>, 5, 3>,
|
||||
ProxySlot<ArrayRedirector<4, &RegisterState::aroffset, index * 2>, 8, 2>,
|
||||
ProxySlot<ArrayRedirector<4, &RegisterState::arrn, index * 2 + 1>, 10, 3>,
|
||||
ProxySlot<ArrayRedirector<4, &RegisterState::arrn, index * 2>, 13, 3>
|
||||
>;
|
||||
|
||||
using ar0 = ar<0>;
|
||||
using ar1 = ar<1>;
|
||||
|
||||
template<unsigned index>
|
||||
using arp = PseudoRegister<
|
||||
ProxySlot<ArrayRedirector<4, &RegisterState::arpstepi, index>, 0, 3>,
|
||||
ProxySlot<ArrayRedirector<4, &RegisterState::arpoffseti, index>, 3, 2>,
|
||||
ProxySlot<ArrayRedirector<4, &RegisterState::arpstepj, index>, 5, 3>,
|
||||
ProxySlot<ArrayRedirector<4, &RegisterState::arpoffsetj, index>, 8, 2>,
|
||||
ProxySlot<ArrayRedirector<4, &RegisterState::arprni, index>, 10, 2>,
|
||||
// bit 12 reserved
|
||||
ProxySlot<ArrayRedirector<4, &RegisterState::arprnj, index>, 13, 2>
|
||||
// bit 15 reserved
|
||||
>;
|
||||
|
||||
using arp0 = arp<0>;
|
||||
using arp1 = arp<1>;
|
||||
using arp2 = arp<2>;
|
||||
using arp3 = arp<3>;
|
||||
|
||||
// clang-format on
|
||||
|
||||
} // namespace Teakra
|
93
third_party/teakra/include/teakra/teakra.h
vendored
Normal file
93
third_party/teakra/include/teakra/teakra.h
vendored
Normal file
|
@ -0,0 +1,93 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace Teakra {
|
||||
struct RegisterState;
|
||||
|
||||
struct AHBMCallback {
|
||||
std::function<std::uint8_t(std::uint32_t address)> read8;
|
||||
std::function<void(std::uint32_t address, std::uint8_t value)> write8;
|
||||
|
||||
std::function<std::uint16_t(std::uint32_t address)> read16;
|
||||
std::function<void(std::uint32_t address, std::uint16_t value)> write16;
|
||||
|
||||
std::function<std::uint32_t(std::uint32_t address)> read32;
|
||||
std::function<void(std::uint32_t address, std::uint32_t value)> write32;
|
||||
};
|
||||
|
||||
struct UserConfig {
|
||||
// DSP memory. By default set to nullptr. If it stays nullptr then Teakra will create its own
|
||||
// DSP memory and retain ownership of it. Otherwise, the user will own it.
|
||||
std::uint8_t* dsp_memory = nullptr;
|
||||
};
|
||||
|
||||
static constexpr std::uint32_t DspMemorySize = 0x80000;
|
||||
|
||||
class Teakra {
|
||||
public:
|
||||
Teakra(const UserConfig& config);
|
||||
~Teakra();
|
||||
|
||||
void Reset();
|
||||
|
||||
uint8_t* GetDspMemory();
|
||||
const uint8_t* GetDspMemory() const;
|
||||
|
||||
RegisterState& GetRegisterState();
|
||||
const RegisterState& GetRegisterState() const;
|
||||
|
||||
// APBP Data
|
||||
bool SendDataIsEmpty(std::uint8_t index) const;
|
||||
void SendData(std::uint8_t index, std::uint16_t value);
|
||||
bool RecvDataIsReady(std::uint8_t index) const;
|
||||
std::uint16_t RecvData(std::uint8_t index);
|
||||
std::uint16_t PeekRecvData(std::uint8_t index);
|
||||
void SetRecvDataHandler(std::uint8_t index, std::function<void()> handler);
|
||||
|
||||
// APBP Semaphore
|
||||
void SetSemaphore(std::uint16_t value);
|
||||
void ClearSemaphore(std::uint16_t value);
|
||||
void MaskSemaphore(std::uint16_t value);
|
||||
void SetSemaphoreHandler(std::function<void()> handler);
|
||||
std::uint16_t GetSemaphore() const;
|
||||
|
||||
// for implementing DSP_PDATA/PADR DMA transfers
|
||||
std::uint16_t ProgramRead(std::uint32_t address) const;
|
||||
void ProgramWrite(std::uint32_t address, std::uint16_t value);
|
||||
std::uint16_t DataRead(std::uint16_t address, bool bypass_mmio = false);
|
||||
void DataWrite(std::uint16_t address, std::uint16_t value, bool bypass_mmio = false);
|
||||
std::uint16_t DataReadA32(std::uint32_t address) const;
|
||||
void DataWriteA32(std::uint32_t address, std::uint16_t value);
|
||||
std::uint16_t MMIORead(std::uint16_t address);
|
||||
void MMIOWrite(std::uint16_t address, std::uint16_t value);
|
||||
|
||||
// DSP_PADR is only 16-bit, so this is where the DMA interface gets the
|
||||
// upper 16-bits from
|
||||
std::uint16_t DMAChan0GetSrcHigh();
|
||||
std::uint16_t DMAChan0GetDstHigh();
|
||||
|
||||
std::uint16_t AHBMGetUnitSize(std::uint16_t i) const;
|
||||
std::uint16_t AHBMGetDirection(std::uint16_t i) const;
|
||||
std::uint16_t AHBMGetDmaChannel(std::uint16_t i) const;
|
||||
// we need these as AHBM does some weird stuff on unaligned accesses internally
|
||||
std::uint16_t AHBMRead16(std::uint32_t addr);
|
||||
void AHBMWrite16(std::uint32_t addr, std::uint16_t value);
|
||||
std::uint16_t AHBMRead32(std::uint32_t addr);
|
||||
void AHBMWrite32(std::uint32_t addr, std::uint32_t value);
|
||||
|
||||
// core
|
||||
void Run(unsigned cycle);
|
||||
|
||||
void SetAHBMCallback(const AHBMCallback& callback);
|
||||
|
||||
void SetAudioCallback(std::function<void(std::array<std::int16_t, 2>)> callback);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
} // namespace Teakra
|
79
third_party/teakra/include/teakra/teakra_c.h
vendored
Normal file
79
third_party/teakra/include/teakra/teakra_c.h
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct TeakraObject;
|
||||
typedef struct TeakraObject TeakraContext;
|
||||
|
||||
typedef void (*Teakra_InterruptCallback)(void* userdata);
|
||||
typedef void (*Teakra_AudioCallback)(void* userdata, int16_t samples[2]);
|
||||
|
||||
typedef uint8_t (*Teakra_AHBMReadCallback8)(void* userdata, uint32_t address);
|
||||
typedef void (*Teakra_AHBMWriteCallback8)(void* userdata, uint32_t address, uint8_t value);
|
||||
|
||||
typedef uint16_t (*Teakra_AHBMReadCallback16)(void* userdata, uint32_t address);
|
||||
typedef void (*Teakra_AHBMWriteCallback16)(void* userdata, uint32_t address, uint16_t value);
|
||||
|
||||
typedef uint32_t (*Teakra_AHBMReadCallback32)(void* userdata, uint32_t address);
|
||||
typedef void (*Teakra_AHBMWriteCallback32)(void* userdata, uint32_t address, uint32_t value);
|
||||
|
||||
TeakraContext* Teakra_Create();
|
||||
void Teakra_Destroy(TeakraContext* context);
|
||||
void Teakra_Reset(TeakraContext* context);
|
||||
uint8_t* Teakra_GetDspMemory(TeakraContext* context);
|
||||
|
||||
int Teakra_SendDataIsEmpty(const TeakraContext* context, uint8_t index);
|
||||
void Teakra_SendData(TeakraContext* context, uint8_t index, uint16_t value);
|
||||
int Teakra_RecvDataIsReady(const TeakraContext* context, uint8_t index);
|
||||
uint16_t Teakra_RecvData(TeakraContext* context, uint8_t index);
|
||||
uint16_t Teakra_PeekRecvData(TeakraContext* context, uint8_t index);
|
||||
void Teakra_SetRecvDataHandler(TeakraContext* context, uint8_t index,
|
||||
Teakra_InterruptCallback handler, void* userdata);
|
||||
|
||||
void Teakra_SetSemaphore(TeakraContext* context, uint16_t value);
|
||||
void Teakra_ClearSemaphore(TeakraContext* context, uint16_t value);
|
||||
void Teakra_MaskSemaphore(TeakraContext* context, uint16_t value);
|
||||
void Teakra_SetSemaphoreHandler(TeakraContext* context, Teakra_InterruptCallback handler,
|
||||
void* userdata);
|
||||
uint16_t Teakra_GetSemaphore(const TeakraContext* context);
|
||||
|
||||
uint16_t Teakra_ProgramRead(TeakraContext* context, uint32_t address);
|
||||
void Teakra_ProgramWrite(TeakraContext* context, uint32_t address, uint16_t value);
|
||||
uint16_t Teakra_DataRead(TeakraContext* context, uint16_t address, bool bypass_mmio);
|
||||
void Teakra_DataWrite(TeakraContext* context, uint16_t address, uint16_t value, bool bypass_mmio);
|
||||
uint16_t Teakra_DataReadA32(TeakraContext* context, uint32_t address);
|
||||
void Teakra_DataWriteA32(TeakraContext* context, uint32_t address, uint16_t value);
|
||||
uint16_t Teakra_MMIORead(TeakraContext* context, uint16_t address);
|
||||
void Teakra_MMIOWrite(TeakraContext* context, uint16_t address, uint16_t value);
|
||||
|
||||
uint16_t Teakra_DMAChan0GetSrcHigh(TeakraContext* context);
|
||||
uint16_t Teakra_DMAChan0GetDstHigh(TeakraContext* context);
|
||||
|
||||
uint16_t Teakra_AHBMGetUnitSize(TeakraContext* context, uint16_t i);
|
||||
uint16_t Teakra_AHBMGetDirection(TeakraContext* context, uint16_t i);
|
||||
uint16_t Teakra_AHBMGetDmaChannel(TeakraContext* context, uint16_t i);
|
||||
|
||||
uint16_t Teakra_AHBMRead16(TeakraContext* context, uint32_t addr);
|
||||
void Teakra_AHBMWrite16(TeakraContext* context, uint32_t addr, uint16_t value);
|
||||
uint16_t Teakra_AHBMRead32(TeakraContext* context, uint32_t addr);
|
||||
void Teakra_AHBMWrite32(TeakraContext* context, uint32_t addr, uint32_t value);
|
||||
|
||||
|
||||
void Teakra_Run(TeakraContext* context, unsigned cycle);
|
||||
|
||||
void Teakra_SetAHBMCallback(TeakraContext* context,
|
||||
Teakra_AHBMReadCallback8 read8 , Teakra_AHBMWriteCallback8 write8 ,
|
||||
Teakra_AHBMReadCallback16 read16, Teakra_AHBMWriteCallback16 write16,
|
||||
Teakra_AHBMReadCallback32 read32, Teakra_AHBMWriteCallback32 write32,
|
||||
void* userdata);
|
||||
|
||||
|
||||
void Teakra_SetAudioCallback(TeakraContext* context, Teakra_AudioCallback callback, void* userdata);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
68
third_party/teakra/src/CMakeLists.txt
vendored
Normal file
68
third_party/teakra/src/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
include(CreateDirectoryGroups)
|
||||
|
||||
add_library(teakra
|
||||
../include/teakra/disassembler.h
|
||||
../include/teakra/teakra.h
|
||||
../include/teakra/impl/register.h
|
||||
ahbm.cpp
|
||||
ahbm.h
|
||||
apbp.cpp
|
||||
apbp.h
|
||||
bit.h
|
||||
btdmp.cpp
|
||||
btdmp.h
|
||||
common_types.h
|
||||
crash.h
|
||||
decoder.h
|
||||
disassembler.cpp
|
||||
dma.cpp
|
||||
dma.h
|
||||
timer.cpp
|
||||
timer.h
|
||||
icu.h
|
||||
interpreter.h
|
||||
matcher.h
|
||||
memory_interface.cpp
|
||||
memory_interface.h
|
||||
mmio.cpp
|
||||
mmio.h
|
||||
operand.h
|
||||
parser.cpp
|
||||
processor.cpp
|
||||
processor.h
|
||||
shared_memory.h
|
||||
teakra.cpp
|
||||
test.h
|
||||
test_generator.cpp
|
||||
test_generator.h
|
||||
)
|
||||
|
||||
create_target_directory_groups(teakra)
|
||||
|
||||
target_link_libraries(teakra PRIVATE Threads::Threads)
|
||||
target_include_directories(teakra
|
||||
PUBLIC
|
||||
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
PRIVATE . ../include/teakra/impl)
|
||||
target_compile_options(teakra PRIVATE ${TEAKRA_CXX_FLAGS})
|
||||
|
||||
add_library(teakra_c
|
||||
../include/teakra/disassembler_c.h
|
||||
../include/teakra/teakra_c.h
|
||||
disassembler_c.cpp
|
||||
teakra_c.cpp
|
||||
)
|
||||
target_link_libraries(teakra_c PRIVATE teakra)
|
||||
|
||||
set_target_properties(teakra teakra_c PROPERTIES VERSION 0)
|
||||
|
||||
if (TEAKRA_BUILD_TOOLS)
|
||||
add_subdirectory(coff_reader)
|
||||
add_subdirectory(dsp1_reader)
|
||||
add_subdirectory(test_generator)
|
||||
add_subdirectory(test_verifier)
|
||||
add_subdirectory(mod_test_generator)
|
||||
add_subdirectory(step2_test_generator)
|
||||
add_subdirectory(makedsp1)
|
||||
endif()
|
29
third_party/teakra/src/README.md
vendored
Normal file
29
third_party/teakra/src/README.md
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
## Content
|
||||
|
||||
- main library
|
||||
- [processor related](processor_general.md)
|
||||
- operand: defines basic operand types used in instructions
|
||||
- matcher and decoder: decodes binary instructions into opcodes and operands
|
||||
- disassembler: translate binary instructions to (pseudo-)assembly.
|
||||
- parser: translate (pseudo-)assembly to binary instructions
|
||||
- interpreter: executes instructions
|
||||
- [register](register.md): defines all register states in the processor
|
||||
- processor: wrapper of interpreter and register as a processor emulator
|
||||
- test_generator: generates test cases information for the instruction set
|
||||
- peripherals
|
||||
- [AHBM](ahbm.md): interface for accessing external memory (DSi/3DS main memory)
|
||||
- [APBP](apbp.md): interface for communication with CPU (ARM in DSi/3DS)
|
||||
- [BTDMP](btdmp.md): audio input/output ports
|
||||
- [DMA](dma.md): engine for transferring large data between DSP memory and external memory
|
||||
- [ICU](icu.md): interrupt controller unit
|
||||
- [timer](timer.md)
|
||||
- [MMIO](mmio.md): I/O ports for all peripherals
|
||||
- shared_memory: the DSP working memory
|
||||
- [memory_interface](miu.md): the memory space exposed to the processor and related control
|
||||
- Tools
|
||||
- coff_reader: disassembles and parses symbols COFF files leaked by some DSi applications
|
||||
- dsp1_reader: disassembles DSP1 files, DSP binary for 3DS applications
|
||||
- makedsp1: assembles DSP1 files
|
||||
- test_generator: generate random test cases for processor instructions.
|
||||
- mod_test_generator & step2_test_generator: similar to test_generator, but dedicated for mod/step2 related instructions
|
||||
- test_verifier: verify test cases on the interpreter against the result generated from 3DS
|
159
third_party/teakra/src/ahbm.cpp
vendored
Normal file
159
third_party/teakra/src/ahbm.cpp
vendored
Normal file
|
@ -0,0 +1,159 @@
|
|||
#include <cstdio>
|
||||
#include "ahbm.h"
|
||||
|
||||
namespace Teakra {
|
||||
|
||||
void Ahbm::Reset() {
|
||||
busy_flag = 0;
|
||||
channels = {};
|
||||
}
|
||||
|
||||
unsigned Ahbm::Channel::GetBurstSize() {
|
||||
switch (burst_size) {
|
||||
case Ahbm::BurstSize::X1:
|
||||
return 1;
|
||||
case Ahbm::BurstSize::X4:
|
||||
return 4;
|
||||
case Ahbm::BurstSize::X8:
|
||||
return 8;
|
||||
default:
|
||||
std::printf("Unknown burst size %04X\n", static_cast<u16>(burst_size));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
u16 Ahbm::Read16(u16 channel, u32 address) {
|
||||
u32 value32 = Read32(channel, address);
|
||||
if ((address & 1) == 0) {
|
||||
return (u16)(value32 & 0xFFFF);
|
||||
} else {
|
||||
return (u16)(value32 >> 16);
|
||||
}
|
||||
}
|
||||
u32 Ahbm::Read32(u16 channel, u32 address) {
|
||||
if (channels[channel].direction != Direction::Read) {
|
||||
std::printf("Wrong direction!\n");
|
||||
}
|
||||
|
||||
if (channels[channel].burst_queue.empty()) {
|
||||
u32 current = address;
|
||||
unsigned size = channels[channel].GetBurstSize();
|
||||
for (unsigned i = 0; i < size; ++i) {
|
||||
u32 value = 0;
|
||||
switch (channels[channel].unit_size) {
|
||||
case UnitSize::U8:
|
||||
value = read_external8(current);
|
||||
if ((current & 1) == 1) {
|
||||
value <<= 8; // this weird bahiviour is hwtested
|
||||
}
|
||||
current += 1;
|
||||
break;
|
||||
case UnitSize::U16: {
|
||||
u32 current_masked = current & 0xFFFFFFFE;
|
||||
value = read_external16(current_masked);
|
||||
current += 2;
|
||||
break;
|
||||
}
|
||||
case UnitSize::U32: {
|
||||
u32 current_masked = current & 0xFFFFFFFC;
|
||||
value = read_external32(current_masked);
|
||||
current += 4;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
std::printf("Unknown unit size %04X\n",
|
||||
static_cast<u16>(channels[channel].unit_size));
|
||||
break;
|
||||
}
|
||||
channels[channel].burst_queue.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
u32 value = channels[channel].burst_queue.front();
|
||||
channels[channel].burst_queue.pop();
|
||||
return value;
|
||||
}
|
||||
void Ahbm::Write16(u16 channel, u32 address, u16 value) {
|
||||
WriteInternal(channel, address, value);
|
||||
}
|
||||
void Ahbm::Write32(u16 channel, u32 address, u32 value) {
|
||||
if ((address & 1) == 1) {
|
||||
value >>= 16; // this weird behaviour is hwtested
|
||||
}
|
||||
WriteInternal(channel, address, value);
|
||||
}
|
||||
|
||||
void Ahbm::WriteInternal(u16 channel, u32 address, u32 value) {
|
||||
if (channels[channel].direction != Direction::Write) {
|
||||
std::printf("Wrong direction!\n");
|
||||
}
|
||||
|
||||
if (channels[channel].burst_queue.empty()) {
|
||||
channels[channel].write_burst_start = address;
|
||||
}
|
||||
|
||||
channels[channel].burst_queue.push(value);
|
||||
if (channels[channel].burst_queue.size() >= channels[channel].GetBurstSize()) {
|
||||
u32 current = channels[channel].write_burst_start;
|
||||
while (!channels[channel].burst_queue.empty()) {
|
||||
u32 value32 = channels[channel].burst_queue.front();
|
||||
channels[channel].burst_queue.pop();
|
||||
switch (channels[channel].unit_size) {
|
||||
case UnitSize::U8: {
|
||||
// this weird behaviour is hwtested
|
||||
u8 value8 = ((current & 1) == 1) ? (u8)(value32 >> 8) : (u8)value32;
|
||||
write_external8(current, value8);
|
||||
current += 1;
|
||||
break;
|
||||
}
|
||||
case UnitSize::U16: {
|
||||
u32 c0 = current & 0xFFFFFFFE;
|
||||
u32 c1 = c0 + 1;
|
||||
if (c0 >= current) {
|
||||
write_external16(c0, (u16)value32);
|
||||
} else {
|
||||
write_external8(c1, (u8)(value32 >> 8));
|
||||
}
|
||||
current += 2;
|
||||
break;
|
||||
}
|
||||
case UnitSize::U32: {
|
||||
u32 c0 = current & 0xFFFFFFFC;
|
||||
u32 c1 = c0 + 1;
|
||||
u32 c2 = c0 + 2;
|
||||
u32 c3 = c0 + 3;
|
||||
|
||||
if (c0 >= current && c1 >= current && c2 >= current) {
|
||||
write_external32(c0, value32);
|
||||
} else if (c2 >= current) {
|
||||
if (c1 >= current) {
|
||||
write_external8(c1, (u8)(value32 >> 8));
|
||||
}
|
||||
write_external16(c2, (u16)(value32 >> 16));
|
||||
} else {
|
||||
write_external8(c3, (u8)(value32 >> 24));
|
||||
}
|
||||
|
||||
current += 4;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
std::printf("Unknown unit size %04X\n",
|
||||
static_cast<u16>(channels[channel].unit_size));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u16 Ahbm::GetChannelForDma(u16 dma_channel) const {
|
||||
for (u16 channel = 0; channel < channels.size(); ++channel) {
|
||||
if ((channels[channel].dma_channel >> dma_channel) & 1) {
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
std::printf("Could not find AHBM channel for DMA channel %04X\n", dma_channel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Teakra
|
103
third_party/teakra/src/ahbm.h
vendored
Normal file
103
third_party/teakra/src/ahbm.h
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <queue>
|
||||
#include "common_types.h"
|
||||
|
||||
namespace Teakra {
|
||||
|
||||
class Ahbm {
|
||||
public:
|
||||
enum class UnitSize : u16 {
|
||||
U8 = 0,
|
||||
U16 = 1,
|
||||
U32 = 2,
|
||||
};
|
||||
|
||||
enum class BurstSize : u16 {
|
||||
X1 = 0,
|
||||
X4 = 1,
|
||||
X8 = 2,
|
||||
};
|
||||
|
||||
enum class Direction : u16 {
|
||||
Read = 0,
|
||||
Write = 1,
|
||||
};
|
||||
|
||||
void Reset();
|
||||
|
||||
u16 GetBusyFlag() const {
|
||||
return busy_flag;
|
||||
}
|
||||
void SetUnitSize(u16 i, u16 value) {
|
||||
channels[i].unit_size = static_cast<UnitSize>(value);
|
||||
}
|
||||
u16 GetUnitSize(u16 i) const {
|
||||
return static_cast<u16>(channels[i].unit_size);
|
||||
}
|
||||
void SetBurstSize(u16 i, u16 value) {
|
||||
channels[i].burst_size = static_cast<BurstSize>(value);
|
||||
}
|
||||
u16 GetBurstSize(u16 i) const {
|
||||
return static_cast<u16>(channels[i].burst_size);
|
||||
}
|
||||
void SetDirection(u16 i, u16 value) {
|
||||
channels[i].direction = static_cast<Direction>(value);
|
||||
}
|
||||
u16 GetDirection(u16 i) const {
|
||||
return static_cast<u16>(channels[i].direction);
|
||||
}
|
||||
void SetDmaChannel(u16 i, u16 value) {
|
||||
channels[i].dma_channel = value;
|
||||
}
|
||||
u16 GetDmaChannel(u16 i) const {
|
||||
return channels[i].dma_channel;
|
||||
}
|
||||
|
||||
u16 Read16(u16 channel, u32 address);
|
||||
u32 Read32(u16 channel, u32 address);
|
||||
void Write16(u16 channel, u32 address, u16 value);
|
||||
void Write32(u16 channel, u32 address, u32 value);
|
||||
|
||||
u16 GetChannelForDma(u16 dma_channel) const;
|
||||
|
||||
void SetExternalMemoryCallback(
|
||||
std::function<u8 (u32)> read8 , std::function<void(u32, u8 )> write8 ,
|
||||
std::function<u16(u32)> read16, std::function<void(u32, u16)> write16,
|
||||
std::function<u32(u32)> read32, std::function<void(u32, u32)> write32) {
|
||||
|
||||
read_external8 = std::move(read8);
|
||||
write_external8 = std::move(write8);
|
||||
read_external16 = std::move(read16);
|
||||
write_external16 = std::move(write16);
|
||||
read_external32 = std::move(read32);
|
||||
write_external32 = std::move(write32);
|
||||
}
|
||||
|
||||
private:
|
||||
u16 busy_flag = 0;
|
||||
struct Channel {
|
||||
UnitSize unit_size = UnitSize::U8;
|
||||
BurstSize burst_size = BurstSize::X1;
|
||||
Direction direction = Direction::Read;
|
||||
u16 dma_channel = 0;
|
||||
|
||||
std::queue<u32> burst_queue;
|
||||
u32 write_burst_start = 0;
|
||||
unsigned GetBurstSize();
|
||||
};
|
||||
std::array<Channel, 3> channels;
|
||||
|
||||
std::function<u8(u32)> read_external8;
|
||||
std::function<void(u32, u8)> write_external8;
|
||||
std::function<u16(u32)> read_external16;
|
||||
std::function<void(u32, u16)> write_external16;
|
||||
std::function<u32(u32)> read_external32;
|
||||
std::function<void(u32, u32)> write_external32;
|
||||
|
||||
void WriteInternal(u16 channel, u32 address, u32 value);
|
||||
};
|
||||
|
||||
} // namespace Teakra
|
37
third_party/teakra/src/ahbm.md
vendored
Normal file
37
third_party/teakra/src/ahbm.md
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
# AHBM
|
||||
|
||||
## MMIO Layout
|
||||
|
||||
```
|
||||
Status register
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00E0 | | | | | | | | | | | | | |RNE| | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
Applications wait for all bits to be 0 before connecting AHBM to DMA
|
||||
RNE: 1 when the burst queue is not empty
|
||||
|
||||
Channel config (N = 0, 1, 2)
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00E2+N*6| - | | | | | | - | TYPE | | BURST | R |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00E4+N*6| - | E | W | | | | | | | | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00E6+N*6| - | D7| D6| D5| D4| D3| D2| D1| D0|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
R: Applications set this to 1 if BURST is non-zero
|
||||
BURST: burst type
|
||||
- 0: x1
|
||||
- 1: x4
|
||||
- 2: x8
|
||||
- 3: ?
|
||||
TYPE: data type
|
||||
- 0: 8 bit
|
||||
- 1: 16 bit
|
||||
- 2: 32 bit
|
||||
- 3: ?
|
||||
W: Transfer direction
|
||||
- 0: read from external memory
|
||||
- 1: write to external memory
|
||||
E: Applications always set this.
|
||||
D0..D7: Connects to DMA channel 0..7 if set to one
|
||||
```
|
149
third_party/teakra/src/apbp.cpp
vendored
Normal file
149
third_party/teakra/src/apbp.cpp
vendored
Normal file
|
@ -0,0 +1,149 @@
|
|||
#include <array>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include "apbp.h"
|
||||
|
||||
namespace Teakra {
|
||||
class DataChannel {
|
||||
public:
|
||||
void Reset() {
|
||||
ready = false;
|
||||
data = 0;
|
||||
}
|
||||
|
||||
void Send(u16 data) {
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
ready = true;
|
||||
this->data = data;
|
||||
if (disable_interrupt)
|
||||
return;
|
||||
}
|
||||
if (handler)
|
||||
handler();
|
||||
}
|
||||
u16 Recv() {
|
||||
std::lock_guard lock(mutex);
|
||||
ready = false;
|
||||
return data;
|
||||
}
|
||||
u16 Peek() const {
|
||||
std::lock_guard lock(mutex);
|
||||
return data;
|
||||
}
|
||||
bool IsReady() const {
|
||||
std::lock_guard lock(mutex);
|
||||
return ready;
|
||||
}
|
||||
u16 GetDisableInterrupt() const {
|
||||
std::lock_guard lock(mutex);
|
||||
return disable_interrupt;
|
||||
}
|
||||
void SetDisableInterrupt(u16 v) {
|
||||
disable_interrupt = v;
|
||||
}
|
||||
|
||||
std::function<void()> handler;
|
||||
|
||||
private:
|
||||
bool ready = false;
|
||||
u16 data = 0;
|
||||
u16 disable_interrupt = 0;
|
||||
mutable std::mutex mutex;
|
||||
};
|
||||
|
||||
class Apbp::Impl {
|
||||
public:
|
||||
std::array<DataChannel, 3> data_channels;
|
||||
u16 semaphore = 0;
|
||||
u16 semaphore_mask = 0;
|
||||
bool semaphore_master_signal = false;
|
||||
mutable std::recursive_mutex semaphore_mutex;
|
||||
std::function<void()> semaphore_handler;
|
||||
|
||||
void Reset() {
|
||||
for (auto& c : data_channels)
|
||||
c.Reset();
|
||||
semaphore = 0;
|
||||
semaphore_mask = 0;
|
||||
semaphore_master_signal = false;
|
||||
}
|
||||
};
|
||||
|
||||
Apbp::Apbp() : impl(new Impl) {}
|
||||
Apbp::~Apbp() = default;
|
||||
|
||||
void Apbp::Reset() {
|
||||
impl->Reset();
|
||||
}
|
||||
|
||||
void Apbp::SendData(unsigned channel, u16 data) {
|
||||
impl->data_channels[channel].Send(data);
|
||||
}
|
||||
|
||||
u16 Apbp::RecvData(unsigned channel) {
|
||||
return impl->data_channels[channel].Recv();
|
||||
}
|
||||
|
||||
u16 Apbp::PeekData(unsigned channel) const {
|
||||
return impl->data_channels[channel].Peek();
|
||||
}
|
||||
|
||||
bool Apbp::IsDataReady(unsigned channel) const {
|
||||
return impl->data_channels[channel].IsReady();
|
||||
}
|
||||
|
||||
u16 Apbp::GetDisableInterrupt(unsigned channel) const {
|
||||
return impl->data_channels[channel].GetDisableInterrupt();
|
||||
}
|
||||
|
||||
void Apbp::SetDisableInterrupt(unsigned channel, u16 v) {
|
||||
impl->data_channels[channel].SetDisableInterrupt(v);
|
||||
}
|
||||
|
||||
void Apbp::SetDataHandler(unsigned channel, std::function<void()> handler) {
|
||||
impl->data_channels[channel].handler = std::move(handler);
|
||||
}
|
||||
|
||||
void Apbp::SetSemaphore(u16 bits) {
|
||||
std::lock_guard lock(impl->semaphore_mutex);
|
||||
impl->semaphore |= bits;
|
||||
bool new_signal = (impl->semaphore & ~impl->semaphore_mask) != 0;
|
||||
if (new_signal && impl->semaphore_handler) {
|
||||
impl->semaphore_handler();
|
||||
}
|
||||
impl->semaphore_master_signal = impl->semaphore_master_signal || new_signal;
|
||||
}
|
||||
|
||||
void Apbp::ClearSemaphore(u16 bits) {
|
||||
std::lock_guard lock(impl->semaphore_mutex);
|
||||
impl->semaphore &= ~bits;
|
||||
impl->semaphore_master_signal = (impl->semaphore & ~impl->semaphore_mask) != 0;
|
||||
}
|
||||
|
||||
u16 Apbp::GetSemaphore() const {
|
||||
std::lock_guard lock(impl->semaphore_mutex);
|
||||
return impl->semaphore;
|
||||
}
|
||||
|
||||
void Apbp::MaskSemaphore(u16 bits) {
|
||||
std::lock_guard lock(impl->semaphore_mutex);
|
||||
impl->semaphore_mask = bits;
|
||||
}
|
||||
|
||||
u16 Apbp::GetSemaphoreMask() const {
|
||||
std::lock_guard lock(impl->semaphore_mutex);
|
||||
return impl->semaphore_mask;
|
||||
}
|
||||
|
||||
void Apbp::SetSemaphoreHandler(std::function<void()> handler) {
|
||||
std::lock_guard lock(impl->semaphore_mutex);
|
||||
impl->semaphore_handler = std::move(handler);
|
||||
}
|
||||
|
||||
bool Apbp::IsSemaphoreSignaled() const {
|
||||
std::lock_guard lock(impl->semaphore_mutex);
|
||||
return impl->semaphore_master_signal;
|
||||
}
|
||||
} // namespace Teakra
|
36
third_party/teakra/src/apbp.h
vendored
Normal file
36
third_party/teakra/src/apbp.h
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include "common_types.h"
|
||||
|
||||
namespace Teakra {
|
||||
class Apbp {
|
||||
public:
|
||||
Apbp();
|
||||
~Apbp();
|
||||
|
||||
void Reset();
|
||||
|
||||
void SendData(unsigned channel, u16 data);
|
||||
u16 RecvData(unsigned channel);
|
||||
u16 PeekData(unsigned channel) const;
|
||||
bool IsDataReady(unsigned channel) const;
|
||||
u16 GetDisableInterrupt(unsigned channel) const;
|
||||
void SetDisableInterrupt(unsigned channel, u16 v);
|
||||
void SetDataHandler(unsigned channel, std::function<void()> handler);
|
||||
|
||||
void SetSemaphore(u16 bits);
|
||||
void ClearSemaphore(u16 bits);
|
||||
u16 GetSemaphore() const;
|
||||
void MaskSemaphore(u16 bits);
|
||||
u16 GetSemaphoreMask() const;
|
||||
void SetSemaphoreHandler(std::function<void()> handler);
|
||||
|
||||
bool IsSemaphoreSignaled() const;
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
} // namespace Teakra
|
110
third_party/teakra/src/apbp.md
vendored
Normal file
110
third_party/teakra/src/apbp.md
vendored
Normal file
|
@ -0,0 +1,110 @@
|
|||
# APBP
|
||||
|
||||
## MMIO Layout
|
||||
|
||||
```
|
||||
Command/Reply registers
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00C0 | REPLY0 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00C2 | CMD0 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00C4 | REPLY1 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00C6 | CMD1 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00C8 | REPLY2 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00CA | CMD2 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|
||||
REPLY0..2: data sent from DSP to CPU
|
||||
CMD0..2: data received from CPU to DSP
|
||||
|
||||
Semaphore registers
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00CC | SET_SEMAPHORE |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00CE | MASK_SEMAPHORE |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00D0 | ACK_SEMAPHORE |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00D2 | GET_SEMAPHORE |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|
||||
SET_SEMAPHORE: sets semaphore sent from DSP to CPU
|
||||
MASK_SEMAPHORE: masks semaphore interrupt received from CPU to DSP
|
||||
ACK_SEMAPHORE: acknowledges/clears semaphore received from CPU to DSP
|
||||
GET_SEMAPHORE: semaphore received from CPU to DSP
|
||||
|
||||
Config/status registers
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00D4 | |CI2|CI1| |CI0| |END| |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00D6 | | | C2| C1| | | S | C0| R2| R1| R0| | | | | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00D8 | C2| C1| C0| R2| R1| R0| S'|WEM|WFL|RNE|RFL| |PRS|WTU|RTU|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|
||||
END: CPU-side registers endianness. 1 to swap byte order of CPU-side registers
|
||||
R0..R2: 1 when there is data in REPLY0..2
|
||||
C0..C2: 1 when there is data in CMD0..2
|
||||
CI0...CI2: 1 to disable interrupt when CMD0..2 is written by CPU
|
||||
S: 1 when (GET_SEMAPHORE & ~MASK_SEMAPHORE) is non-zero
|
||||
S': similar to S, but for CPU side
|
||||
RTU: CPU-side read transfer underway flag
|
||||
WTU: CPU-side write transfer underway flag
|
||||
PRS: peripheral reset flag
|
||||
RFL: CPU-side read FIFO full flag
|
||||
RNE: CPU-side read FIFO non-empty flag
|
||||
WFL: CPU-side write FIFO full flag
|
||||
WEM: CPU-side write FIFO empty flag
|
||||
* Note 0x00D8 is a mirror of CPU-side register DSP_PSTS
|
||||
```
|
||||
|
||||
## CPU-DSP communication
|
||||
|
||||
APBP is an important port for CPU and DSP to communicate with each other. It mainly contains 3 pairs of symmetrical data channels and a pair of 16-bit symmetrical semaphore channel.
|
||||
|
||||
### Data channels
|
||||
|
||||
When one side writes to a data channel, it sets the 1 to the "data ready" bit (`R0..R2` or `C0..C2`), and fires interrupt on the other side if enabled. When the othersides read from the channel register, it automatically clears the "data ready" bit. If new data is written before the previous one is read, the new data will overwrite the old data and fires another interrupt.
|
||||
|
||||
### Semaphore channels
|
||||
|
||||
There are two 16-bit semaphore channels for two directions, `CPU->DSP` and `DSP->CPU`. Writing to `SET_SEMAPHORE` from side A or `ACK_SEMAPHORE` from side B changes the semaphore value of direction `A->B`. Semaphore value change also changes the corresponding `S` bit (See the calculation above). Changing in mask is also reflected in the `S` bit immediately. Whenever a `0->1` transition for `S` is detected, interrupt is fired on B side.
|
||||
|
||||
## CPU side MMIO
|
||||
|
||||
```
|
||||
Command/Reply registers
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0000 | PDATA |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0004 | PADDR |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0008 | DATA_SPACE |RI2|RI1|RI0|IWE|IWF|IRE|IRF|STR| BURST |INC|RST|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x000C | C2| C1| C0| R2| R1| R0| S'|WEM|WFL|RNE|RFL| |PRS|WTU|RTU|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0010 | SET_SEMAPHORE' |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0014 | MASK_SEMAPHORE' |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0018 | ACK_SEMAPHORE' |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x001C | GET_SEMAPHORE' |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0020 | CMD0 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0024 | REPLY0 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0028 | CMD1 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x002C | REPLY1 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0030 | CMD2 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0034 | REPLY2 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
```
|
26
third_party/teakra/src/bit.h
vendored
Normal file
26
third_party/teakra/src/bit.h
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
namespace std20 {
|
||||
|
||||
// A simple (and not very correct) implementation of C++20's std::log2p1
|
||||
template <class T>
|
||||
constexpr T log2p1(T x) noexcept {
|
||||
static_assert(std::is_integral_v<T> && std::is_unsigned_v<T>);
|
||||
if (x == 0)
|
||||
return 0;
|
||||
#ifdef _MSC_VER
|
||||
unsigned long index = 0;
|
||||
_BitScanReverse64(&index, x);
|
||||
return static_cast<T>(index) + 1;
|
||||
#else
|
||||
return static_cast<T>(std::numeric_limits<unsigned long long>::digits - __builtin_clzll(x));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace std20
|
97
third_party/teakra/src/btdmp.cpp
vendored
Normal file
97
third_party/teakra/src/btdmp.cpp
vendored
Normal file
|
@ -0,0 +1,97 @@
|
|||
#include <string>
|
||||
#include "btdmp.h"
|
||||
#include "crash.h"
|
||||
|
||||
namespace Teakra {
|
||||
|
||||
Btdmp::Btdmp(CoreTiming& core_timing) {
|
||||
core_timing.RegisterCallbacks(this);
|
||||
}
|
||||
|
||||
Btdmp::~Btdmp() = default;
|
||||
|
||||
void Btdmp::Reset() {
|
||||
transmit_clock_config = 0;
|
||||
transmit_period = 4096;
|
||||
transmit_timer = 0;
|
||||
transmit_enable = 0;
|
||||
transmit_empty = true;
|
||||
transmit_full = false;
|
||||
transmit_queue = {};
|
||||
}
|
||||
|
||||
void Btdmp::Tick() {
|
||||
if (transmit_enable) {
|
||||
++transmit_timer;
|
||||
if (transmit_timer >= transmit_period) {
|
||||
transmit_timer = 0;
|
||||
std::array<std::int16_t, 2> sample;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (transmit_queue.empty()) {
|
||||
std::printf("BTDMP: transmit buffer underrun\n");
|
||||
sample[i] = 0;
|
||||
} else {
|
||||
sample[i] = static_cast<s16>(transmit_queue.front());
|
||||
transmit_queue.pop();
|
||||
transmit_empty = transmit_queue.empty();
|
||||
transmit_full = false;
|
||||
if (transmit_empty) {
|
||||
interrupt_handler();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (audio_callback) {
|
||||
audio_callback(sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u64 Btdmp::GetMaxSkip() const {
|
||||
if (!transmit_enable || transmit_queue.empty()) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
u64 ticks = 0;
|
||||
if (transmit_timer < transmit_period) {
|
||||
// number of ticks before the tick of the next transmit
|
||||
ticks += transmit_period - transmit_timer - 1;
|
||||
}
|
||||
|
||||
// number of ticks from the next transmit to the one just before the transmit that empties
|
||||
// the buffer
|
||||
ticks += ((transmit_queue.size() + 1) / 2 - 1) * transmit_period;
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
void Btdmp::Skip(u64 ticks) {
|
||||
if (!transmit_enable)
|
||||
return;
|
||||
|
||||
if (transmit_timer >= transmit_period)
|
||||
transmit_timer = 0;
|
||||
|
||||
u64 future_timer = transmit_timer + ticks;
|
||||
u64 cycles = future_timer / transmit_period;
|
||||
transmit_timer = (u16)(future_timer % transmit_period);
|
||||
|
||||
for (u64 c = 0; c < cycles; ++c) {
|
||||
std::array<std::int16_t, 2> sample;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (transmit_queue.empty()) {
|
||||
sample[i] = 0;
|
||||
} else {
|
||||
sample[i] = static_cast<s16>(transmit_queue.front());
|
||||
transmit_queue.pop();
|
||||
ASSERT(!transmit_queue.empty());
|
||||
transmit_full = false;
|
||||
}
|
||||
}
|
||||
if (audio_callback) {
|
||||
audio_callback(sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Teakra
|
99
third_party/teakra/src/btdmp.h
vendored
Normal file
99
third_party/teakra/src/btdmp.h
vendored
Normal file
|
@ -0,0 +1,99 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <queue>
|
||||
#include "common_types.h"
|
||||
#include "core_timing.h"
|
||||
|
||||
namespace Teakra {
|
||||
|
||||
class Btdmp : public CoreTiming::Callbacks {
|
||||
public:
|
||||
Btdmp(CoreTiming& core_timing);
|
||||
~Btdmp();
|
||||
|
||||
void Reset();
|
||||
|
||||
void SetTransmitClockConfig(u16 value) {
|
||||
transmit_clock_config = value;
|
||||
}
|
||||
|
||||
u16 GetTransmitClockConfig() const {
|
||||
return transmit_clock_config;
|
||||
}
|
||||
|
||||
void SetTransmitPeriod(u16 value) {
|
||||
transmit_period = value;
|
||||
}
|
||||
|
||||
u16 GetTransmitPeriod() const {
|
||||
return transmit_period;
|
||||
}
|
||||
|
||||
void SetTransmitEnable(u16 value) {
|
||||
transmit_enable = value;
|
||||
}
|
||||
|
||||
u16 GetTransmitEnable() const {
|
||||
return transmit_enable;
|
||||
}
|
||||
|
||||
u16 GetTransmitEmpty() const {
|
||||
return transmit_empty;
|
||||
}
|
||||
|
||||
u16 GetTransmitFull() const {
|
||||
return transmit_full;
|
||||
}
|
||||
|
||||
void Send(u16 value) {
|
||||
if (transmit_queue.size() == 16) {
|
||||
std::printf("BTDMP: transmit buffer overrun\n");
|
||||
} else {
|
||||
transmit_queue.push(value);
|
||||
transmit_empty = false;
|
||||
transmit_full = transmit_queue.size() == 16;
|
||||
}
|
||||
}
|
||||
|
||||
void SetTransmitFlush(u16 value) {
|
||||
transmit_queue = {};
|
||||
transmit_empty = true;
|
||||
transmit_full = false;
|
||||
}
|
||||
|
||||
u16 GetTransmitFlush() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Tick() override;
|
||||
u64 GetMaxSkip() const override;
|
||||
void Skip(u64 ticks) override;
|
||||
|
||||
void SetAudioCallback(std::function<void(std::array<std::int16_t, 2>)> callback) {
|
||||
audio_callback = std::move(callback);
|
||||
}
|
||||
|
||||
void SetInterruptHandler(std::function<void()> handler) {
|
||||
interrupt_handler = std::move(handler);
|
||||
}
|
||||
|
||||
private:
|
||||
// TODO: figure out the relation between clock_config and period.
|
||||
// Default to period = 4096 for now which every game uses
|
||||
u16 transmit_clock_config = 0;
|
||||
u16 transmit_period = 4096;
|
||||
u16 transmit_timer = 0;
|
||||
u16 transmit_enable = 0;
|
||||
bool transmit_empty = true;
|
||||
bool transmit_full = false;
|
||||
std::queue<u16> transmit_queue;
|
||||
std::function<void(std::array<std::int16_t, 2>)> audio_callback;
|
||||
std::function<void()> interrupt_handler;
|
||||
|
||||
class BtdmpTimingCallbacks;
|
||||
};
|
||||
|
||||
} // namespace Teakra
|
84
third_party/teakra/src/btdmp.md
vendored
Normal file
84
third_party/teakra/src/btdmp.md
vendored
Normal file
|
@ -0,0 +1,84 @@
|
|||
# BTDMP
|
||||
|
||||
## MMIO Layout
|
||||
|
||||
```
|
||||
Receive config registers
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0280 | | | | | | |RIR| | | | | | | | | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0282 | | | | | | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0284 | "0004"? |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0286 | "0021"? |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0288 | "0000"? |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x028A | "0000"? |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x028C | "0000"? |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x028E | ? |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0290 | ? |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
...
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x029E |RE | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|
||||
RIR: enable IRQ for receive if 1
|
||||
RE: enable receive if 1
|
||||
|
||||
Transmit config registers
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x02A0 | | | | | | |TIR| | | | | | | | | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x02A2 | | | | | | | <- clock related, "1004"
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x02A4 | "0004"? |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x02A6 | "0021"? |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x02A8 | "0000"? |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x02AA | "0000"? |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x02AC | "0000"? |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x02AE | ? |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x02B0 | ? |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
...
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x02BE |TE | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|
||||
TIR: enable IRQ for transmit if 1
|
||||
TE: enable transmit if 1
|
||||
|
||||
Receive/transmit status and data registers
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x02C0 | |RE |RF | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x02C2 | |TE |TF | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x02C4 | FIFO_RECEIVE |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x02C6 | FIFO_TRANSMIT |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x02C8 | ? |RFL| |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x02CA | |TFL| |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|
||||
RF: 1 if receive buffer is full
|
||||
RE: 1 if receive buffer is empty
|
||||
TF: 1 if transmit buffer is full
|
||||
TE: 1 if transmit buffer is empty
|
||||
RFL: flush receive buffer
|
||||
TFL: flush transmit buffer. Application set and then spin waits on this flag
|
||||
|
||||
```
|
10
third_party/teakra/src/coff_reader/CMakeLists.txt
vendored
Normal file
10
third_party/teakra/src/coff_reader/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
include(CreateDirectoryGroups)
|
||||
|
||||
add_executable(coff_reader
|
||||
coff.h
|
||||
main.cpp
|
||||
)
|
||||
create_target_directory_groups(coff_reader)
|
||||
target_link_libraries(coff_reader PRIVATE teakra)
|
||||
target_include_directories(coff_reader PRIVATE .)
|
||||
target_compile_options(coff_reader PRIVATE ${TEAKRA_CXX_FLAGS})
|
367
third_party/teakra/src/coff_reader/coff.h
vendored
Normal file
367
third_party/teakra/src/coff_reader/coff.h
vendored
Normal file
|
@ -0,0 +1,367 @@
|
|||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
|
||||
struct NameOrIndex {
|
||||
u8 value[8];
|
||||
std::variant<std::string, u32> Get() {
|
||||
u32 temp;
|
||||
std::memcpy(&temp, value, 4);
|
||||
if (temp == 0) {
|
||||
std::memcpy(&temp, value + 4, 4);
|
||||
return temp;
|
||||
}
|
||||
std::string ret(value, value + 8);
|
||||
size_t pos = ret.find('\0');
|
||||
if (pos != std::string::npos)
|
||||
ret.erase(pos);
|
||||
return ret;
|
||||
}
|
||||
std::string GetString(std::FILE* file, u32 offset) {
|
||||
auto v = Get();
|
||||
if (v.index() == 1) {
|
||||
std::fseek(file, offset + std::get<1>(v), SEEK_SET);
|
||||
char c;
|
||||
std::string ret;
|
||||
while (true) {
|
||||
if (std::fread(&c, 1, 1, file) != 1)
|
||||
throw "unexpected end";
|
||||
if (c == '\0')
|
||||
break;
|
||||
ret += c;
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
return std::get<0>(v);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Header {
|
||||
u16 magic;
|
||||
u16 num_section;
|
||||
u32 time;
|
||||
u32 offset_symbol;
|
||||
u32 num_symbol;
|
||||
u16 optheader_size;
|
||||
u16 flags;
|
||||
};
|
||||
static_assert(sizeof(Header) == 20);
|
||||
|
||||
struct SectionHeader {
|
||||
NameOrIndex name;
|
||||
u32 prog_addr;
|
||||
u32 data_addr;
|
||||
u32 size;
|
||||
u32 offset_data;
|
||||
u32 offset_rel;
|
||||
u32 offset_line;
|
||||
u16 num_rel;
|
||||
u16 num_line;
|
||||
u32 flags;
|
||||
};
|
||||
static_assert(sizeof(SectionHeader) == 40);
|
||||
|
||||
namespace SFlag {
|
||||
constexpr u32 Exec = 0x0001;
|
||||
constexpr u32 Unk2 = 0x0002;
|
||||
constexpr u32 Prog = 0x0008;
|
||||
constexpr u32 Data = 0x0010;
|
||||
constexpr u32 RegionMask = Prog | Data;
|
||||
constexpr u32 Moni = 0x0020;
|
||||
constexpr u32 Un40 = 0x0040;
|
||||
constexpr u32 Dupl = 0x0080;
|
||||
constexpr u32 U200 = 0x0200;
|
||||
} // namespace SFlag
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct Symbol {
|
||||
NameOrIndex name;
|
||||
u32 value;
|
||||
u16 section_index;
|
||||
u16 type;
|
||||
u8 storage;
|
||||
u8 num_aux;
|
||||
};
|
||||
static_assert(sizeof(Symbol) == 18);
|
||||
|
||||
inline const std::unordered_map<u32, const char*> storage_names{
|
||||
{0, "label"}, {1, "auto"}, {2, "external"}, {3, "static"},
|
||||
{4, "register"}, {8, "struct"}, {9, "arg"}, {10, "struct-tag"},
|
||||
{11, "union"}, {12, "union-tag"}, {13, "typedef"}, {15, "enum-tag"},
|
||||
{16, "enum"}, {18, "bitfield"}, {19, "auto-arg"}, {98, "start"},
|
||||
{99, "end"}, {100, "block"}, {101, "func"}, {102, "struct-size"},
|
||||
{103, "file"}, {107, "ar"}, {108, "ar"}, {109, "ar"},
|
||||
{110, "ar"}, {111, "ar"}, {112, "ar"}, {255, "physical-function-end"},
|
||||
};
|
||||
|
||||
/*
|
||||
Storage:
|
||||
0 labels? | section+address | type = 0 | no aux
|
||||
1 auto variable | address, rel to sp? | variable type | variable aux
|
||||
2 externals | section+address | variable type | variable aux
|
||||
3 statics | section+address | variable type | variable aux
|
||||
4 register variable | address, abs? | variable type | variable aux
|
||||
8 structure member | address | variable type | variable aux
|
||||
9 function argument | address, ? | variable type | variable aux
|
||||
10 structure tag | debug/no address | type = 8 (structure) | aux = 1
|
||||
11 union member | address = 0? | type = 5 (long) or 8 (structure) | aux = 0 (long) or 1 (structure)
|
||||
12 union tag | debug/no address | type = 9 (union) | aux = 1
|
||||
13 typedef | debug/no address | type = 5 (long) or 8 (structure) | aux = 0 (long) or 1 (structure)
|
||||
15 enum tag | debug/no address | type = 10 (enum) | aux = 1
|
||||
16 enum member | abs value | type = 11 (enum member) | aux = 0
|
||||
18 bitfield | address | type = 5 (long) or 15 ulong | aux = 1
|
||||
19 auto arg | section+address | type = 17 | aux = 0
|
||||
98 function start | section + address | type = 0 | aux = 0
|
||||
99 function end | section + address | type = 0 | aux = 0
|
||||
100 .eb/.bb | section + address | type = 0 | aux = 1
|
||||
101 .ef/.bf | section + address | type = 0 | aux = 1
|
||||
102 struct end | address | type = 0 | aux = 1
|
||||
103 .file | debug value | type = 1 or 2 | aux = 1
|
||||
107 .ar | section + address | type = => ar0 value | aux = 0
|
||||
108 .ar | section + address | type = => ar1 value | aux = 0
|
||||
109 .ar | section + address | type = => arp0 value | aux = 0
|
||||
110 .ar | section + address | type = => arp1 value | aux = 0
|
||||
111 .ar | section + address | type = => arp2 value | aux = 0
|
||||
112 .ar | section + address | type = => arp3 value | aux = 0
|
||||
255 physical function end | section + address | type = 0 | aux = 0
|
||||
*/
|
||||
|
||||
struct Line {
|
||||
u32 symbol_index_or_addr;
|
||||
u16 line_number;
|
||||
};
|
||||
static_assert(sizeof(Line) == 6);
|
||||
|
||||
struct Relocation {
|
||||
u32 addr;
|
||||
u32 symbol;
|
||||
u32 type;
|
||||
};
|
||||
static_assert(sizeof(Relocation) == 12); // not 10 bytes!
|
||||
#pragma pack(pop)
|
||||
|
||||
class Coff {
|
||||
|
||||
public:
|
||||
Coff(std::FILE* in) {
|
||||
fseek(in, 0, SEEK_SET);
|
||||
Header header;
|
||||
if (fread(&header, sizeof(header), 1, in) != 1)
|
||||
throw "failed to read header";
|
||||
u32 string_offset = header.offset_symbol + header.num_symbol * sizeof(Symbol);
|
||||
|
||||
sections.resize(header.num_section);
|
||||
u32 expected = 0;
|
||||
for (u32 i = 0; i < header.num_section; ++i) {
|
||||
fseek(in, sizeof(header) + header.optheader_size + i * sizeof(SectionHeader), SEEK_SET);
|
||||
SectionHeader sheader;
|
||||
if (fread(&sheader, sizeof(sheader), 1, in) != 1)
|
||||
throw "failed to read sheader";
|
||||
sections[i].name = sheader.name.GetString(in, string_offset);
|
||||
sections[i].prog_addr = sheader.prog_addr;
|
||||
sections[i].data_addr = sheader.data_addr;
|
||||
if (expected == 0)
|
||||
expected = sheader.offset_data;
|
||||
if (expected != sheader.offset_data) {
|
||||
throw "";
|
||||
}
|
||||
sections[i].data.resize(sheader.size);
|
||||
if ((sheader.flags & SFlag::Dupl) == 0) {
|
||||
fseek(in, sheader.offset_data, SEEK_SET);
|
||||
if (sheader.size != 0)
|
||||
if (fread(sections[i].data.data(), sheader.size, 1, in) != 1)
|
||||
throw "failed to read section";
|
||||
expected += sheader.size;
|
||||
}
|
||||
sections[i].num_rel = sheader.num_rel;
|
||||
sections[i].flags = sheader.flags;
|
||||
if ((sections[i].flags & SFlag::RegionMask) == 0)
|
||||
throw "no region type";
|
||||
if ((sections[i].flags & SFlag::RegionMask) == SFlag::Prog) {
|
||||
if (sections[i].data_addr != 0)
|
||||
throw "prog section has data addr";
|
||||
} else if ((sections[i].flags & SFlag::RegionMask) == SFlag::Data) {
|
||||
if (sections[i].prog_addr != 0)
|
||||
throw "data section has prog addr";
|
||||
}
|
||||
|
||||
fseek(in, sheader.offset_line, SEEK_SET);
|
||||
u32 current_symbol = 0;
|
||||
for (u16 j = 0; j < sheader.num_line; ++j) {
|
||||
Line line;
|
||||
if (fread(&line, sizeof(line), 1, in) != 1)
|
||||
throw "failed to read line";
|
||||
if (line.line_number == 0) {
|
||||
current_symbol = line.symbol_index_or_addr;
|
||||
} else {
|
||||
u32 addr = line.symbol_index_or_addr;
|
||||
if (addr < sections[i].prog_addr ||
|
||||
addr >= sections[i].prog_addr + sections[i].data.size() / 2) {
|
||||
if (addr != 0xFFFFFFFF && addr != 0xFFFFFFFE &&
|
||||
addr != 0xFFFFFFFD) // unknown magic
|
||||
throw "line out of range";
|
||||
}
|
||||
sections[i].line_numbers[addr].emplace_back(
|
||||
Section::LineNumber{current_symbol, line.line_number});
|
||||
}
|
||||
}
|
||||
|
||||
fseek(in, sheader.offset_rel, SEEK_SET);
|
||||
// printf("\n");
|
||||
for (u16 j = 0; j < sheader.num_rel; ++j) {
|
||||
Relocation relocation;
|
||||
if (fread(&relocation, sizeof(relocation), 1, in) != 1)
|
||||
throw "failed to read relocation";
|
||||
// printf("%08X, %08X, %08X\n", relocation.addr, relocation.symbol,
|
||||
// relocation.type);
|
||||
sections[i].relocations[relocation.addr].push_back(relocation);
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < header.num_symbol; ++i) {
|
||||
Symbol symbol;
|
||||
fseek(in, header.offset_symbol + i * sizeof(symbol), SEEK_SET);
|
||||
if (fread(&symbol, sizeof(symbol), 1, in) != 1)
|
||||
throw "failed to read symbol";
|
||||
i += symbol.num_aux;
|
||||
printf("%s\n", symbol.name.GetString(in, string_offset).c_str());
|
||||
printf("value = %08X, section = %04X, type = %04X, storage = %02X, aux = %02X\n",
|
||||
symbol.value, symbol.section_index, symbol.type, symbol.storage, symbol.num_aux);
|
||||
SymbolEx symbol_ex;
|
||||
symbol_ex.name = symbol.name.GetString(in, string_offset);
|
||||
if (symbol.section_index > 0 && symbol.section_index < 0x7FFF) {
|
||||
u16 section_index = symbol.section_index - 1;
|
||||
symbol_ex.region =
|
||||
sections[section_index].flags & SFlag::Prog ? SymbolEx::Prog : SymbolEx::Data;
|
||||
symbol_ex.value = symbol.value + (sections[section_index].flags & SFlag::Prog
|
||||
? sections[section_index].prog_addr
|
||||
: sections[section_index].data_addr);
|
||||
|
||||
} else {
|
||||
symbol_ex.region = SymbolEx::Absolute;
|
||||
symbol_ex.value = symbol.value;
|
||||
}
|
||||
|
||||
symbol_ex.storage = symbol.storage;
|
||||
symbol_ex.type = symbol.type;
|
||||
|
||||
symbols.push_back(symbol_ex);
|
||||
|
||||
if (symbols.back().region == SymbolEx::Prog ||
|
||||
symbols.back().region == SymbolEx::Data) {
|
||||
symbols_lut[symbols.back().value].push_back(symbols.size() - 1);
|
||||
}
|
||||
|
||||
symbol_ex.region = SymbolEx::Aux;
|
||||
for (u16 aux = 0; aux < symbol.num_aux; ++aux) {
|
||||
symbols.push_back(symbol_ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Merge duplicated sections
|
||||
std::sort(sections.begin(), sections.end(), [](const Section& left, const Section& right) {
|
||||
u32 lm = left.flags & SFlag::Dupl;
|
||||
u32 rm = right.flags & SFlag::Dupl;
|
||||
if (lm == rm) {
|
||||
return left.name < right.name;
|
||||
} else {
|
||||
return lm < rm;
|
||||
}
|
||||
});
|
||||
auto mid = sections.begin() + sections.size() / 2;
|
||||
for (auto a = sections.begin(), b = mid; a != mid; ++a, ++b) {
|
||||
if (a->name != b->name || a->prog_addr != b->prog_addr ||
|
||||
a->data_addr != b->data_addr || a->data.size() != b->data.size() ||
|
||||
(a->flags + SFlag::Dupl) != b->flags)
|
||||
throw "mismatch";
|
||||
if (!b->line_numbers.empty())
|
||||
throw "dup has line numbers";
|
||||
if (b->num_rel != 0)
|
||||
throw "dup has relocations";
|
||||
}
|
||||
sections.resize(sections.size() / 2);
|
||||
|
||||
// Break up multi-region section
|
||||
auto both_begin =
|
||||
std::partition(sections.begin(), sections.end(), [](const Section& section) {
|
||||
return (section.flags & SFlag::RegionMask) != SFlag::RegionMask;
|
||||
});
|
||||
std::vector<Section> copy(both_begin, sections.end());
|
||||
for (auto i = both_begin; i != sections.end(); ++i) {
|
||||
i->flags &= ~SFlag::Prog;
|
||||
i->prog_addr = 0;
|
||||
}
|
||||
for (auto& section : copy) {
|
||||
section.flags &= ~SFlag::Data;
|
||||
section.data_addr = 0;
|
||||
}
|
||||
sections.insert(sections.end(), std::make_move_iterator(copy.begin()),
|
||||
std::make_move_iterator(copy.end()));
|
||||
|
||||
// Sort according to address
|
||||
std::sort(sections.begin(), sections.end(), [](const Section& left, const Section& right) {
|
||||
if ((left.flags & SFlag::RegionMask) == SFlag::Prog) {
|
||||
if ((right.flags & SFlag::RegionMask) == SFlag::Prog) {
|
||||
if (left.prog_addr == right.prog_addr) {
|
||||
return left.data.size() < right.data.size();
|
||||
}
|
||||
return left.prog_addr < right.prog_addr;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if ((right.flags & SFlag::RegionMask) == SFlag::Prog) {
|
||||
return false;
|
||||
} else {
|
||||
if (left.data_addr == right.data_addr) {
|
||||
return left.data.size() < right.data.size();
|
||||
}
|
||||
return left.data_addr < right.data_addr;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
struct Section {
|
||||
std::string name;
|
||||
u32 prog_addr;
|
||||
u32 data_addr;
|
||||
std::vector<u8> data;
|
||||
u16 num_rel;
|
||||
u32 flags;
|
||||
|
||||
struct LineNumber {
|
||||
u32 symbol;
|
||||
u16 line;
|
||||
};
|
||||
std::unordered_map<u32 /*abs addr*/, std::vector<LineNumber>> line_numbers;
|
||||
std::unordered_map<u32 /*rel addr*/, std::vector<Relocation>> relocations;
|
||||
};
|
||||
std::vector<Section> sections;
|
||||
|
||||
struct SymbolEx {
|
||||
std::string name;
|
||||
enum Region {
|
||||
Aux,
|
||||
Absolute,
|
||||
Prog,
|
||||
Data,
|
||||
} region;
|
||||
u32 value;
|
||||
u16 type;
|
||||
u8 storage;
|
||||
};
|
||||
std::vector<SymbolEx> symbols;
|
||||
std::unordered_map<u32 /*abs addr*/, std::vector<std::size_t /*index in symbols*/>> symbols_lut;
|
||||
};
|
132
third_party/teakra/src/coff_reader/main.cpp
vendored
Normal file
132
third_party/teakra/src/coff_reader/main.cpp
vendored
Normal file
|
@ -0,0 +1,132 @@
|
|||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <teakra/disassembler.h>
|
||||
#include "coff.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
printf("Please input a file\n");
|
||||
return -1;
|
||||
}
|
||||
std::FILE* in;
|
||||
std::FILE* out;
|
||||
in = std::fopen(argv[1], "rb");
|
||||
if (!in) {
|
||||
printf("Failed to open input file\n");
|
||||
return -1;
|
||||
}
|
||||
out = std::fopen((argv[1] + std::string(".out")).c_str(), "wt");
|
||||
if (!out) {
|
||||
printf("Failed to open output file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Coff coff(in);
|
||||
|
||||
u32 prog_grow = 0, data_grow = 0;
|
||||
for (const auto& section : coff.sections) {
|
||||
u32 addr;
|
||||
bool is_prog;
|
||||
if ((section.flags & SFlag::RegionMask) == SFlag::Prog) {
|
||||
if (section.prog_addr < prog_grow) {
|
||||
throw "overlap";
|
||||
}
|
||||
is_prog = true;
|
||||
addr = section.prog_addr;
|
||||
prog_grow = section.prog_addr + (u32)section.data.size() / 2;
|
||||
} else {
|
||||
if (section.data_addr < data_grow) {
|
||||
throw "overlap";
|
||||
}
|
||||
is_prog = false;
|
||||
addr = section.data_addr;
|
||||
data_grow = section.data_addr + (u32)section.data.size() / 2;
|
||||
}
|
||||
|
||||
fprintf(out, "\n===Section===\n");
|
||||
fprintf(out, "name: %s\n", section.name.c_str());
|
||||
fprintf(out, "addr: %s.%08X\n", is_prog ? "Prog" : "Data", addr);
|
||||
fprintf(out, "flag: %08X\n\n", section.flags);
|
||||
|
||||
std::vector<u16> data(section.data.size() / 2);
|
||||
std::memcpy(data.data(), section.data.data(), section.data.size());
|
||||
|
||||
for (auto iter = data.begin(); iter != data.end(); ++iter) {
|
||||
u32 current_rel_addr = (u32)(iter - data.begin());
|
||||
u32 current_addr = current_rel_addr + addr;
|
||||
|
||||
if (coff.symbols_lut.count(current_addr)) {
|
||||
const auto& symbol_list = coff.symbols_lut.at(current_addr);
|
||||
auto begin = symbol_list.begin();
|
||||
while (true) {
|
||||
auto symbol_index =
|
||||
std::find_if(begin, symbol_list.end(),
|
||||
[is_prog, current_addr, &coff](const auto symbol_index) {
|
||||
const auto& symbol = coff.symbols[symbol_index];
|
||||
if (symbol.region == Coff::SymbolEx::Absolute ||
|
||||
symbol.region == Coff::SymbolEx::Aux)
|
||||
throw "what";
|
||||
if (symbol.region == Coff::SymbolEx::Prog && !is_prog)
|
||||
return false;
|
||||
if (symbol.region == Coff::SymbolEx::Data && is_prog)
|
||||
return false;
|
||||
if (symbol.value != current_addr)
|
||||
throw "what";
|
||||
return true;
|
||||
});
|
||||
if (symbol_index != symbol_list.end()) {
|
||||
const auto& symbol = coff.symbols[*symbol_index];
|
||||
fprintf(out, "$[%s]%s\n", storage_names.at(symbol.storage),
|
||||
symbol.name.c_str());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
begin = symbol_index + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (section.line_numbers.count(current_addr)) {
|
||||
for (const auto& line_number : section.line_numbers.at(current_addr)) {
|
||||
const auto& symbol = coff.symbols[line_number.symbol];
|
||||
fprintf(out, "#%d + $[%s]%s\n", line_number.line,
|
||||
storage_names.at(symbol.storage), symbol.name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(out, ".%08X ", current_addr);
|
||||
fprintf(out, "%04X", *iter);
|
||||
|
||||
if (section.flags & SFlag::Exec) {
|
||||
u16 opcode = *iter;
|
||||
std::string dsm;
|
||||
if (Teakra::Disassembler::NeedExpansion(opcode)) {
|
||||
++iter;
|
||||
if (iter == data.end()) {
|
||||
fprintf(out, "[broken expansion]\n");
|
||||
break;
|
||||
}
|
||||
u16 exp = *iter;
|
||||
dsm = Teakra::Disassembler::Do(opcode, exp);
|
||||
fprintf(out, " %04X ", exp);
|
||||
} else {
|
||||
dsm = Teakra::Disassembler::Do(opcode);
|
||||
fprintf(out, " ");
|
||||
}
|
||||
fprintf(out, "%s ;", dsm.c_str());
|
||||
}
|
||||
|
||||
if (section.relocations.count(current_rel_addr)) {
|
||||
for (const auto& relocation : section.relocations.at(current_rel_addr)) {
|
||||
const auto& symbol = coff.symbols[relocation.symbol];
|
||||
fprintf(out, "{@%08X + $[%s]%s}", relocation.type,
|
||||
storage_names.at(symbol.storage), symbol.name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
std::fclose(in);
|
||||
std::fclose(out);
|
||||
}
|
42
third_party/teakra/src/common_types.h
vendored
Normal file
42
third_party/teakra/src/common_types.h
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
|
||||
using s8 = std::int8_t;
|
||||
using s16 = std::int16_t;
|
||||
using s32 = std::int32_t;
|
||||
using s64 = std::int64_t;
|
||||
|
||||
template <typename T>
|
||||
constexpr unsigned BitSize() {
|
||||
return sizeof(T) * 8; // yeah I know I shouldn't use 8 here.
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T SignExtend(const T value, unsigned bit_count) {
|
||||
const T mask = static_cast<T>(1ULL << bit_count) - 1;
|
||||
const bool sign_bit = ((value >> (bit_count - 1)) & 1) != 0;
|
||||
if (sign_bit) {
|
||||
return value | ~mask;
|
||||
}
|
||||
return value & mask;
|
||||
}
|
||||
|
||||
template <unsigned bit_count, typename T>
|
||||
constexpr T SignExtend(const T value) {
|
||||
static_assert(bit_count <= BitSize<T>(), "bit_count larger than bitsize of T");
|
||||
return SignExtend(value, bit_count);
|
||||
}
|
||||
|
||||
inline constexpr u16 BitReverse(u16 value) {
|
||||
u16 result = 0;
|
||||
for (u32 i = 0; i < 16; ++i) {
|
||||
result |= ((value >> i) & 1) << (15 - i);
|
||||
}
|
||||
return result;
|
||||
}
|
46
third_party/teakra/src/core_timing.h
vendored
Normal file
46
third_party/teakra/src/core_timing.h
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include "common_types.h"
|
||||
|
||||
namespace Teakra {
|
||||
|
||||
class CoreTiming {
|
||||
public:
|
||||
class Callbacks {
|
||||
public:
|
||||
virtual ~Callbacks() = default;
|
||||
virtual void Tick() = 0;
|
||||
virtual u64 GetMaxSkip() const = 0;
|
||||
virtual void Skip(u64) = 0;
|
||||
static constexpr u64 Infinity = std::numeric_limits<u64>::max();
|
||||
};
|
||||
|
||||
void Tick() {
|
||||
for (const auto& callbacks : registered_callbacks) {
|
||||
callbacks->Tick();
|
||||
}
|
||||
}
|
||||
|
||||
u64 Skip(u64 maximum) {
|
||||
u64 ticks = maximum;
|
||||
for (const auto& callbacks : registered_callbacks) {
|
||||
ticks = std::min(ticks, callbacks->GetMaxSkip());
|
||||
}
|
||||
for (const auto& callbacks : registered_callbacks) {
|
||||
callbacks->Skip(ticks);
|
||||
}
|
||||
return ticks;
|
||||
}
|
||||
|
||||
void RegisterCallbacks(Callbacks* callbacks) {
|
||||
registered_callbacks.push_back(std::move(callbacks));
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Callbacks*> registered_callbacks;
|
||||
};
|
||||
} // namespace Teakra
|
11
third_party/teakra/src/crash.h
vendored
Normal file
11
third_party/teakra/src/crash.h
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
[[noreturn]] inline void Assert(const char* expression, const char* file, int line) {
|
||||
std::fprintf(stderr, "Assertion '%s' failed, file '%s' line '%d'.", expression, file, line);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
#define ASSERT(EXPRESSION) ((EXPRESSION) ? (void)0 : Assert(#EXPRESSION, __FILE__, __LINE__))
|
||||
#define UNREACHABLE() Assert("UNREACHABLE", __FILE__, __LINE__)
|
18
third_party/teakra/src/cru.md
vendored
Normal file
18
third_party/teakra/src/cru.md
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
# CRU
|
||||
|
||||
## MMIO Layout
|
||||
|
||||
```
|
||||
(N = 0..14)
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0140+N*4| OFFSET_L |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0142+N*4| | | ? |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x017C | OFFSET_L |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x017E | | | | ? |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|
||||
OFFSET_L, OFFSET_H: address of the program to replace
|
||||
```
|
722
third_party/teakra/src/decoder.h
vendored
Normal file
722
third_party/teakra/src/decoder.h
vendored
Normal file
|
@ -0,0 +1,722 @@
|
|||
#pragma once
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include "crash.h"
|
||||
#include "matcher.h"
|
||||
#include "operand.h"
|
||||
|
||||
template <typename... OperandAtT>
|
||||
struct OperandList {
|
||||
template <typename OperandAtT0>
|
||||
using prefix = OperandList<OperandAtT0, OperandAtT...>;
|
||||
};
|
||||
|
||||
template <typename... OperandAtT>
|
||||
struct FilterOperand;
|
||||
|
||||
template <>
|
||||
struct FilterOperand<> {
|
||||
using result = OperandList<>;
|
||||
};
|
||||
|
||||
template <bool keep, typename OperandAtT0, typename... OperandAtT>
|
||||
struct FilterOperandHelper;
|
||||
|
||||
template <typename OperandAtT0, typename... OperandAtT>
|
||||
struct FilterOperandHelper<false, OperandAtT0, OperandAtT...> {
|
||||
using result = typename FilterOperand<OperandAtT...>::result;
|
||||
};
|
||||
|
||||
template <typename OperandAtT0, typename... OperandAtT>
|
||||
struct FilterOperandHelper<true, OperandAtT0, OperandAtT...> {
|
||||
using result = typename FilterOperand<OperandAtT...>::result ::template prefix<OperandAtT0>;
|
||||
};
|
||||
|
||||
template <typename OperandAtT0, typename... OperandAtT>
|
||||
struct FilterOperand<OperandAtT0, OperandAtT...> {
|
||||
using result =
|
||||
typename FilterOperandHelper<OperandAtT0::PassAsParameter, OperandAtT0, OperandAtT...>::result;
|
||||
};
|
||||
|
||||
template <typename V, typename OperandListT>
|
||||
struct VisitorFunctionWithoutFilter;
|
||||
|
||||
template <typename V, typename... OperandAtT>
|
||||
struct VisitorFunctionWithoutFilter<V, OperandList<OperandAtT...>> {
|
||||
using type = typename V::instruction_return_type (V::*)(typename OperandAtT::FilterResult...);
|
||||
};
|
||||
|
||||
template <typename V, typename... OperandAtT>
|
||||
struct VisitorFunction {
|
||||
using type =
|
||||
typename VisitorFunctionWithoutFilter<V, typename FilterOperand<OperandAtT...>::result>::type;
|
||||
};
|
||||
|
||||
template <typename V, u16 expected, typename... OperandAtT>
|
||||
struct MatcherCreator {
|
||||
template <typename OperandListT>
|
||||
struct Proxy;
|
||||
|
||||
using F = typename VisitorFunction<V, OperandAtT...>::type;
|
||||
|
||||
template <typename... OperandAtTs>
|
||||
struct Proxy<OperandList<OperandAtTs...>> {
|
||||
F func;
|
||||
auto operator()(V& visitor, [[maybe_unused]] u16 opcode,
|
||||
[[maybe_unused]] u16 expansion) const {
|
||||
return (visitor.*func)(OperandAtTs::Extract(opcode, expansion)...);
|
||||
}
|
||||
};
|
||||
|
||||
static Matcher<V> Create(const char* name, F func) {
|
||||
// Operands shouldn't overlap each other, nor overlap with the expected ones
|
||||
static_assert(NoOverlap<u16, expected, OperandAtT::Mask...>, "Error");
|
||||
|
||||
Proxy<typename FilterOperand<OperandAtT...>::result> proxy{func};
|
||||
|
||||
constexpr u16 mask = (~OperandAtT::Mask & ... & 0xFFFF);
|
||||
constexpr bool expanded = (OperandAtT::NeedExpansion || ...);
|
||||
return Matcher<V>(name, mask, expected, expanded, proxy);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... OperandAtConstT>
|
||||
struct RejectorCreator {
|
||||
static constexpr Rejector rejector{(OperandAtConstT::Mask | ...), (OperandAtConstT::Pad | ...)};
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
|
||||
template <typename V>
|
||||
std::vector<Matcher<V>> GetDecodeTable() {
|
||||
return {
|
||||
|
||||
#define INST(name, ...) MatcherCreator<V, __VA_ARGS__>::Create(#name, &V::name)
|
||||
#define EXCEPT(...) Except(RejectorCreator<__VA_ARGS__>::rejector)
|
||||
|
||||
// <<< Misc >>>
|
||||
INST(nop, 0x0000),
|
||||
INST(norm, 0x94C0, At<Ax, 8>, At<Rn, 0>, At<StepZIDS, 3>),
|
||||
INST(swap, 0x4980, At<SwapType, 0>),
|
||||
INST(trap, 0x0020),
|
||||
|
||||
// <<< ALM normal >>>
|
||||
INST(alm, 0xA000, At<Alm, 9>, At<MemImm8, 0>, At<Ax, 8>),
|
||||
INST(alm, 0x8080, At<Alm, 9>, At<Rn, 0>, At<StepZIDS, 3>, At<Ax, 8>),
|
||||
INST(alm, 0x80A0, At<Alm, 9>, At<Register, 0>, At<Ax, 8>),
|
||||
|
||||
// <<< ALM r6 >>>
|
||||
INST(alm_r6, 0xD388, Const<Alm, 0>, At<Ax, 4>),
|
||||
INST(alm_r6, 0xD389, Const<Alm, 1>, At<Ax, 4>),
|
||||
INST(alm_r6, 0xD38A, Const<Alm, 2>, At<Ax, 4>),
|
||||
INST(alm_r6, 0xD38B, Const<Alm, 3>, At<Ax, 4>),
|
||||
INST(alm_r6, 0xD38C, Const<Alm, 4>, At<Ax, 4>),
|
||||
INST(alm_r6, 0xD38D, Const<Alm, 5>, At<Ax, 4>),
|
||||
INST(alm_r6, 0xD38E, Const<Alm, 6>, At<Ax, 4>),
|
||||
INST(alm_r6, 0xD38F, Const<Alm, 7>, At<Ax, 4>),
|
||||
INST(alm_r6, 0x9462, Const<Alm, 8>, At<Ax, 0>),
|
||||
INST(alm_r6, 0x9464, Const<Alm, 9>, At<Ax, 0>),
|
||||
INST(alm_r6, 0x9466, Const<Alm, 10>, At<Ax, 0>),
|
||||
INST(alm_r6, 0x5E23, Const<Alm, 11>, At<Ax, 8>),
|
||||
INST(alm_r6, 0x5E22, Const<Alm, 12>, At<Ax, 8>),
|
||||
INST(alm_r6, 0x5F41, Const<Alm, 13>, Const<Ax, 0>),
|
||||
INST(alm_r6, 0x9062, Const<Alm, 14>, At<Ax, 8>, Unused<0>),
|
||||
INST(alm_r6, 0x8A63, Const<Alm, 15>, At<Ax, 3>),
|
||||
|
||||
// <<< ALU normal >>>
|
||||
INST(alu, 0xD4F8, At<Alu, 0>, At<MemImm16, 16>, At<Ax, 8>)
|
||||
.EXCEPT(AtConst<Alu, 0, 4>).EXCEPT(AtConst<Alu, 0, 5>),
|
||||
INST(alu, 0xD4D8, At<Alu, 0>, At<MemR7Imm16, 16>, At<Ax, 8>)
|
||||
.EXCEPT(AtConst<Alu, 0, 4>).EXCEPT(AtConst<Alu, 0, 5>),
|
||||
INST(alu, 0x80C0, At<Alu, 9>, At<Imm16, 16>, At<Ax, 8>)
|
||||
.EXCEPT(AtConst<Alu, 9, 4>).EXCEPT(AtConst<Alu, 9, 5>),
|
||||
INST(alu, 0xC000, At<Alu, 9>, At<Imm8, 0>, At<Ax, 8>)
|
||||
.EXCEPT(AtConst<Alu, 9, 4>).EXCEPT(AtConst<Alu, 9, 5>),
|
||||
INST(alu, 0x4000, At<Alu, 9>, At<MemR7Imm7s, 0>, At<Ax, 8>)
|
||||
.EXCEPT(AtConst<Alu, 9, 4>).EXCEPT(AtConst<Alu, 9, 5>),
|
||||
|
||||
// <<< OR Extra >>>
|
||||
INST(or_, 0xD291, At<Ab, 10>, At<Ax, 6>, At<Ax, 5>),
|
||||
INST(or_, 0xD4A4, At<Ax, 8>, At<Bx, 1>, At<Ax, 0>),
|
||||
INST(or_, 0xD3C4, At<Bx, 10>, At<Bx, 1>, At<Ax, 0>),
|
||||
|
||||
// <<< ALB normal >>>
|
||||
INST(alb, 0xE100, At<Alb, 9>, At<Imm16, 16>, At<MemImm8, 0>),
|
||||
INST(alb, 0x80E0, At<Alb, 9>, At<Imm16, 16>, At<Rn, 0>, At<StepZIDS, 3>),
|
||||
INST(alb, 0x81E0, At<Alb, 9>, At<Imm16, 16>, At<Register, 0>),
|
||||
INST(alb_r6, 0x47B8, At<Alb, 0>, At<Imm16, 16>),
|
||||
|
||||
// <<< ALB SttMod >>>
|
||||
INST(alb, 0x43C8, Const<Alb, 0>, At<Imm16, 16>, At<SttMod, 0>),
|
||||
INST(alb, 0x4388, Const<Alb, 1>, At<Imm16, 16>, At<SttMod, 0>),
|
||||
INST(alb, 0x0038, Const<Alb, 2>, At<Imm16, 16>, At<SttMod, 0>),
|
||||
//INST(alb, 0x????, Const<Alb, 3>, At<Imm,16, 16>, At<SttMod, 0>),
|
||||
INST(alb, 0x9470, Const<Alb, 4>, At<Imm16, 16>, At<SttMod, 0>),
|
||||
INST(alb, 0x9478, Const<Alb, 5>, At<Imm16, 16>, At<SttMod, 0>),
|
||||
//INST(alb, 0x????, Const<Alb, 6>, At<Imm,16, 16>, At<SttMod, 0>),
|
||||
//INST(alb, 0x????, Const<Alb, 7>, At<Imm,16, 16>, At<SttMod, 0>),
|
||||
|
||||
// <<< Add extra >>>
|
||||
INST(add, 0xD2DA, At<Ab, 10>, At<Bx, 0>),
|
||||
INST(add, 0x5DF0, At<Bx, 1>, At<Ax, 0>),
|
||||
INST(add_p1, 0xD782, At<Ax, 0>),
|
||||
INST(add, 0x5DF8, At<Px, 1>, At<Bx, 0>),
|
||||
|
||||
// <<< Sub extra >>>
|
||||
INST(sub, 0x8A61, At<Ab, 3>, At<Bx, 8>),
|
||||
INST(sub, 0x8861, At<Bx, 4>, At<Ax, 3>),
|
||||
INST(sub_p1, 0xD4B9, At<Ax, 8>),
|
||||
INST(sub, 0x8FD0, At<Px, 1>, At<Bx, 0>),
|
||||
|
||||
/// <<< addsub p0 p1 >>>
|
||||
INST(app, 0x5DC0, At<Ab, 2>, BZr, Add, PP, Add, PP),
|
||||
INST(app, 0x5DC1, At<Ab, 2>, BZr, Add, PP, Add, PA),
|
||||
INST(app, 0x4590, At<Ab, 2>, BAc, Add, PP, Add, PP),
|
||||
INST(app, 0x4592, At<Ab, 2>, BAc, Add, PP, Add, PA),
|
||||
INST(app, 0x4593, At<Ab, 2>, BAc, Add, PA, Add, PA),
|
||||
INST(app, 0x5DC2, At<Ab, 2>, BZr, Add, PP, Sub, PP),
|
||||
INST(app, 0x5DC3, At<Ab, 2>, BZr, Add, PP, Sub, PA),
|
||||
INST(app, 0x80C6, At<Ab, 10>, BAc, Sub, PP, Sub, PP),
|
||||
INST(app, 0x82C6, At<Ab, 10>, BAc, Sub, PP, Sub, PA),
|
||||
INST(app, 0x83C6, At<Ab, 10>, BAc, Sub, PA, Sub, PA),
|
||||
INST(app, 0x906C, At<Ab, 0>, BAc, Add, PP, Sub, PP),
|
||||
INST(app, 0x49C2, At<Ab, 4>, BAc, Sub, PP, Add, PP),
|
||||
INST(app, 0x916C, At<Ab, 0>, BAc, Add, PP, Sub, PA),
|
||||
INST(app, 0x49C3, At<Ab, 4>, BAc, Sub, PP, Add, PA),
|
||||
|
||||
/// <<< add||sub >>>
|
||||
INST(add_add, 0x6F80, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>, At<Ab, 3>),
|
||||
INST(add_sub, 0x6FA0, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>, At<Ab, 3>),
|
||||
INST(sub_add, 0x6FC0, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>, At<Ab, 3>),
|
||||
INST(sub_sub, 0x6FE0, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>, At<Ab, 3>),
|
||||
|
||||
/// <<< add||sub sv >>>
|
||||
INST(add_sub_sv, 0x5DB0, At<ArRn1, 1>, At<ArStep1, 0>, At<Ab, 2>),
|
||||
INST(sub_add_sv, 0x5DE0, At<ArRn1, 1>, At<ArStep1, 0>, At<Ab, 2>),
|
||||
|
||||
/// <<< add||sub||mov sv >>>
|
||||
INST(sub_add_i_mov_j_sv, 0x8064, At<ArpRn1, 8>, At<ArpStep1, 0>, At<ArpStep1, 1>, At<Ab, 3>),
|
||||
INST(sub_add_j_mov_i_sv, 0x5D80, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>, At<Ab, 3>),
|
||||
INST(add_sub_i_mov_j, 0x9070, At<ArpRn1, 8>, At<ArpStep1, 0>, At<ArpStep1, 1>, At<Ab, 2>),
|
||||
INST(add_sub_j_mov_i, 0x5E30, At<ArpRn1, 8>, At<ArpStep1, 0>, At<ArpStep1, 1>, At<Ab, 2>),
|
||||
|
||||
// <<< Mul >>>
|
||||
INST(mul, 0x8000, At<Mul3, 8>, At<Rn, 0>, At<StepZIDS, 3>, At<Imm16, 16>, At<Ax, 11>),
|
||||
INST(mul_y0, 0x8020, At<Mul3, 8>, At<Rn, 0>, At<StepZIDS, 3>, At<Ax, 11>),
|
||||
INST(mul_y0, 0x8040, At<Mul3, 8>, At<Register, 0>, At<Ax, 11>),
|
||||
INST(mul, 0xD000, At<Mul3, 8>, At<R45, 2>, At<StepZIDS, 5>, At<R0123, 0>, At<StepZIDS, 3>, At<Ax, 11>),
|
||||
INST(mul_y0_r6, 0x5EA0, At<Mul3, 1>, At<Ax, 0>),
|
||||
INST(mul_y0, 0xE000, At<Mul2, 9>, At<MemImm8, 0>, At<Ax, 11>),
|
||||
|
||||
// <<< Mul Extra >>>
|
||||
INST(mpyi, 0x0800, At<Imm8s, 0>),
|
||||
INST(msu, 0xD080, At<R45, 2>, At<StepZIDS, 5>, At<R0123, 0>, At<StepZIDS, 3>, At<Ax, 8>),
|
||||
INST(msu, 0x90C0, At<Rn, 0>, At<StepZIDS, 3>, At<Imm16, 16>, At<Ax, 8>),
|
||||
INST(msusu, 0x8264, At<ArRn2, 3>, At<ArStep2, 0>, At<Ax, 8>),
|
||||
INST(mac_x1to0, 0x4D84, At<Ax, 1>, Unused<0>),
|
||||
INST(mac1, 0x5E28, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>, At<Ax, 8>),
|
||||
|
||||
// <<< MODA >>>
|
||||
INST(moda4, 0x6700, At<Moda4, 4>, At<Ax, 12>, At<Cond, 0>)
|
||||
.EXCEPT(AtConst<Moda4, 4, 7>),
|
||||
INST(moda3, 0x6F00, At<Moda3, 4>, At<Bx, 12>, At<Cond, 0>),
|
||||
INST(pacr1, 0xD7C2, At<Ax, 0>),
|
||||
INST(clr, 0x8ED0, At<Ab, 2>, At<Ab, 0>),
|
||||
INST(clrr, 0x8DD0, At<Ab, 2>, At<Ab, 0>),
|
||||
|
||||
// <<< Block repeat >>>
|
||||
INST(bkrep, 0x5C00, At<Imm8, 0>, At<Address16, 16>),
|
||||
INST(bkrep, 0x5D00, At<Register, 0>, At<Address18_16, 16>, At<Address18_2, 5>),
|
||||
INST(bkrep_r6, 0x8FDC, At<Address18_16, 16>, At<Address18_2, 0>),
|
||||
INST(bkreprst, 0xDA9C, At<ArRn2, 0>),
|
||||
INST(bkreprst_memsp, 0x5F48, Unused<0>, Unused<1>),
|
||||
INST(bkrepsto, 0xDADC, At<ArRn2, 0>, Unused<10>),
|
||||
INST(bkrepsto_memsp, 0x9468, Unused<0>, Unused<1>, Unused<2>),
|
||||
|
||||
// <<< Bank >>>
|
||||
INST(banke, 0x4B80, At<BankFlags, 0>),
|
||||
INST(bankr, 0x8CDF),
|
||||
INST(bankr, 0x8CDC, At<Ar, 0>),
|
||||
INST(bankr, 0x8CD0, At<Ar, 2>, At<Arp, 0>),
|
||||
INST(bankr, 0x8CD8, At<Arp, 0>),
|
||||
|
||||
// <<< Bitrev >>>
|
||||
INST(bitrev, 0x5EB8, At<Rn, 0>),
|
||||
INST(bitrev_dbrv, 0xD7E8, At<Rn, 0>),
|
||||
INST(bitrev_ebrv, 0xD7E0, At<Rn, 0>),
|
||||
|
||||
// <<< Branching >>>
|
||||
INST(br, 0x4180, At<Address18_16, 16>, At<Address18_2, 4>, At<Cond, 0>),
|
||||
INST(brr, 0x5000, At<RelAddr7, 4>, At<Cond, 0>),
|
||||
|
||||
// <<< Break >>>
|
||||
INST(break_, 0xD3C0),
|
||||
|
||||
// <<< Call >>>
|
||||
INST(call, 0x41C0, At<Address18_16, 16>, At<Address18_2, 4>, At<Cond, 0>),
|
||||
INST(calla, 0xD480, At<Axl, 8>),
|
||||
INST(calla, 0xD381, At<Ax, 4>),
|
||||
INST(callr, 0x1000, At<RelAddr7, 4>, At<Cond, 0>),
|
||||
|
||||
// <<< Context >>>
|
||||
INST(cntx_s, 0xD380),
|
||||
INST(cntx_r, 0xD390),
|
||||
|
||||
// <<< Return >>>
|
||||
INST(ret, 0x4580, At<Cond, 0>),
|
||||
INST(retd, 0xD780),
|
||||
INST(reti, 0x45C0, At<Cond, 0>),
|
||||
INST(retic, 0x45D0, At<Cond, 0>),
|
||||
INST(retid, 0xD7C0),
|
||||
INST(retidc, 0xD3C3),
|
||||
INST(rets, 0x0900, At<Imm8, 0>),
|
||||
|
||||
// <<< Load >>>
|
||||
INST(load_ps, 0x4D80, At<Imm2, 0>),
|
||||
INST(load_stepi, 0xDB80, At<Imm7s, 0>),
|
||||
INST(load_stepj, 0xDF80, At<Imm7s, 0>),
|
||||
INST(load_page, 0x0400, At<Imm8, 0>),
|
||||
INST(load_modi, 0x0200, At<Imm9, 0>),
|
||||
INST(load_modj, 0x0A00, At<Imm9, 0>),
|
||||
INST(load_movpd, 0xD7D8, At<Imm2, 1>, Unused<0>),
|
||||
INST(load_ps01, 0x0010, At<Imm4, 0>),
|
||||
|
||||
// <<< Push >>>
|
||||
INST(push, 0x5F40, At<Imm16, 16>),
|
||||
INST(push, 0x5E40, At<Register, 0>),
|
||||
INST(push, 0xD7C8, At<Abe, 1>, Unused<0>),
|
||||
INST(push, 0xD3D0, At<ArArpSttMod, 0>),
|
||||
INST(push_prpage, 0xD7FC, Unused<0>, Unused<1>),
|
||||
INST(push, 0xD78C, At<Px, 1>, Unused<0>),
|
||||
INST(push_r6, 0xD4D7, Unused<5>),
|
||||
INST(push_repc, 0xD7F8, Unused<0>, Unused<1>),
|
||||
INST(push_x0, 0xD4D4, Unused<5>),
|
||||
INST(push_x1, 0xD4D5, Unused<5>),
|
||||
INST(push_y1, 0xD4D6, Unused<5>),
|
||||
INST(pusha, 0x4384, At<Ax, 6>, Unused<0>, Unused<1>),
|
||||
INST(pusha, 0xD788, At<Bx, 1>, Unused<0>),
|
||||
|
||||
// <<< Pop >>>
|
||||
INST(pop, 0x5E60, At<Register, 0>),
|
||||
INST(pop, 0x47B4, At<Abe, 0>),
|
||||
INST(pop, 0x80C7, At<ArArpSttMod, 8>),
|
||||
INST(pop, 0x0006, At<Bx, 5>, Unused<0>),
|
||||
INST(pop_prpage, 0xD7F4, Unused<0>, Unused<1>),
|
||||
INST(pop, 0xD496, At<Px, 0>),
|
||||
INST(pop_r6, 0x0024, Unused<0>),
|
||||
INST(pop_repc, 0xD7F0, Unused<0>, Unused<1>),
|
||||
INST(pop_x0, 0xD494),
|
||||
INST(pop_x1, 0xD495),
|
||||
INST(pop_y1, 0x0004, Unused<0>),
|
||||
INST(popa, 0x47B0, At<Ab, 0>),
|
||||
|
||||
// <<< Repeat >>>
|
||||
INST(rep, 0x0C00, At<Imm8, 0>),
|
||||
INST(rep, 0x0D00, At<Register, 0>),
|
||||
INST(rep_r6, 0x0002, Unused<0>),
|
||||
|
||||
// <<< Shift >>>
|
||||
INST(shfc, 0xD280, At<Ab, 10>, At<Ab, 5>, At<Cond, 0>),
|
||||
INST(shfi, 0x9240, At<Ab, 10>, At<Ab, 7>, At<Imm6s, 0>),
|
||||
|
||||
// <<< TSTB >>>
|
||||
INST(tst4b, 0x80C1, At<ArRn2, 10>, At<ArStep2, 8>),
|
||||
INST(tst4b, 0x4780, At<ArRn2, 2>, At<ArStep2, 0>, At<Ax, 4>),
|
||||
INST(tstb, 0xF000, At<MemImm8, 0>, At<Imm4, 8>),
|
||||
INST(tstb, 0x9020, At<Rn, 0>, At<StepZIDS, 3>, At<Imm4, 8>),
|
||||
INST(tstb, 0x9000, At<Register, 0>, At<Imm4, 8>)
|
||||
.EXCEPT(AtConst<Register, 0, 24>), // override by tstb_r6
|
||||
INST(tstb_r6, 0x9018, At<Imm4, 8>),
|
||||
INST(tstb, 0x0028, At<SttMod, 0>, At<Imm16, 16>), // unused12@20
|
||||
|
||||
// <<< AND Extra >>>
|
||||
INST(and_, 0x6770, At<Ab, 2>, At<Ab, 0>, At<Ax, 12>),
|
||||
|
||||
// <<< Interrupt >>>
|
||||
INST(dint, 0x43C0),
|
||||
INST(eint, 0x4380),
|
||||
|
||||
// <<< EXP >>>
|
||||
INST(exp, 0x9460, At<Bx, 0>),
|
||||
INST(exp, 0x9060, At<Bx, 0>, At<Ax, 8>),
|
||||
INST(exp, 0x9C40, At<Rn, 0>, At<StepZIDS, 3>),
|
||||
INST(exp, 0x9840, At<Rn, 0>, At<StepZIDS, 3>, At<Ax, 8>),
|
||||
INST(exp, 0x9440, At<Register, 0>),
|
||||
INST(exp, 0x9040, At<Register, 0>, At<Ax, 8>),
|
||||
INST(exp_r6, 0xD7C1),
|
||||
INST(exp_r6, 0xD382, At<Ax, 4>),
|
||||
|
||||
// <<< MODR >>>
|
||||
INST(modr, 0x0080, At<Rn, 0>, At<StepZIDS, 3>),
|
||||
INST(modr_dmod, 0x00A0, At<Rn, 0>, At<StepZIDS, 3>),
|
||||
INST(modr_i2, 0x4990, At<Rn, 0>),
|
||||
INST(modr_i2_dmod, 0x4998, At<Rn, 0>),
|
||||
INST(modr_d2, 0x5DA0, At<Rn, 0>),
|
||||
INST(modr_d2_dmod, 0x5DA8, At<Rn, 0>),
|
||||
INST(modr_eemod, 0xD294, At<ArpRn2, 10>, At<ArpStep2, 0>, At<ArpStep2, 5>),
|
||||
INST(modr_edmod, 0x0D80, At<ArpRn2, 5>, At<ArpStep2, 1>, At<ArpStep2, 3>),
|
||||
INST(modr_demod, 0x8464, At<ArpRn2, 8>, At<ArpStep2, 0>, At<ArpStep2, 3>),
|
||||
INST(modr_ddmod, 0x0D81, At<ArpRn2, 5>, At<ArpStep2, 1>, At<ArpStep2, 3>),
|
||||
|
||||
// <<< MOV >>>
|
||||
INST(mov, 0xD290, At<Ab, 10>, At<Ab, 5>),
|
||||
INST(mov_dvm, 0xD298, At<Abl, 10>),
|
||||
INST(mov_x0, 0xD2D8, At<Abl, 10>),
|
||||
INST(mov_x1, 0xD394, At<Abl, 0>),
|
||||
INST(mov_y1, 0xD384, At<Abl, 0>),
|
||||
|
||||
INST(mov, 0x3000, At<Ablh, 9>, At<MemImm8, 0>),
|
||||
INST(mov, 0xD4BC, At<Axl, 8>, At<MemImm16, 16>),
|
||||
INST(mov, 0xD49C, At<Axl, 8>, At<MemR7Imm16, 16>),
|
||||
INST(mov, 0xDC80, At<Axl, 8>, At<MemR7Imm7s, 0>),
|
||||
|
||||
INST(mov, 0xD4B8, At<MemImm16, 16>, At<Ax, 8>),
|
||||
INST(mov, 0x6100, At<MemImm8, 0>, At<Ab, 11>),
|
||||
INST(mov, 0x6200, At<MemImm8, 0>, At<Ablh, 10>),
|
||||
INST(mov_eu, 0x6500, At<MemImm8, 0>, At<Axh, 12>),
|
||||
INST(mov, 0x6000, At<MemImm8, 0>, At<RnOld, 10>),
|
||||
INST(mov_sv, 0x6D00, At<MemImm8, 0>),
|
||||
|
||||
INST(mov_dvm_to, 0xD491, At<Ab, 5>),
|
||||
INST(mov_icr_to, 0xD492, At<Ab, 5>),
|
||||
|
||||
INST(mov, 0x5E20, At<Imm16, 16>, At<Bx, 8>),
|
||||
INST(mov, 0x5E00, At<Imm16, 16>, At<Register, 0>),
|
||||
INST(mov_icr, 0x4F80, At<Imm5, 0>),
|
||||
INST(mov, 0x2500, At<Imm8s, 0>, At<Axh, 12>),
|
||||
INST(mov_ext0, 0x2900, At<Imm8s, 0>),
|
||||
INST(mov_ext1, 0x2D00, At<Imm8s, 0>),
|
||||
INST(mov_ext2, 0x3900, At<Imm8s, 0>),
|
||||
INST(mov_ext3, 0x3D00, At<Imm8s, 0>),
|
||||
INST(mov, 0x2300, At<Imm8s, 0>, At<RnOld, 10>),
|
||||
INST(mov_sv, 0x0500, At<Imm8s, 0>),
|
||||
INST(mov, 0x2100, At<Imm8, 0>, At<Axl, 12>),
|
||||
|
||||
INST(mov, 0xD498, At<MemR7Imm16, 16>, At<Ax, 8>),
|
||||
INST(mov, 0xD880, At<MemR7Imm7s, 0>, At<Ax, 8>),
|
||||
INST(mov, 0x98C0, At<Rn, 0>, At<StepZIDS, 3>, At<Bx, 8>),
|
||||
INST(mov, 0x1C00, At<Rn, 0>, At<StepZIDS, 3>, At<Register, 5>),
|
||||
|
||||
INST(mov_memsp_to, 0x47E0, At<Register, 0>),
|
||||
INST(mov_mixp_to, 0x47C0, At<Register, 0>),
|
||||
INST(mov, 0x2000, At<RnOld, 9>, At<MemImm8, 0>),
|
||||
INST(mov_icr, 0x4FC0, At<Register, 0>),
|
||||
INST(mov_mixp, 0x5E80, At<Register, 0>),
|
||||
INST(mov, 0x1800, At<Register, 5>, At<Rn, 0>, At<StepZIDS, 3>)
|
||||
.EXCEPT(AtConst<Register, 5, 24>).EXCEPT(AtConst<Register, 5, 25>), // override by mov_r6(_to)
|
||||
INST(mov, 0x5EC0, At<Register, 0>, At<Bx, 5>),
|
||||
INST(mov, 0x5800, At<Register, 0>, At<Register, 5>)
|
||||
.EXCEPT(AtConst<Register, 0, 24>).EXCEPT(AtConst<Register, 0, 25>), // override by mma_mov
|
||||
INST(mov_repc_to, 0xD490, At<Ab, 5>),
|
||||
INST(mov_sv_to, 0x7D00, At<MemImm8, 0>),
|
||||
INST(mov_x0_to, 0xD493, At<Ab, 5>),
|
||||
INST(mov_x1_to, 0x49C1, At<Ab, 4>),
|
||||
INST(mov_y1_to, 0xD299, At<Ab, 10>),
|
||||
|
||||
// <<< MOV load >>>
|
||||
INST(mov, 0x0008, At<Imm16, 16>, At<ArArp, 0>),
|
||||
INST(mov_r6, 0x0023, At<Imm16, 16>),
|
||||
INST(mov_repc, 0x0001, At<Imm16, 16>),
|
||||
INST(mov_stepi0, 0x8971, At<Imm16, 16>),
|
||||
INST(mov_stepj0, 0x8979, At<Imm16, 16>),
|
||||
INST(mov, 0x0030, At<Imm16, 16>, At<SttMod, 0>),
|
||||
INST(mov_prpage, 0x5DD0, At<Imm4, 0>),
|
||||
|
||||
// <<< <<< MOV p/d >>>
|
||||
INST(movd, 0x5F80, At<R0123, 0>, At<StepZIDS, 3>, At<R45, 2>, At<StepZIDS, 5>),
|
||||
INST(movp, 0x0040, At<Axl, 5>, At<Register, 0>),
|
||||
INST(movp, 0x0D40, At<Ax, 5>, At<Register, 0>),
|
||||
INST(movp, 0x0600, At<Rn, 0>, At<StepZIDS, 3>, At<R0123, 5>, At<StepZIDS, 7>),
|
||||
INST(movpdw, 0xD499, At<Ax, 8>),
|
||||
|
||||
// <<< MOV 2 >>>
|
||||
INST(mov_a0h_stepi0, 0xD49B),
|
||||
INST(mov_a0h_stepj0, 0xD59B),
|
||||
INST(mov_stepi0_a0h, 0xD482),
|
||||
INST(mov_stepj0_a0h, 0xD582),
|
||||
|
||||
INST(mov_prpage, 0x9164, At<Abl, 0>),
|
||||
INST(mov_repc, 0x9064, At<Abl, 0>),
|
||||
INST(mov, 0x9540, At<Abl, 3>, At<ArArp, 0>),
|
||||
INST(mov, 0x9C60, At<Abl, 3>, At<SttMod, 0>),
|
||||
|
||||
INST(mov_prpage_to, 0x5EB0, At<Abl, 0>),
|
||||
INST(mov_repc_to, 0xD2D9, At<Abl, 10>),
|
||||
INST(mov, 0x9560, At<ArArp, 0>, At<Abl, 3>),
|
||||
INST(mov, 0xD2F8, At<SttMod, 0>, At<Abl, 10>),
|
||||
|
||||
INST(mov_repc_to, 0xD7D0, At<ArRn1, 1>, At<ArStep1, 0>),
|
||||
INST(mov, 0xD488, At<ArArp, 0>, At<ArRn1, 8>, At<ArStep1, 5>),
|
||||
INST(mov, 0x49A0, At<SttMod, 0>, At<ArRn1, 4>, At<ArStep1, 3>),
|
||||
|
||||
INST(mov_repc, 0xD7D4, At<ArRn1, 1>, At<ArStep1, 0>),
|
||||
INST(mov, 0x8062, At<ArRn1, 4>, At<ArStep1, 3>, At<ArArp, 8>),
|
||||
INST(mov, 0x8063, At<ArRn1, 4>, At<ArStep1, 3>, At<SttMod, 8>),
|
||||
|
||||
INST(mov_repc_to, 0xD3C8, At<MemR7Imm16, 16>, Unused<0>, Unused<1>, Unused<2>),
|
||||
INST(mov, 0x5F50, At<ArArpSttMod, 0>, At<MemR7Imm16, 16>),
|
||||
|
||||
INST(mov_repc, 0xD2DC, At<MemR7Imm16, 16>, Unused<0>, Unused<1>, Unused<10>),
|
||||
INST(mov, 0x4D90, At<MemR7Imm16, 16>, At<ArArpSttMod, 0>),
|
||||
|
||||
INST(mov_pc, 0x886B, At<Ax, 8>),
|
||||
INST(mov_pc, 0x8863, At<Bx, 8>),
|
||||
|
||||
INST(mov_mixp_to, 0x8A73, At<Bx, 3>),
|
||||
INST(mov_mixp_r6, 0x4381),
|
||||
INST(mov_p0h_to, 0x4382, At<Bx, 0>),
|
||||
INST(mov_p0h_r6, 0xD3C2),
|
||||
INST(mov_p0h_to, 0x4B60, At<Register, 0>),
|
||||
INST(mov_p0, 0x8FD4, At<Ab, 0>),
|
||||
INST(mov_p1_to, 0x8FD8, At<Ab, 0>),
|
||||
|
||||
INST(mov2, 0x88D0, At<Px, 1>, At<ArRn2, 8>, At<ArStep2, 2>),
|
||||
INST(mov2s, 0x88D1, At<Px, 1>, At<ArRn2, 8>, At<ArStep2, 2>),
|
||||
INST(mov2, 0xD292, At<ArRn2, 10>, At<ArStep2, 5>, At<Px, 0>),
|
||||
INST(mova, 0x4DC0, At<Ab, 4>, At<ArRn2, 2>, At<ArStep2, 0>),
|
||||
INST(mova, 0x4BC0, At<ArRn2, 2>, At<ArStep2, 0>, At<Ab, 4>),
|
||||
|
||||
INST(mov_r6_to, 0xD481, At<Bx, 8>),
|
||||
INST(mov_r6_mixp, 0x43C1),
|
||||
INST(mov_r6_to, 0x5F00, At<Register, 0>),
|
||||
INST(mov_r6, 0x5F60, At<Register, 0>),
|
||||
INST(mov_memsp_r6, 0xD29C, Unused<0>, Unused<1>, Unused<10>),
|
||||
INST(mov_r6_to, 0x1B00, At<Rn, 0>, At<StepZIDS, 3>),
|
||||
INST(mov_r6, 0x1B20, At<Rn, 0>, At<StepZIDS, 3>),
|
||||
|
||||
INST(movs, 0x6300, At<MemImm8, 0>, At<Ab, 11>),
|
||||
INST(movs, 0x0180, At<Rn, 0>, At<StepZIDS, 3>, At<Ab, 5>),
|
||||
INST(movs, 0x0100, At<Register, 0>, At<Ab, 5>),
|
||||
INST(movs_r6_to, 0x5F42, At<Ax, 0>),
|
||||
INST(movsi, 0x4080, At<RnOld, 9>, At<Ab, 5>, At<Imm5s, 0>),
|
||||
|
||||
// <<< MOV MOV >>>
|
||||
INST(mov2_axh_m_y0_m, 0x4390, At<Axh, 6>, At<ArRn2, 2>, At<ArStep2, 0>),
|
||||
INST(mov2_ax_mij, 0x43A0, At<Ab, 3>, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>),
|
||||
INST(mov2_ax_mji, 0x43E0, At<Ab, 3>, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>),
|
||||
INST(mov2_mij_ax, 0x80C4, At<ArpRn1, 9>, At<ArpStep1, 0>, At<ArpStep1, 8>, At<Ab, 10>),
|
||||
INST(mov2_mji_ax, 0xD4C0, At<ArpRn1, 5>, At<ArpStep1, 0>, At<ArpStep1, 1>, At<Ab, 2>),
|
||||
INST(mov2_abh_m, 0x9D40, At<Abh, 4>, At<Abh, 2>, At<ArRn1, 1>, At<ArStep1, 0>),
|
||||
INST(exchange_iaj, 0x8C60, At<Axh, 4>, At<ArpRn2, 8>, At<ArpStep2, 0>, At<ArpStep2, 2>),
|
||||
INST(exchange_riaj, 0x7F80, At<Axh, 6>, At<ArpRn2, 4>, At<ArpStep2, 0>, At<ArpStep2, 2>),
|
||||
INST(exchange_jai, 0x4900, At<Axh, 6>, At<ArpRn2, 4>, At<ArpStep2, 0>, At<ArpStep2, 2>),
|
||||
INST(exchange_rjai, 0x4800, At<Axh, 6>, At<ArpRn2, 4>, At<ArpStep2, 0>, At<ArpStep2, 2>),
|
||||
|
||||
// <<< MOVR >>>
|
||||
INST(movr, 0x8864, At<ArRn2, 3>, At<ArStep2, 0>, At<Abh, 8>),
|
||||
INST(movr, 0x9CE0, At<Rn, 0>, At<StepZIDS, 3>, At<Ax, 8>),
|
||||
INST(movr, 0x9CC0, At<Register, 0>, At<Ax, 8>),
|
||||
INST(movr, 0x5DF4, At<Bx, 1>, At<Ax, 0>),
|
||||
INST(movr_r6_to, 0x8961, At<Ax, 3>),
|
||||
|
||||
// <<< LIM >>>
|
||||
INST(lim, 0x49C0, At<Ax, 5>, At<Ax, 4>),
|
||||
|
||||
// <<< Viterbi >>>
|
||||
INST(vtrclr0, 0x5F45),
|
||||
INST(vtrclr1, 0x5F46),
|
||||
INST(vtrclr, 0x5F47),
|
||||
INST(vtrmov0, 0xD29A, At<Axl, 0>),
|
||||
INST(vtrmov1, 0xD69A, At<Axl, 0>),
|
||||
INST(vtrmov, 0xD383, At<Axl, 4>),
|
||||
INST(vtrshr, 0xD781),
|
||||
|
||||
// <<< CLRP >>>
|
||||
INST(clrp0, 0x5DFE),
|
||||
INST(clrp1, 0x5DFD),
|
||||
INST(clrp, 0x5DFF),
|
||||
|
||||
// <<< min/max >>>
|
||||
INST(max_ge, 0x8460, At<Ax, 8>, At<StepZIDS, 3>),
|
||||
INST(max_gt, 0x8660, At<Ax, 8>, At<StepZIDS, 3>),
|
||||
INST(min_le, 0x8860, At<Ax, 8>, At<StepZIDS, 3>),
|
||||
INST(min_lt, 0x8A60, At<Ax, 8>, At<StepZIDS, 3>),
|
||||
INST(max_ge_r0, 0x8060, At<Ax, 8>, At<StepZIDS, 3>),
|
||||
INST(max_gt_r0, 0x8260, At<Ax, 8>, At<StepZIDS, 3>),
|
||||
INST(min_le_r0, 0x47A0, At<Ax, 3>, At<StepZIDS, 0>),
|
||||
INST(min_lt_r0, 0x47A4, At<Ax, 3>, At<StepZIDS, 0>),
|
||||
|
||||
// <<< Division Step >>>
|
||||
INST(divs, 0x0E00, At<MemImm8, 0>, At<Ax, 8>),
|
||||
|
||||
// <<< Sqr >>>
|
||||
INST(sqr_sqr_add3, 0xD790, At<Ab, 2>, At<Ab, 0>),
|
||||
INST(sqr_sqr_add3, 0x4B00, At<ArRn2, 4>, At<ArStep2, 2>, At<Ab, 0>),
|
||||
INST(sqr_mpysu_add3a, 0x49C4, At<Ab, 4>, At<Ab, 0>),
|
||||
|
||||
// <<< CMP Extra >>>
|
||||
INST(cmp, 0x4D8C, At<Ax, 1>, At<Bx, 0>),
|
||||
INST(cmp_b0_b1, 0xD483),
|
||||
INST(cmp_b1_b0, 0xD583),
|
||||
INST(cmp, 0xDA9A, At<Bx, 10>, At<Ax, 0>),
|
||||
INST(cmp_p1_to, 0x8B63, At<Ax, 4>),
|
||||
|
||||
// <<< min||max||vtrshr >>>
|
||||
INST(max2_vtr, 0x5E21, At<Ax, 8>),
|
||||
INST(min2_vtr, 0x43C2, At<Ax, 0>),
|
||||
INST(max2_vtr, 0xD784, At<Ax, 1>, At<Bx, 0>),
|
||||
INST(min2_vtr, 0xD4BA, At<Ax, 8>, At<Bx, 0>),
|
||||
INST(max2_vtr_movl, 0x4A40, At<Ax, 3>, At<Bx, 4>, At<ArRn1, 1>, At<ArStep1, 0>),
|
||||
INST(max2_vtr_movh, 0x4A44, At<Ax, 3>, At<Bx, 4>, At<ArRn1, 1>, At<ArStep1, 0>),
|
||||
INST(max2_vtr_movl, 0x4A60, At<Bx, 4>, At<Ax, 3>, At<ArRn1, 1>, At<ArStep1, 0>),
|
||||
INST(max2_vtr_movh, 0x4A64, At<Bx, 4>, At<Ax, 3>, At<ArRn1, 1>, At<ArStep1, 0>),
|
||||
INST(min2_vtr_movl, 0x4A00, At<Ax, 3>, At<Bx, 4>, At<ArRn1, 1>, At<ArStep1, 0>),
|
||||
INST(min2_vtr_movh, 0x4A04, At<Ax, 3>, At<Bx, 4>, At<ArRn1, 1>, At<ArStep1, 0>),
|
||||
INST(min2_vtr_movl, 0x4A20, At<Bx, 4>, At<Ax, 3>, At<ArRn1, 1>, At<ArStep1, 0>),
|
||||
INST(min2_vtr_movh, 0x4A24, At<Bx, 4>, At<Ax, 3>, At<ArRn1, 1>, At<ArStep1, 0>),
|
||||
INST(max2_vtr_movij, 0xD590, At<Ax, 6>, At<Bx, 5>, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>),
|
||||
INST(max2_vtr_movji, 0x45A0, At<Ax, 4>, At<Bx, 3>, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>),
|
||||
INST(min2_vtr_movij, 0xD2B8, At<Ax, 11>, At<Bx, 10>, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>),
|
||||
INST(min2_vtr_movji, 0x45E0, At<Ax, 4>, At<Bx, 3>, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>),
|
||||
|
||||
// <<< MOV ADDSUB >>>
|
||||
INST(mov_sv_app, 0x4B40, At<ArRn1, 3>, At<ArStep1, 2>, At<Bx, 0>, BSv, Sub, PP, Add, PP),
|
||||
INST(mov_sv_app, 0x9960, At<ArRn1, 4>, At<ArStep1Alt, 3>, At<Bx, 2>, BSv, Sub, PP, Add, PP),
|
||||
INST(mov_sv_app, 0x4B42, At<ArRn1, 3>, At<ArStep1, 2>, At<Bx, 0>, BSr, Sub, PP, Add, PP),
|
||||
INST(mov_sv_app, 0x99E0, At<ArRn1, 4>, At<ArStep1Alt, 3>, At<Bx, 2>, BSr, Sub, PP, Add, PP),
|
||||
INST(mov_sv_app, 0x5F4C, At<ArRn1, 1>, At<ArStep1, 0>, Const<Bx, 0>, BSv, Sub, PP, Sub, PP),
|
||||
INST(mov_sv_app, 0x8873, At<ArRn1, 8>, At<ArStep1, 3>, Const<Bx, 1>, BSv, Sub, PP, Sub, PP),
|
||||
INST(mov_sv_app, 0x9860, At<ArRn1, 4>, At<ArStep1Alt, 3>, At<Bx, 2>, BSv, Sub, PP, Sub, PP),
|
||||
INST(mov_sv_app, 0xDE9C, At<ArRn1, 1>, At<ArStep1, 0>, Const<Bx, 0>, BSr, Sub, PP, Sub, PP),
|
||||
INST(mov_sv_app, 0xD4B4, At<ArRn1, 1>, At<ArStep1, 0>, Const<Bx, 1>, BSr, Sub, PP, Sub, PP),
|
||||
INST(mov_sv_app, 0x98E0, At<ArRn1, 4>, At<ArStep1Alt, 3>, At<Bx, 2>, BSr, Sub, PP, Sub, PP),
|
||||
|
||||
// <<< CBS >>>
|
||||
INST(cbs, 0x9068, At<Axh, 0>, At<CbsCond, 8>),
|
||||
INST(cbs, 0xD49E, At<Axh, 8>, At<Bxh, 5>, At<CbsCond, 0>),
|
||||
INST(cbs, 0xD5C0, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>, At<CbsCond, 3>),
|
||||
|
||||
// [[[XXX_xy_XXX_xy_XXX]]]
|
||||
INST(mma, 0x4D88, AtNamed<Ax, 1>, SX, SY, SX, SY, BZr, Add, PP, Sub, PP),
|
||||
INST(mma, 0xD49D, AtNamed<Bx, 5>, SX, SY, SX, SY, BZr, Add, PP, Sub, PP),
|
||||
INST(mma, 0x5E24, AtNamed<Ab, 0>, SX, SY, SX, SY, BZr, Add, PP, Add, PP),
|
||||
INST(mma, 0x8061, AtNamed<Ab, 8>, SX, SY, SX, SY, BAc, Add, PP, Add, PP),
|
||||
INST(mma, 0x8071, AtNamed<Ab, 8>, SX, SY, SX, SY, BAc, Add, PP, Add, PA),
|
||||
INST(mma, 0x8461, AtNamed<Ab, 8>, SX, SY, SX, SY, BAc, Sub, PP, Sub, PP),
|
||||
INST(mma, 0x8471, AtNamed<Ab, 8>, SX, SY, SX, SY, BAc, Sub, PP, Sub, PA),
|
||||
INST(mma, 0xD484, AtNamed<Ab, 0>, SX, SY, SX, SY, BAc, Add, PA, Add, PA),
|
||||
INST(mma, 0xD4A0, AtNamed<Ab, 0>, SX, SY, SX, SY, BAc, Add, PP, Sub, PP),
|
||||
INST(mma, 0x4D89, AtNamed<Ax, 1>, SX, SY, SX, UY, BZr, Add, PP, Sub, PP),
|
||||
INST(mma, 0xD59D, AtNamed<Bx, 5>, SX, SY, SX, UY, BZr, Add, PP, Sub, PP),
|
||||
INST(mma, 0x5F24, AtNamed<Ab, 0>, SX, SY, SX, UY, BZr, Add, PP, Add, PP),
|
||||
INST(mma, 0x8069, AtNamed<Ab, 8>, SX, SY, SX, UY, BAc, Add, PP, Add, PP),
|
||||
INST(mma, 0x8079, AtNamed<Ab, 8>, SX, SY, SX, UY, BAc, Add, PP, Add, PA),
|
||||
INST(mma, 0x8469, AtNamed<Ab, 8>, SX, SY, SX, UY, BAc, Sub, PP, Sub, PP),
|
||||
INST(mma, 0x8479, AtNamed<Ab, 8>, SX, SY, SX, UY, BAc, Sub, PP, Sub, PA),
|
||||
INST(mma, 0xD584, AtNamed<Ab, 0>, SX, SY, SX, UY, BAc, Add, PA, Add, PA),
|
||||
INST(mma, 0xD5A0, AtNamed<Ab, 0>, SX, SY, SX, UY, BAc, Add, PP, Sub, PP),
|
||||
|
||||
// [[[XXX_mm_XXX_mm_XXX]]]
|
||||
INST(mma, 0xCA00, At<ArpRn1, 5>, At<ArpStep1, 3>, At<ArpStep1, 4>, EMod, EMod, AtNamed<Ab, 6>, UX, SY, UX, SY, BAc, Sub, PP, Sub, PA),
|
||||
INST(mma, 0xCA01, At<ArpRn1, 5>, At<ArpStep1, 3>, At<ArpStep1, 4>, EMod, EMod, AtNamed<Ab, 6>, UX, SY, SX, UY, BAc, Sub, PP, Sub, PA),
|
||||
INST(mma, 0xCA02, At<ArpRn1, 5>, At<ArpStep1, 3>, At<ArpStep1, 4>, EMod, EMod, AtNamed<Ab, 6>, UX, SY, UX, SY, BAc, Sub, PA, Sub, PA),
|
||||
INST(mma, 0xCA03, At<ArpRn1, 5>, At<ArpStep1, 3>, At<ArpStep1, 4>, EMod, EMod, AtNamed<Ab, 6>, UX, SY, SX, UY, BAc, Sub, PA, Sub, PA),
|
||||
INST(mma, 0xCA04, At<ArpRn1, 5>, At<ArpStep1, 3>, At<ArpStep1, 4>, EMod, EMod, AtNamed<Ab, 6>, UX, SY, UX, SY, BAc, Add, PP, Add, PA),
|
||||
INST(mma, 0xCA05, At<ArpRn1, 5>, At<ArpStep1, 3>, At<ArpStep1, 4>, EMod, EMod, AtNamed<Ab, 6>, UX, SY, SX, UY, BAc, Add, PP, Add, PA),
|
||||
INST(mma, 0xCA06, At<ArpRn1, 5>, At<ArpStep1, 3>, At<ArpStep1, 4>, EMod, EMod, AtNamed<Ab, 6>, UX, SY, UX, SY, BAc, Add, PA, Add, PA),
|
||||
INST(mma, 0xCA07, At<ArpRn1, 5>, At<ArpStep1, 3>, At<ArpStep1, 4>, EMod, EMod, AtNamed<Ab, 6>, UX, SY, SX, UY, BAc, Add, PA, Add, PA),
|
||||
INST(mma, 0xCB00, At<ArpRn1, 5>, At<ArpStep1, 3>, At<ArpStep1, 4>, EMod, EMod, AtNamed<Ab, 6>, SX, SY, UX, SY, BAc, Sub, PP, Sub, PP),
|
||||
INST(mma, 0xCB01, At<ArpRn1, 5>, At<ArpStep1, 3>, At<ArpStep1, 4>, EMod, EMod, AtNamed<Ab, 6>, SX, SY, SX, UY, BAc, Sub, PP, Sub, PP),
|
||||
INST(mma, 0xCB02, At<ArpRn1, 5>, At<ArpStep1, 3>, At<ArpStep1, 4>, EMod, EMod, AtNamed<Ab, 6>, SX, SY, UX, SY, BAc, Sub, PP, Sub, PA),
|
||||
INST(mma, 0xCB03, At<ArpRn1, 5>, At<ArpStep1, 3>, At<ArpStep1, 4>, EMod, EMod, AtNamed<Ab, 6>, SX, SY, SX, UY, BAc, Sub, PP, Sub, PA),
|
||||
INST(mma, 0xCB04, At<ArpRn1, 5>, At<ArpStep1, 3>, At<ArpStep1, 4>, EMod, EMod, AtNamed<Ab, 6>, SX, SY, UX, SY, BAc, Add, PP, Add, PP),
|
||||
INST(mma, 0xCB05, At<ArpRn1, 5>, At<ArpStep1, 3>, At<ArpStep1, 4>, EMod, EMod, AtNamed<Ab, 6>, SX, SY, SX, UY, BAc, Add, PP, Add, PP),
|
||||
INST(mma, 0xCB06, At<ArpRn1, 5>, At<ArpStep1, 3>, At<ArpStep1, 4>, EMod, EMod, AtNamed<Ab, 6>, SX, SY, UX, SY, BAc, Add, PP, Add, PA),
|
||||
INST(mma, 0xCB07, At<ArpRn1, 5>, At<ArpStep1, 3>, At<ArpStep1, 4>, EMod, EMod, AtNamed<Ab, 6>, SX, SY, SX, UY, BAc, Add, PP, Add, PA),
|
||||
|
||||
INST(mma, 0x0D30, At<ArpRn1, 3>, At<ArpStep1, 1>, At<ArpStep1, 2>, EMod, DMod, AtNamed<Ax, 0>, SX, SY, SX, UY, BAc, Add, PP, Add, PA),
|
||||
INST(mma, 0x0D20, At<ArpRn1, 3>, At<ArpStep1, 1>, At<ArpStep1, 2>, DMod, EMod, AtNamed<Ax, 0>, SX, SY, SX, UY, BAc, Add, PP, Add, PA),
|
||||
INST(mma, 0x4B50, At<ArpRn1, 3>, At<ArpStep1, 1>, At<ArpStep1, 2>, DMod, DMod, AtNamed<Ax, 0>, SX, SY, SX, UY, BAc, Add, PP, Add, PA),
|
||||
|
||||
INST(mma, 0x9861, At<ArpRn1, 4>, At<ArpStep1, 2>, At<ArpStep1, 3>, EMod, DMod, AtNamed<Ax, 8>, SX, SY, SX, SY, BAc, Add, PP, Add, PP),
|
||||
INST(mma, 0x9862, At<ArpRn1, 4>, At<ArpStep1, 2>, At<ArpStep1, 3>, DMod, EMod, AtNamed<Ax, 8>, SX, SY, SX, SY, BAc, Add, PP, Add, PP),
|
||||
INST(mma, 0x9863, At<ArpRn1, 4>, At<ArpStep1, 2>, At<ArpStep1, 3>, DMod, DMod, AtNamed<Ax, 8>, SX, SY, SX, SY, BAc, Add, PP, Add, PP),
|
||||
|
||||
INST(mma, 0x98E1, At<ArpRn1, 4>, At<ArpStep1, 2>, At<ArpStep1, 3>, EMod, DMod, AtNamed<Ax, 8>, SX, SY, SX, SY, BAc, Add, PP, Add, PA),
|
||||
INST(mma, 0x98E2, At<ArpRn1, 4>, At<ArpStep1, 2>, At<ArpStep1, 3>, DMod, EMod, AtNamed<Ax, 8>, SX, SY, SX, SY, BAc, Add, PP, Add, PA),
|
||||
INST(mma, 0x98E3, At<ArpRn1, 4>, At<ArpStep1, 2>, At<ArpStep1, 3>, DMod, DMod, AtNamed<Ax, 8>, SX, SY, SX, SY, BAc, Add, PP, Add, PA),
|
||||
|
||||
INST(mma, 0x80C8, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>, EMod, EMod, AtNamed<Ab, 10>, SX, SY, SX, SY, BAc, Add, PP, Sub, PP),
|
||||
INST(mma, 0x81C8, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>, EMod, EMod, AtNamed<Ab, 10>, SX, SY, SX, SY, BAc, Add, PP, Sub, PA),
|
||||
INST(mma, 0x82C8, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>, EMod, EMod, AtNamed<Ab, 10>, SX, SY, SX, SY, BZr, Add, PP, Add, PP),
|
||||
INST(mma, 0x83C8, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>, EMod, EMod, AtNamed<Ab, 10>, SX, SY, SX, SY, BZr, Add, PP, Add, PA),
|
||||
|
||||
INST(mma, 0x80C2, At<ArpRn1, 0>, At<ArpStep1, 8>, At<ArpStep1, 9>, EMod, EMod, AtNamed<Ab, 10>, SX, SY, SX, SY, BAc, Add, PP, Add, PA),
|
||||
INST(mma, 0x49C8, At<ArpRn1, 2>, At<ArpStep1, 0>, At<ArpStep1, 1>, EMod, EMod, AtNamed<Ab, 4>, SX, SY, SX, SY, BAc, Sub, PP, Sub, PA),
|
||||
INST(mma, 0x00C0, At<ArpRn1, 3>, At<ArpStep1, 1>, At<ArpStep1, 2>, EMod, EMod, AtNamed<Ab, 4>, SX, SY, SX, SY, BZr, Add, PP, Sub, PP),
|
||||
INST(mma, 0x00C1, At<ArpRn1, 3>, At<ArpStep1, 1>, At<ArpStep1, 2>, EMod, EMod, AtNamed<Ab, 4>, SX, SY, SX, SY, BZr, Add, PP, Sub, PA),
|
||||
INST(mma, 0xD7A0, At<ArpRn1, 3>, At<ArpStep1, 1>, At<ArpStep1, 2>, EMod, EMod, AtNamed<Ax, 4>, SX, SY, SX, SY, BSv, Add, PP, Add, PP),
|
||||
INST(mma, 0xD7A1, At<ArpRn1, 3>, At<ArpStep1, 1>, At<ArpStep1, 2>, EMod, EMod, AtNamed<Ax, 4>, SX, SY, SX, SY, BSr, Add, PP, Add, PP),
|
||||
|
||||
INST(mma, 0xC800, At<ArpRn2, 4>, At<ArpStep2, 0>, At<ArpStep2, 2>, EMod, EMod, AtNamed<Ab, 6>, SX, SY, SX, SY, BAc, Add, PP, Add, PP),
|
||||
INST(mma, 0xC900, At<ArpRn2, 4>, At<ArpStep2, 0>, At<ArpStep2, 2>, EMod, EMod, AtNamed<Ab, 6>, SX, SY, SX, SY, BAc, Sub, PP, Sub, PP),
|
||||
|
||||
// [[[XXX_mx_XXX_xy_XXX]]]
|
||||
INST(mma_mx_xy, 0xD5E0, At<ArRn1, 1>, At<ArStep1, 0>, AtNamed<Ax, 3>, SX, SY, SX, SY, BAc, Sub, PP, Sub, PP),
|
||||
INST(mma_mx_xy, 0xD5E4, At<ArRn1, 1>, At<ArStep1, 0>, AtNamed<Ax, 3>, SX, SY, SX, SY, BAc, Add, PP, Add, PP),
|
||||
|
||||
// [[[XXX_xy_XXX_mx_XXX]]]
|
||||
INST(mma_xy_mx, 0x8862, At<ArRn1, 4>, At<ArStep1, 3>, AtNamed<Ax, 8>, SX, SY, SX, SY, BAc, Sub, PP, Sub, PP),
|
||||
INST(mma_xy_mx, 0x8A62, At<ArRn1, 4>, At<ArStep1, 3>, AtNamed<Ax, 8>, SX, SY, SX, SY, BAc, Add, PP, Add, PP),
|
||||
|
||||
// [[[XXX_my_XXX_my_XXX]]]
|
||||
INST(mma_my_my, 0x4DA0, At<ArRn1, 3>, At<ArStep1, 2>, AtNamed<Ax, 4>, SX, SY, SX, UY, BAc, Sub, PP, Sub, PP),
|
||||
INST(mma_my_my, 0x4DA1, At<ArRn1, 3>, At<ArStep1, 2>, AtNamed<Ax, 4>, SX, SY, SX, UY, BAc, Sub, PP, Sub, PA),
|
||||
INST(mma_my_my, 0x4DA2, At<ArRn1, 3>, At<ArStep1, 2>, AtNamed<Ax, 4>, SX, SY, SX, UY, BAc, Add, PP, Add, PP),
|
||||
INST(mma_my_my, 0x4DA3, At<ArRn1, 3>, At<ArStep1, 2>, AtNamed<Ax, 4>, SX, SY, SX, UY, BAc, Add, PP, Add, PA),
|
||||
|
||||
INST(mma_my_my, 0x94E0, At<ArRn1, 4>, At<ArStep1, 3>, AtNamed<Ax, 8>, SX, SY, SX, SY, BAc, Sub, PP, Sub, PP),
|
||||
INST(mma_my_my, 0x94E1, At<ArRn1, 4>, At<ArStep1, 3>, AtNamed<Ax, 8>, SX, SY, UX, SY, BAc, Sub, PP, Sub, PP),
|
||||
INST(mma_my_my, 0x94E2, At<ArRn1, 4>, At<ArStep1, 3>, AtNamed<Ax, 8>, SX, SY, SX, SY, BAc, Sub, PP, Sub, PA),
|
||||
INST(mma_my_my, 0x94E3, At<ArRn1, 4>, At<ArStep1, 3>, AtNamed<Ax, 8>, SX, SY, UX, SY, BAc, Sub, PP, Sub, PA),
|
||||
INST(mma_my_my, 0x94E4, At<ArRn1, 4>, At<ArStep1, 3>, AtNamed<Ax, 8>, SX, SY, SX, SY, BAc, Add, PP, Add, PP),
|
||||
INST(mma_my_my, 0x94E5, At<ArRn1, 4>, At<ArStep1, 3>, AtNamed<Ax, 8>, SX, SY, UX, SY, BAc, Add, PP, Add, PP),
|
||||
INST(mma_my_my, 0x94E6, At<ArRn1, 4>, At<ArStep1, 3>, AtNamed<Ax, 8>, SX, SY, SX, SY, BAc, Add, PP, Add, PA),
|
||||
INST(mma_my_my, 0x94E7, At<ArRn1, 4>, At<ArStep1, 3>, AtNamed<Ax, 8>, SX, SY, UX, SY, BAc, Add, PP, Add, PA),
|
||||
|
||||
// [[[XXX_xy_XXX_xy_XXX_mov]]]
|
||||
INST(mma_mov, 0x4FA0, At<Axh, 6>, At<Bxh, 2>, At<ArRn1, 1>, At<ArStep1, 0>, AtNamed<Ab, 3>, SX, SY, SX, SY, BAc, Add, PP, Add, PP),
|
||||
INST(mma_mov, 0xD3A0, At<Axh, 6>, At<Bxh, 2>, At<ArRn1, 1>, At<ArStep1, 0>, AtNamed<Ab, 3>, SX, SY, SX, SY, BAc, Add, PP, Sub, PP),
|
||||
INST(mma_mov, 0x80D0, At<Axh, 9>, At<Bxh, 8>, At<ArRn1, 3>, At<ArStep1, 2>, AtNamed<Ax, 10>, SX, SY, SX, SY, BSv, Add, PP, Sub, PP),
|
||||
INST(mma_mov, 0x80D1, At<Axh, 9>, At<Bxh, 8>, At<ArRn1, 3>, At<ArStep1, 2>, AtNamed<Ax, 10>, SX, SY, SX, SY, BSr, Add, PP, Sub, PP),
|
||||
INST(mma_mov, 0x80D2, At<Axh, 9>, At<Bxh, 8>, At<ArRn1, 3>, At<ArStep1, 2>, AtNamed<Ax, 10>, SX, SY, SX, SY, BSv, Add, PP, Add, PP),
|
||||
INST(mma_mov, 0x80D3, At<Axh, 9>, At<Bxh, 8>, At<ArRn1, 3>, At<ArStep1, 2>, AtNamed<Ax, 10>, SX, SY, SX, SY, BSr, Add, PP, Add, PP),
|
||||
INST(mma_mov, 0x5818, At<ArRn2, 7>, At<ArStep1, 6>, AtNamed<Ax, 0>, SX, SY, SX, SY, BSv, Add, PP, Sub, PP),
|
||||
INST(mma_mov, 0x5838, At<ArRn2, 7>, At<ArStep1, 6>, AtNamed<Ax, 0>, SX, SY, SX, SY, BSr, Add, PP, Sub, PP),
|
||||
|
||||
INST(addhp, 0x90E0, At<ArRn2, 2>, At<ArStep2, 0>, At<Px, 4>, At<Ax, 8>),
|
||||
};
|
||||
|
||||
#undef INST
|
||||
#undef EXCEPT
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
||||
template <typename V>
|
||||
Matcher<V> Decode(u16 instruction) {
|
||||
static const auto table = GetDecodeTable<V>();
|
||||
|
||||
const auto matches_instruction = [instruction](const auto& matcher) {
|
||||
return matcher.Matches(instruction);
|
||||
};
|
||||
|
||||
auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
|
||||
if (iter == table.end()) {
|
||||
return Matcher<V>::AllMatcher([](V& v, u16 opcode, u16) { return v.undefined(opcode); });
|
||||
} else {
|
||||
auto other = std::find_if(iter + 1, table.end(), matches_instruction);
|
||||
ASSERT(other == table.end());
|
||||
return *iter;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
std::vector<Matcher<V>> GetDecoderTable() {
|
||||
std::vector<Matcher<V>> table;
|
||||
table.reserve(0x10000);
|
||||
for (u32 i = 0; i < 0x10000; ++i) {
|
||||
table.push_back(Decode<V>((u16)i));
|
||||
}
|
||||
return table;
|
||||
}
|
80
third_party/teakra/src/decoder.md
vendored
Normal file
80
third_party/teakra/src/decoder.md
vendored
Normal file
|
@ -0,0 +1,80 @@
|
|||
# Teak Instruction Set Encoding
|
||||
|
||||
Each opcode is one- or two-word wide. In this code base, two-word wide opcodes are generally referred as "expanded". The optional second word is used purely for parameters, so the opcode family can always be determined from the first word (which then determine whether there is a second word). The encoding is very messy. The general theme of the encoding are
|
||||
- The encoding seems first designed for TeakLite (which is already a mess), then additional opcodes from Teak squeezed in formerly unused locations.
|
||||
- While registers `r0`~`r7` are generally at equal status in the computation model, they are not always in the opcodes. Specifically, as `r6` doesn't present in TeakLite, and because of the opcode squeezing, many opcodes involving `r6` are encoded separately with seemingly random pattern.
|
||||
- Similarly, there are also opcodes that are in the same family but have different pattern for different arguments.
|
||||
- Some specific combination of arguments can be invalid for one opcode, and this specific pattern can be used for a totally different opcode.
|
||||
- Some opcodes have unused bits, where setting either 0 or 1 has no effect.
|
||||
|
||||
## The format of the decoder table
|
||||
|
||||
`decoder.h` contains the full table of the instruction set. Each entry in the table is in the form:
|
||||
```
|
||||
INST(name, pattern[, slot0[...]])[.EXCEPT(except_slot)[..]]
|
||||
```
|
||||
An entry represents match rules for an opcode family, in the following procedure:
|
||||
- The opcode is matched against the pattern, with some bits masked out (i.e. not compared). The masked bits are provided by all the `slot`s. If the pattern doesn't match, try a different entry.
|
||||
- Then the opcode is matched against each EXCEPT clause. A specific pattern and masked bits are provided by the `except_slot`. If any pattern in EXCEPT matches, try a different entry.
|
||||
- Now this entry is successfully matched with the opcode. Each `slot` extract their relavent bits from the opcode and convert them into arguments.
|
||||
- The function `Visitor::name` is called with the converted arguments.
|
||||
|
||||
## Comparison between the decoder table and gbatek
|
||||
|
||||
The decoder table is derived from the table in gbatek, but rearranged a little according to the opcode families and patterns. The notation of the two table are comparable. For example:
|
||||
|
||||
```
|
||||
teakra: INST(push, 0xD7C8, At<Abe, 1>, Unused<0>)
|
||||
gbatek: D7C8h TL2 push Abe@1, Unused1@0
|
||||
```
|
||||
This means that the opcode has a parameter `Abe` at bit 1 (which is 2-bit long, defined by operand `Abe`), and an unused bit at bit 0. The rest bit of the opcode (bit 3~15) should match the pattern `0xD7C8`. The assembly of this opcode would be like `push <Abe>`.
|
||||
|
||||
Sometimes there is a 18-bit address parameter that is split into two parts:
|
||||
|
||||
```
|
||||
teakra: INST(br, 0x4180, At<Address18_16, 16>, At<Address18_2, 4>, At<Cond, 0>)
|
||||
gbatek: 4180h TL br Address18@16and4, Cond@0
|
||||
```
|
||||
Note that the existence of `At<..., 16>` also indicates that this opcode is 2-word long. The pattern `0x4180` is only matched against the first word.
|
||||
|
||||
Some opcodes that have the same pattern are merged into one opcode in teakra. For example
|
||||
```
|
||||
teakra: INST(alm, 0xA000, At<Alm, 9>, At<MemImm8, 0>, At<Ax, 8>),
|
||||
gbatek: {
|
||||
A000h TL or MemImm8@0, Ax@8
|
||||
A200h TL and MemImm8@0, Ax@8
|
||||
...
|
||||
BE00h TL cmpu MemImm8@0, Ax@8
|
||||
}
|
||||
```
|
||||
Here the operation name is also treated as an operand `Alm` in teakra.
|
||||
|
||||
Opcodes with constants that has special encoding are marked in their opcode names:
|
||||
```
|
||||
teakra: INST(sub_p1, 0xD4B9, At<Ax, 8>),
|
||||
gbatek: D4B9h TL2 sub p1, Ax@8
|
||||
```
|
||||
|
||||
However, if several opcodes with constants have different encoding but similar semantics, the constants are placed in the parameter list and delegate to the same opcode
|
||||
```
|
||||
teakra: {
|
||||
INST(alm_r6, 0xD388, Const<Alm, 0>, At<Ax, 4>),
|
||||
INST(alm_r6, 0xD389, Const<Alm, 1>, At<Ax, 4>),
|
||||
...
|
||||
INST(alm_r6, 0x9462, Const<Alm, 8>, At<Ax, 0>),
|
||||
...
|
||||
INST(alm_r6, 0x5F41, Const<Alm, 13>, Const<Ax, 0>),
|
||||
INST(alm_r6, 0x9062, Const<Alm, 14>, At<Ax, 8>, Unused<0>),
|
||||
INST(alm_r6, 0x8A63, Const<Alm, 15>, At<Ax, 3>),
|
||||
}
|
||||
gbatek: {
|
||||
D388h TL2 or r6, Ax@4
|
||||
D389h TL2 and r6, Ax@4
|
||||
...
|
||||
9462h TL2 msu y0, r6, Ax@0 // y0 is implied by msu
|
||||
...
|
||||
5F41h TL2 sqr r6 // Ax is implied/unused for sqr
|
||||
9062h TL2 sqra r6, Ax@8, Unused1@0
|
||||
8A63h TL2 cmpu r6, Ax@3
|
||||
}
|
||||
```
|
1763
third_party/teakra/src/disassembler.cpp
vendored
Normal file
1763
third_party/teakra/src/disassembler.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
23
third_party/teakra/src/disassembler_c.cpp
vendored
Normal file
23
third_party/teakra/src/disassembler_c.cpp
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
#include "teakra/disassembler.h"
|
||||
#include "teakra/disassembler_c.h"
|
||||
|
||||
extern "C" {
|
||||
bool Teakra_Disasm_NeedExpansion(uint16_t opcode) {
|
||||
return Teakra::Disassembler::NeedExpansion(opcode);
|
||||
}
|
||||
|
||||
size_t Teakra_Disasm_Do(char* dst, size_t dstlen,
|
||||
uint16_t opcode, uint16_t expansion /*= 0*/) {
|
||||
std::string r = Teakra::Disassembler::Do(opcode, expansion);
|
||||
|
||||
if (dst) {
|
||||
size_t i = 0;
|
||||
for (; i < (dstlen-1) && i < r.length(); ++i) {
|
||||
dst[i] = r[i];
|
||||
}
|
||||
dst[dstlen-1] = '\0';
|
||||
}
|
||||
|
||||
return r.length();
|
||||
}
|
||||
}
|
137
third_party/teakra/src/dma.cpp
vendored
Normal file
137
third_party/teakra/src/dma.cpp
vendored
Normal file
|
@ -0,0 +1,137 @@
|
|||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include "ahbm.h"
|
||||
#include "dma.h"
|
||||
#include "shared_memory.h"
|
||||
|
||||
namespace Teakra {
|
||||
|
||||
void Dma::Reset() {
|
||||
enable_channel = 0;
|
||||
active_channel = 0;
|
||||
channels = {};
|
||||
}
|
||||
|
||||
void Dma::DoDma(u16 channel) {
|
||||
channels[channel].Start();
|
||||
|
||||
channels[channel].ahbm_channel = ahbm.GetChannelForDma(channel);
|
||||
|
||||
// TODO: actually Tick this according to global Tick;
|
||||
while (channels[channel].running)
|
||||
channels[channel].Tick(*this);
|
||||
|
||||
interrupt_handler();
|
||||
}
|
||||
|
||||
void Dma::Channel::Start() {
|
||||
running = 1;
|
||||
current_src = addr_src_low | ((u32)addr_src_high << 16);
|
||||
current_dst = addr_dst_low | ((u32)addr_dst_high << 16);
|
||||
counter0 = 0;
|
||||
counter1 = 0;
|
||||
counter2 = 0;
|
||||
}
|
||||
|
||||
void Dma::Channel::Tick(Dma& parent) {
|
||||
static constexpr u32 DataMemoryOffset = 0x20000;
|
||||
if (dword_mode) {
|
||||
u32 value = 0;
|
||||
switch (src_space) {
|
||||
case 0: {
|
||||
u32 l = current_src & 0xFFFFFFFE;
|
||||
u32 h = current_src | 1;
|
||||
value = parent.shared_memory.ReadWord(DataMemoryOffset + l) |
|
||||
((u32)parent.shared_memory.ReadWord(DataMemoryOffset + h) << 16);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
std::printf("Unimplemented MMIO space");
|
||||
value = 0;
|
||||
break;
|
||||
case 7:
|
||||
value = parent.ahbm.Read32(ahbm_channel, current_src);
|
||||
break;
|
||||
default:
|
||||
std::printf("Unknown SrcSpace %04X\n", src_space);
|
||||
}
|
||||
|
||||
switch (dst_space) {
|
||||
case 0: {
|
||||
u32 l = current_dst & 0xFFFFFFFE;
|
||||
u32 h = current_dst | 1;
|
||||
parent.shared_memory.WriteWord(DataMemoryOffset + l, (u16)value);
|
||||
parent.shared_memory.WriteWord(DataMemoryOffset + h, (u16)(value >> 16));
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
std::printf("Unimplemented MMIO space");
|
||||
value = 0;
|
||||
break;
|
||||
case 7:
|
||||
parent.ahbm.Write32(ahbm_channel, current_dst, value);
|
||||
break;
|
||||
default:
|
||||
std::printf("Unknown DstSpace %04X\n", dst_space);
|
||||
}
|
||||
|
||||
counter0 += 2;
|
||||
} else {
|
||||
u16 value = 0;
|
||||
switch (src_space) {
|
||||
case 0:
|
||||
value = parent.shared_memory.ReadWord(DataMemoryOffset + current_src);
|
||||
break;
|
||||
case 1:
|
||||
std::printf("Unimplemented MMIO space");
|
||||
value = 0;
|
||||
break;
|
||||
case 7:
|
||||
value = parent.ahbm.Read16(ahbm_channel, current_src);
|
||||
break;
|
||||
default:
|
||||
std::printf("Unknown SrcSpace %04X\n", src_space);
|
||||
}
|
||||
|
||||
switch (dst_space) {
|
||||
case 0:
|
||||
parent.shared_memory.WriteWord(DataMemoryOffset + current_dst, value);
|
||||
break;
|
||||
case 1:
|
||||
std::printf("Unimplemented MMIO space");
|
||||
value = 0;
|
||||
break;
|
||||
case 7:
|
||||
parent.ahbm.Write16(ahbm_channel, current_dst, value);
|
||||
break;
|
||||
default:
|
||||
std::printf("Unknown DstSpace %04X\n", dst_space);
|
||||
}
|
||||
|
||||
counter0 += 1;
|
||||
}
|
||||
|
||||
if (counter0 >= size0) {
|
||||
counter0 = 0;
|
||||
counter1 += 1;
|
||||
if (counter1 >= size1) {
|
||||
counter1 = 0;
|
||||
counter2 += 1;
|
||||
if (counter2 >= size2) {
|
||||
running = 0;
|
||||
return;
|
||||
} else {
|
||||
current_src += src_step2;
|
||||
current_dst += dst_step2;
|
||||
}
|
||||
} else {
|
||||
current_src += src_step1;
|
||||
current_dst += dst_step1;
|
||||
}
|
||||
} else {
|
||||
current_src += src_step0;
|
||||
current_dst += dst_step0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Teakra
|
201
third_party/teakra/src/dma.h
vendored
Normal file
201
third_party/teakra/src/dma.h
vendored
Normal file
|
@ -0,0 +1,201 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include "common_types.h"
|
||||
|
||||
namespace Teakra {
|
||||
|
||||
struct SharedMemory;
|
||||
class Ahbm;
|
||||
|
||||
class Dma {
|
||||
public:
|
||||
Dma(SharedMemory& shared_memory, Ahbm& ahbm) : shared_memory(shared_memory), ahbm(ahbm) {}
|
||||
|
||||
void Reset();
|
||||
|
||||
void EnableChannel(u16 value) {
|
||||
enable_channel = value;
|
||||
}
|
||||
u16 GetChannelEnabled() const {
|
||||
return enable_channel;
|
||||
}
|
||||
|
||||
void ActivateChannel(u16 value) {
|
||||
active_channel = value;
|
||||
}
|
||||
u16 GetActiveChannel() const {
|
||||
return active_channel;
|
||||
}
|
||||
|
||||
void SetAddrSrcLow(u16 value) {
|
||||
channels[active_channel].addr_src_low = value;
|
||||
}
|
||||
u16 GetAddrSrcLow() const {
|
||||
return channels[active_channel].addr_src_low;
|
||||
}
|
||||
|
||||
void SetAddrSrcHigh(u16 value) {
|
||||
channels[active_channel].addr_src_high = value;
|
||||
}
|
||||
u16 GetAddrSrcHigh() const {
|
||||
return channels[active_channel].addr_src_high;
|
||||
}
|
||||
|
||||
void SetAddrDstLow(u16 value) {
|
||||
channels[active_channel].addr_dst_low = value;
|
||||
}
|
||||
u16 GetAddrDstLow() const {
|
||||
return channels[active_channel].addr_dst_low;
|
||||
}
|
||||
|
||||
void SetAddrDstHigh(u16 value) {
|
||||
channels[active_channel].addr_dst_high = value;
|
||||
}
|
||||
u16 GetAddrDstHigh() const {
|
||||
return channels[active_channel].addr_dst_high;
|
||||
}
|
||||
|
||||
void SetSize0(u16 value) {
|
||||
channels[active_channel].size0 = value;
|
||||
}
|
||||
u16 GetSize0() const {
|
||||
return channels[active_channel].size0;
|
||||
}
|
||||
|
||||
void SetSize1(u16 value) {
|
||||
channels[active_channel].size1 = value;
|
||||
}
|
||||
u16 GetSize1() const {
|
||||
return channels[active_channel].size1;
|
||||
}
|
||||
|
||||
void SetSize2(u16 value) {
|
||||
channels[active_channel].size2 = value;
|
||||
}
|
||||
u16 GetSize2() const {
|
||||
return channels[active_channel].size2;
|
||||
}
|
||||
|
||||
void SetSrcStep0(u16 value) {
|
||||
channels[active_channel].src_step0 = value;
|
||||
}
|
||||
u16 GetSrcStep0() const {
|
||||
return channels[active_channel].src_step0;
|
||||
}
|
||||
|
||||
void SetDstStep0(u16 value) {
|
||||
channels[active_channel].dst_step0 = value;
|
||||
}
|
||||
u16 GetDstStep0() const {
|
||||
return channels[active_channel].dst_step0;
|
||||
}
|
||||
|
||||
void SetSrcStep1(u16 value) {
|
||||
channels[active_channel].src_step1 = value;
|
||||
}
|
||||
u16 GetSrcStep1() const {
|
||||
return channels[active_channel].src_step1;
|
||||
}
|
||||
|
||||
void SetDstStep1(u16 value) {
|
||||
channels[active_channel].dst_step1 = value;
|
||||
}
|
||||
u16 GetDstStep1() const {
|
||||
return channels[active_channel].dst_step1;
|
||||
}
|
||||
|
||||
void SetSrcStep2(u16 value) {
|
||||
channels[active_channel].src_step2 = value;
|
||||
}
|
||||
u16 GetSrcStep2() const {
|
||||
return channels[active_channel].src_step2;
|
||||
}
|
||||
|
||||
void SetDstStep2(u16 value) {
|
||||
channels[active_channel].dst_step2 = value;
|
||||
}
|
||||
u16 GetDstStep2() const {
|
||||
return channels[active_channel].dst_step2;
|
||||
}
|
||||
|
||||
void SetSrcSpace(u16 value) {
|
||||
channels[active_channel].src_space = value;
|
||||
}
|
||||
u16 GetSrcSpace() const {
|
||||
return channels[active_channel].src_space;
|
||||
}
|
||||
|
||||
void SetDstSpace(u16 value) {
|
||||
channels[active_channel].dst_space = value;
|
||||
}
|
||||
u16 GetDstSpace() const {
|
||||
return channels[active_channel].dst_space;
|
||||
}
|
||||
|
||||
void SetDwordMode(u16 value) {
|
||||
channels[active_channel].dword_mode = value;
|
||||
}
|
||||
u16 GetDwordMode() const {
|
||||
return channels[active_channel].dword_mode;
|
||||
}
|
||||
|
||||
void SetY(u16 value) {
|
||||
channels[active_channel].y = value;
|
||||
}
|
||||
u16 GetY() const {
|
||||
return channels[active_channel].y;
|
||||
}
|
||||
|
||||
void SetZ(u16 value) {
|
||||
channels[active_channel].z = value;
|
||||
|
||||
if (value == 0x40C0) {
|
||||
DoDma(active_channel);
|
||||
}
|
||||
}
|
||||
u16 GetZ() const {
|
||||
return channels[active_channel].z;
|
||||
}
|
||||
|
||||
void DoDma(u16 channel);
|
||||
|
||||
void SetInterruptHandler(std::function<void()> handler) {
|
||||
interrupt_handler = std::move(handler);
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void()> interrupt_handler;
|
||||
|
||||
u16 enable_channel = 0;
|
||||
u16 active_channel = 0;
|
||||
|
||||
struct Channel {
|
||||
u16 addr_src_low = 0, addr_src_high = 0;
|
||||
u16 addr_dst_low = 0, addr_dst_high = 0;
|
||||
u16 size0 = 0, size1 = 0, size2 = 0;
|
||||
u16 src_step0 = 0, dst_step0 = 0;
|
||||
u16 src_step1 = 0, dst_step1 = 0;
|
||||
u16 src_step2 = 0, dst_step2 = 0;
|
||||
u16 src_space = 0, dst_space = 0;
|
||||
u16 dword_mode = 0;
|
||||
u16 y = 0;
|
||||
u16 z = 0;
|
||||
|
||||
u32 current_src = 0, current_dst = 0;
|
||||
u16 counter0 = 0, counter1 = 0, counter2 = 0;
|
||||
u16 running = 0;
|
||||
u16 ahbm_channel = 0;
|
||||
|
||||
void Start();
|
||||
void Tick(Dma& parent);
|
||||
};
|
||||
|
||||
std::array<Channel, 8> channels;
|
||||
|
||||
SharedMemory& shared_memory;
|
||||
Ahbm& ahbm;
|
||||
};
|
||||
|
||||
} // namespace Teakra
|
99
third_party/teakra/src/dma.md
vendored
Normal file
99
third_party/teakra/src/dma.md
vendored
Normal file
|
@ -0,0 +1,99 @@
|
|||
# DMA
|
||||
|
||||
## MMIO Layout
|
||||
|
||||
```
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0180 | | | | | | | | | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0182 | | | | | | | | | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0184 | | | | | | | | |C7 |C6 |C5 |C4 |C3 |C2 |C1 |C0 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0186 | | | | | | | | | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0188 | | | | | | | | | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x018A | | | | | | | | | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x018C | |E7 |E6 |E5 |E4 |E3 |E2 |E1 |E0 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x018E | | S3 | | S2 | | S1 | | S0 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0190 | | S7 | | S6 | | S5 | | S4 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|
||||
C0..C7: enable channel 0..7?
|
||||
E0..E7: end of transfer flag for channel 0..7?
|
||||
S0..S7: some sort of slots for channel 0..7? are initialized as value 0..7
|
||||
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x01BE | | CHANNEL |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x01C0 | SRC_ADDR_LOW |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x01C2 | SRC_ADDR_HIGH |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x01C4 | DST_ADDR_LOW |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x01C6 | DST_ADDR_HIGH |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x01C8 | SIZE0 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x01CA | SIZE1 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x01CC | SIZE2 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x01CE | SRC_STEP0 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x01D0 | DST_STEP0 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x01D2 | SRC_STEP1 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x01D4 | DST_STEP1 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x01D6 | SRC_STEP2 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x01D8 | DST_STEP2 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x01DA | | - |DWM| ? | | DST_SPACE | SRC_SPACE |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x01DC | - | | | ? | ? | ? | | | | | - | | | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x01DE |RST|STR| | ? | ? | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|
||||
CHANNEL: set the channel to bind with register +0x01C0..+0x01DE
|
||||
SRC_SPACE / DST_SPACE: the memory space associated with SRC_ADDR and DST_ADDR
|
||||
- 0: DSP main memory
|
||||
- 1: MMIO
|
||||
- 5: Program memory (only for DST_SPACE) (untested)
|
||||
- 7: connect to AHBM / external memory
|
||||
|
||||
```
|
||||
|
||||
## Data Transfer Engine
|
||||
|
||||
The engine is used for transferring data between memory locations. One transfer session is like copying a three-dimensional array, with configurable strides, from the specified source and destination. `SRC_ADDR` and `DST_ADDR` specify the starting address of data source and destination. `SIZE0`, `SIZE1` and `SIZE2` specify the element count for each dimension, where `SIZE0` specifies the finest dimension in the linear memory space. (`SRC_`/`DST_`)`STEP0`, `STEP1` and `STEP2` specify the address stepping between elements for each dimension. Below is an example showing how it works.
|
||||
|
||||
With the source address configuration:
|
||||
|
||||
```
|
||||
SRC_ADDR = 0
|
||||
SIZE0 = 3
|
||||
SIZE1 = 5
|
||||
SIZE2 = 2
|
||||
SRC_STEP0 = 2
|
||||
SRC_STEP1 = 1
|
||||
SRC_STEP2 = 7
|
||||
```
|
||||
The data transfer copies data from the following addresses, where `,` represents dimension 0 stepping, `/` for dimension 1 and `||` for dimension 2.
|
||||
```
|
||||
0, 2, 4/ 5, 7, 9/ 10, 12, 14/ 15, 17, 19/ 20, 22, 24||
|
||||
31, 33, 35/ 36, 38, 40/ 41, 43, 45/ 46, 48, 50/ 51, 53, 55
|
||||
```
|
||||
The same rule applies to the destination address as well. Note that `SIZEx` can be 0, in which case they have the same effect as being 1.
|
||||
|
||||
One element can be either a 16-bit(`DWM = 0`) word or a 32-bit double word (`DWM = 1`). In the 32-bit double word mode, the address stepping method is the same as described above, except for `SIZE0`, where one 32-bit element is counted as 2 for the dimension 0 counter, so effectively `SIZE0` value is twice as the actual count of 32-bit elements copied in a dimension 0 stride. The double word mode also automatically aligns down the addresses to 32-bit boundaries.
|
||||
|
||||
When the memory space is specified as 7 (AHBM), it performs data transfer from/to external memory. Take 3DS for example, the external memory is FCRAM, and the address is specified as FCRAM's physical address as-is. Because the external memory has 8-bit addressing, the address resolution mismatch between DSP memory and FCRAM can make it unintuitive. Just keep in mind that the `STEP` value is also added to the address as-is, so `STEP = 1` means stepping 8-bit for FCRAM, while stepping 16-bit for DSP memory. There are also more alignment requirement on the external memory address, but it is handled by AHBM. See [ahbm.md](ahbm.md) for detail.
|
9
third_party/teakra/src/dsp1_reader/CMakeLists.txt
vendored
Normal file
9
third_party/teakra/src/dsp1_reader/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
include(CreateDirectoryGroups)
|
||||
|
||||
add_executable(dsp1_reader
|
||||
main.cpp
|
||||
)
|
||||
create_target_directory_groups(dsp1_reader)
|
||||
target_link_libraries(dsp1_reader PRIVATE teakra)
|
||||
target_include_directories(dsp1_reader PRIVATE .)
|
||||
target_compile_options(dsp1_reader PRIVATE ${TEAKRA_CXX_FLAGS})
|
125
third_party/teakra/src/dsp1_reader/main.cpp
vendored
Normal file
125
third_party/teakra/src/dsp1_reader/main.cpp
vendored
Normal file
|
@ -0,0 +1,125 @@
|
|||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <teakra/disassembler.h>
|
||||
#include "../common_types.h"
|
||||
|
||||
class Dsp1 {
|
||||
public:
|
||||
Dsp1(std::vector<u8> raw);
|
||||
|
||||
struct Header {
|
||||
u8 signature[0x100];
|
||||
u8 magic[4];
|
||||
u32 binary_size;
|
||||
u16 memory_layout;
|
||||
u16 padding;
|
||||
u8 unknown;
|
||||
u8 filter_segment_type;
|
||||
u8 num_segments;
|
||||
u8 flags;
|
||||
u32 filter_segment_address;
|
||||
u32 filter_segment_size;
|
||||
u64 zero;
|
||||
struct Segment {
|
||||
u32 offset;
|
||||
u32 address;
|
||||
u32 size;
|
||||
u8 pa, pb, pc;
|
||||
u8 memory_type;
|
||||
u8 sha256[0x20];
|
||||
} segments[10];
|
||||
};
|
||||
|
||||
static_assert(sizeof(Header) == 0x300);
|
||||
|
||||
struct Segment {
|
||||
std::vector<u8> data;
|
||||
u8 memory_type;
|
||||
u32 target;
|
||||
};
|
||||
|
||||
std::vector<Segment> segments;
|
||||
bool recv_data_on_start;
|
||||
};
|
||||
|
||||
Dsp1::Dsp1(std::vector<u8> raw) {
|
||||
Header header;
|
||||
std::memcpy(&header, raw.data(), sizeof(header));
|
||||
|
||||
recv_data_on_start = (header.flags & 1) != 0;
|
||||
|
||||
printf("Memory layout = %04X\n", header.memory_layout);
|
||||
printf("Unk = %02X\n", header.unknown);
|
||||
printf("Filter segment type = %d\n", header.filter_segment_type);
|
||||
printf("Num segments = %d\n", header.num_segments);
|
||||
printf("Flags = %d\n", header.flags);
|
||||
printf("Filter address = 2 * %08X\n", header.filter_segment_address);
|
||||
printf("Filter size = %08X\n", header.filter_segment_size);
|
||||
|
||||
for (u32 i = 0; i < header.num_segments; ++i) {
|
||||
Segment segment;
|
||||
segment.data =
|
||||
std::vector<u8>(raw.begin() + header.segments[i].offset,
|
||||
raw.begin() + header.segments[i].offset + header.segments[i].size);
|
||||
segment.memory_type = header.segments[i].memory_type;
|
||||
segment.target = header.segments[i].address; /*header.segments[i].address * 2 +
|
||||
(segment.memory_type == 2 ? 0x1FF40000 : 0x1FF00000);*/
|
||||
segments.push_back(segment);
|
||||
|
||||
printf("[Segment %d]\n", i);
|
||||
printf("memory_type = %d\n", segment.memory_type);
|
||||
printf("target = %08X\n", segment.target);
|
||||
printf("size = %08X\n", (u32)segment.data.size());
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 3)
|
||||
return -1;
|
||||
|
||||
FILE* file = fopen(argv[1], "rb");
|
||||
std::vector<u8> raw;
|
||||
u8 ch;
|
||||
while (fread(&ch, 1, 1, file) == 1) {
|
||||
raw.push_back(ch);
|
||||
}
|
||||
fclose(file);
|
||||
Dsp1 dsp(raw);
|
||||
|
||||
file = fopen(argv[2], "wt");
|
||||
|
||||
for (const auto& segment : dsp.segments) {
|
||||
if (segment.memory_type == 0 || segment.memory_type == 1) {
|
||||
fprintf(file, "\n>>>>>>>> Segment <<<<<<<<\n\n");
|
||||
for (unsigned pos = 0; pos < segment.data.size(); pos += 2) {
|
||||
u16 opcode = segment.data[pos] | (segment.data[pos + 1] << 8);
|
||||
fprintf(file, "%08X %04X ", segment.target + pos / 2, opcode);
|
||||
bool expand = false;
|
||||
u16 expand_value = 0;
|
||||
if (Teakra::Disassembler::NeedExpansion(opcode)) {
|
||||
expand = true;
|
||||
pos += 2;
|
||||
expand_value = segment.data[pos] | (segment.data[pos + 1] << 8);
|
||||
}
|
||||
std::string result = Teakra::Disassembler::Do(opcode, expand_value);
|
||||
fprintf(file, "%s\n", result.c_str());
|
||||
if (expand) {
|
||||
fprintf(file, "%08X %04X ^^^\n", segment.target + pos / 2, expand_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (segment.memory_type == 2) {
|
||||
fprintf(file, "\n>>>>>>>> Data Segment <<<<<<<<\n\n");
|
||||
for (unsigned pos = 0; pos < segment.data.size(); pos += 2) {
|
||||
u16 opcode = segment.data[pos] | (segment.data[pos + 1] << 8);
|
||||
fprintf(file, "%08X %04X\n", segment.target + pos / 2, opcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
}
|
89
third_party/teakra/src/icu.h
vendored
Normal file
89
third_party/teakra/src/icu.h
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include "common_types.h"
|
||||
|
||||
namespace Teakra {
|
||||
|
||||
class ICU {
|
||||
public:
|
||||
using IrqBits = std::bitset<16>;
|
||||
u16 GetRequest() const {
|
||||
std::lock_guard lock(mutex);
|
||||
return (u16)request.to_ulong();
|
||||
}
|
||||
void Acknowledge(u16 irq_bits) {
|
||||
std::lock_guard lock(mutex);
|
||||
request &= ~IrqBits(irq_bits);
|
||||
}
|
||||
u16 GetAcknowledge() {
|
||||
return 0;
|
||||
}
|
||||
void Trigger(u16 irq_bits) {
|
||||
std::lock_guard lock(mutex);
|
||||
IrqBits bits(irq_bits);
|
||||
request |= bits;
|
||||
for (u32 irq = 0; irq < 16; ++irq) {
|
||||
if (bits[irq]) {
|
||||
for (u32 interrupt = 0; interrupt < enabled.size(); ++interrupt) {
|
||||
if (enabled[interrupt][irq]) {
|
||||
on_interrupt(interrupt);
|
||||
}
|
||||
}
|
||||
if (vectored_enabled[irq]) {
|
||||
on_vectored_interrupt(GetVector(irq), vector_context_switch[irq] != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
u16 GetTrigger() {
|
||||
return 0;
|
||||
}
|
||||
void TriggerSingle(u32 irq) {
|
||||
Trigger(1 << irq);
|
||||
}
|
||||
void SetEnable(u32 interrupt_index, u16 irq_bits) {
|
||||
std::lock_guard lock(mutex);
|
||||
enabled[interrupt_index] = IrqBits(irq_bits);
|
||||
}
|
||||
void SetEnableVectored(u16 irq_bits) {
|
||||
std::lock_guard lock(mutex);
|
||||
vectored_enabled = IrqBits(irq_bits);
|
||||
}
|
||||
u16 GetEnable(u32 interrupt_index) const {
|
||||
std::lock_guard lock(mutex);
|
||||
return (u16)enabled[interrupt_index].to_ulong();
|
||||
}
|
||||
u16 GetEnableVectored() const {
|
||||
std::lock_guard lock(mutex);
|
||||
return (u16)vectored_enabled.to_ulong();
|
||||
}
|
||||
|
||||
u32 GetVector(u32 irq) const {
|
||||
return vector_low[irq] | ((u32)vector_high[irq] << 16);
|
||||
}
|
||||
|
||||
void SetInterruptHandler(std::function<void(u32)> interrupt,
|
||||
std::function<void(u32, bool)> vectored_interrupt) {
|
||||
on_interrupt = std::move(interrupt);
|
||||
on_vectored_interrupt = std::move(vectored_interrupt);
|
||||
}
|
||||
|
||||
std::array<u16, 16> vector_low, vector_high;
|
||||
std::array<u16, 16> vector_context_switch;
|
||||
|
||||
private:
|
||||
std::function<void(u32)> on_interrupt;
|
||||
std::function<void(u32, bool)> on_vectored_interrupt;
|
||||
|
||||
IrqBits request;
|
||||
std::array<IrqBits, 3> enabled;
|
||||
IrqBits vectored_enabled;
|
||||
mutable std::mutex mutex;
|
||||
};
|
||||
|
||||
} // namespace Teakra
|
70
third_party/teakra/src/icu.md
vendored
Normal file
70
third_party/teakra/src/icu.md
vendored
Normal file
|
@ -0,0 +1,70 @@
|
|||
# ICU
|
||||
|
||||
## MMIO Layout
|
||||
|
||||
```
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0200 |IPF|IPE|IPD|IPC|IPB|IPA|IP9|IP8|IP7|IP6|IP5|IP4|IP3|IP2|IP1|IP0|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0202 |IAF|IAE|IAD|IAC|IAB|IAA|IA9|IA8|IA7|IA6|IA5|IA4|IA3|IA2|IA1|IA0|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0204 |ITF|ITE|ITD|ITC|ITB|ITA|IT9|IT8|IT7|IT6|IT5|IT4|IT3|IT2|IT1|IT0|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0206 |I0F|I0E|I0D|I0C|I0B|I0A|I09|I08|I07|I06|I05|I04|I03|I02|I01|I00|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0208 |I1F|I1E|I1D|I1C|I1B|I1A|I19|I18|I17|I16|I15|I14|I13|I12|I11|I10|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x020A |I2F|I2E|I2D|I2C|I2B|I2A|I29|I28|I27|I26|I25|I24|I23|I22|I21|I20|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x020C |IVF|IVE|IVD|IVC|IVB|IVA|IV9|IV8|IV7|IV6|IV5|IV4|IV3|IV2|IV1|IV0|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x020E |TPF|TPE|TPD|TPC|TPB|TPA|TP9|TP8|TP7|TP6|TP5|TP4|TP3|TP2|TP1|TP0|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0210 |PLF|PLE|PLD|PLC|PLB|PLA|PL9|PL8|PL7|PL6|PL5|PL4|PL3|PL2|PL1|PL0|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|
||||
N = 0..15
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0212+N*4|VIC| |VADDR_H|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0214+N*4| VADDR_L |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|
||||
IP0..IPF: IRQ pending flag
|
||||
IA0..IAF: IRQ acknowledge, 1 to clears pending flag
|
||||
IT0..ITF: triggers IRQ manually
|
||||
I00..I0F: connects IRQ to core interrupt 0
|
||||
I10..I1F: connects IRQ to core interrupt 1
|
||||
I20..I2F: connects IRQ to core interrupt 2
|
||||
IV0..IVF: connects IRQ to core vectored interrupt
|
||||
TP0..TPF: IRQ trigger mode? 0: pulse, 1: sticky
|
||||
PL0..PLF: polarity of IRQ signal?
|
||||
* I might have got PLx and TPx swapped
|
||||
VADDR_H, VADDR_L: address of interrupt handler for vectored interrupt
|
||||
VIC: 1 to enable context switch on vectored interrupt
|
||||
```
|
||||
|
||||
## IRQ signal
|
||||
|
||||
All bit fields in MMIO correspond to 16 IRQ signal. Some of them are known to connect to other peripherals as shown below. There might be more undiscovered components that have associated IRQ
|
||||
```
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
| | *F| *E| *D| *C| *B| *A| *9| *8| *7| *6| *5| *4| *3| *2| *1| *0|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
| | | | | | |
|
||||
DMA --------* | | | | | |
|
||||
APBP ------------* | | | | |
|
||||
SIO ----------------* | | | |
|
||||
BTDMP1--------------------* | | |
|
||||
BTDMP0------------------------* | |
|
||||
TIMER0----------------------------* |
|
||||
TIMER1--------------------------------*
|
||||
```
|
||||
|
||||
## IRQ-to-interrupt translator
|
||||
|
||||
The main job of ICU is to translate 16 IRQ signals to 4 processor interrupt signals (int0, int1, int2 and vint), specified by `I0x`, `I1x`, `I2x` and `IVx` registers. When an IRQ is signaled, ICU will generate interrupt signal and the processor starts to execute interrupt handling procedure if the core interrupt is enabled. The procedure is supposed check `IPx` reigster to know the exact IRQ index, and to set `IAx` registers to clear the IRQ signal.
|
||||
|
||||
## Software interrupt
|
||||
|
||||
The processor can writes to `ITx` to generate a software interrupt manually. A use case for this in many 3DS games is that IRQ 0 is used as the reschedule procedure for multithreading. When a thread wants to yield, it triggers IRQ 0, which switches thread context and resume another thread.
|
3633
third_party/teakra/src/interpreter.h
vendored
Normal file
3633
third_party/teakra/src/interpreter.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
11
third_party/teakra/src/makedsp1/CMakeLists.txt
vendored
Normal file
11
third_party/teakra/src/makedsp1/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
include(CreateDirectoryGroups)
|
||||
|
||||
add_executable(makedsp1
|
||||
main.cpp
|
||||
sha256.cpp
|
||||
sha256.h
|
||||
)
|
||||
create_target_directory_groups(makedsp1)
|
||||
target_link_libraries(makedsp1 PRIVATE teakra)
|
||||
target_include_directories(makedsp1 PRIVATE .)
|
||||
target_compile_options(makedsp1 PRIVATE ${TEAKRA_CXX_FLAGS})
|
172
third_party/teakra/src/makedsp1/main.cpp
vendored
Normal file
172
third_party/teakra/src/makedsp1/main.cpp
vendored
Normal file
|
@ -0,0 +1,172 @@
|
|||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include "../common_types.h"
|
||||
#include "../parser.h"
|
||||
#include "sha256.h"
|
||||
|
||||
template <typename T>
|
||||
std::vector<u8> Sha256(const std::vector<T>& data) {
|
||||
SHA256_CTX ctx;
|
||||
sha256_init(&ctx);
|
||||
sha256_update(&ctx, (const BYTE*)data.data(), data.size() * sizeof(T));
|
||||
std::vector<u8> result(0x20);
|
||||
sha256_final(&ctx, result.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
struct Segment {
|
||||
std::vector<u16> data;
|
||||
u8 memory_type;
|
||||
u32 target;
|
||||
};
|
||||
|
||||
std::vector<std::string> StringToTokens(const std::string& in) {
|
||||
std::vector<std::string> out;
|
||||
bool need_new = true;
|
||||
for (char c : in) {
|
||||
if (c == ' ' || c == '\t') {
|
||||
need_new = true;
|
||||
} else {
|
||||
if (need_new) {
|
||||
need_new = false;
|
||||
out.push_back("");
|
||||
}
|
||||
out.back() += c;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
auto parser = Teakra::GenerateParser();
|
||||
|
||||
if (argc < 3) {
|
||||
printf("Not enough parameters\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::ifstream in(argv[1]);
|
||||
if (!in.is_open()) {
|
||||
printf("Failed to open input file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
std::vector<Segment> segments;
|
||||
int line_number = 0;
|
||||
while (std::getline(in, line)) {
|
||||
++line_number;
|
||||
auto comment_pos = line.find("//");
|
||||
if (comment_pos != std::string::npos) {
|
||||
line.erase(comment_pos);
|
||||
}
|
||||
|
||||
auto expansion_pos = line.find("$");
|
||||
bool has_expansion = expansion_pos != std::string::npos;
|
||||
u16 expansion_data;
|
||||
if (has_expansion) {
|
||||
if (line.size() - expansion_pos < 5) {
|
||||
printf("%d: unexpected line break in expansion data\n", line_number);
|
||||
return -1;
|
||||
}
|
||||
expansion_data = (u16)std::stoi(line.substr(expansion_pos + 1, 4), 0, 16);
|
||||
line = line.substr(0, expansion_pos) + "0000" + line.substr(expansion_pos + 5);
|
||||
}
|
||||
|
||||
auto tokens = StringToTokens(line);
|
||||
if (tokens.size() == 0)
|
||||
continue;
|
||||
|
||||
if (tokens[0] == "segment") {
|
||||
if (tokens.size() != 3) {
|
||||
printf("%d: Wrong parameter count for 'segment'\n", line_number);
|
||||
return -1;
|
||||
}
|
||||
Segment s;
|
||||
if (tokens[1] == "p") {
|
||||
s.memory_type = 0;
|
||||
} else if (tokens[1] == "d") {
|
||||
s.memory_type = 2;
|
||||
} else {
|
||||
printf("%d: Unknown segment type %s\n", line_number, tokens[1].c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
s.target = std::stoi(tokens[2], 0, 16);
|
||||
segments.push_back(s);
|
||||
} else {
|
||||
u16 v;
|
||||
if (tokens[0] == "data") {
|
||||
if (tokens.size() != 2) {
|
||||
printf("%d: Wrong parameter count for 'data'\n", line_number);
|
||||
return -1;
|
||||
}
|
||||
v = (u16)std::stoi(tokens[1], 0, 16);
|
||||
segments.back().data.push_back(v);
|
||||
} else {
|
||||
auto maybe_v = parser->Parse(tokens);
|
||||
if (maybe_v.status == Teakra::Parser::Opcode::Invalid) {
|
||||
printf("%d: could not parse\n", line_number);
|
||||
return -1;
|
||||
}
|
||||
v = maybe_v.opcode;
|
||||
segments.back().data.push_back(v);
|
||||
|
||||
if (maybe_v.status == Teakra::Parser::Opcode::ValidWithExpansion) {
|
||||
if (!has_expansion) {
|
||||
printf("%d: needs expansion\n", line_number);
|
||||
return -1;
|
||||
}
|
||||
segments.back().data.push_back(expansion_data);
|
||||
} else {
|
||||
if (has_expansion) {
|
||||
printf("%d: unexpected expansion\n", line_number);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
in.close();
|
||||
|
||||
FILE* out = fopen(argv[2], "wb");
|
||||
if (!out) {
|
||||
printf("Failed to open output file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
u32 data_ptr = 0x300;
|
||||
|
||||
for (unsigned i = 0; i < segments.size(); ++i) {
|
||||
fseek(out, 0x120 + i * 0x30, SEEK_SET);
|
||||
fwrite(&data_ptr, 4, 1, out);
|
||||
fwrite(&segments[i].target, 4, 1, out);
|
||||
u32 size = (u32)segments[i].data.size() * 2;
|
||||
fwrite(&size, 4, 1, out);
|
||||
u32 memory_type = segments[i].memory_type << 24;
|
||||
fwrite(&memory_type, 4, 1, out);
|
||||
auto sha = Sha256(segments[i].data);
|
||||
fwrite(sha.data(), 0x20, 1, out);
|
||||
|
||||
fseek(out, data_ptr, SEEK_SET);
|
||||
fwrite(segments[i].data.data(), size, 1, out);
|
||||
data_ptr += size;
|
||||
}
|
||||
|
||||
fseek(out, 0x100, SEEK_SET);
|
||||
fwrite("DSP1", 4, 1, out);
|
||||
fwrite(&data_ptr, 4, 1, out);
|
||||
u32 memory_layout = 0x0000FFFF;
|
||||
fwrite(&memory_layout, 4, 1, out);
|
||||
u32 misc = (u32)segments.size() << 16;
|
||||
fwrite(&misc, 4, 1, out);
|
||||
u32 zero = 0;
|
||||
fwrite(&zero, 4, 1, out);
|
||||
fwrite(&zero, 4, 1, out);
|
||||
fwrite(&zero, 4, 1, out);
|
||||
fwrite(&zero, 4, 1, out);
|
||||
|
||||
fclose(out);
|
||||
}
|
152
third_party/teakra/src/makedsp1/sha256.cpp
vendored
Normal file
152
third_party/teakra/src/makedsp1/sha256.cpp
vendored
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*********************************************************************
|
||||
* Filename: sha256.c
|
||||
* Author: Brad Conte (brad AT bradconte.com)
|
||||
* Copyright:
|
||||
* Disclaimer: This code is presented "as is" without any guarantees.
|
||||
* Details: Implementation of the SHA-256 hashing algorithm.
|
||||
SHA-256 is one of the three algorithms in the SHA2
|
||||
specification. The others, SHA-384 and SHA-512, are not
|
||||
offered in this implementation.
|
||||
Algorithm specification can be found here:
|
||||
* http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
|
||||
This implementation uses little endian byte order.
|
||||
*********************************************************************/
|
||||
|
||||
/*************************** HEADER FILES ***************************/
|
||||
#include <memory.h>
|
||||
#include <stdlib.h>
|
||||
#include "sha256.h"
|
||||
|
||||
/****************************** MACROS ******************************/
|
||||
#define ROTLEFT(a, b) (((a) << (b)) | ((a) >> (32 - (b))))
|
||||
#define ROTRIGHT(a, b) (((a) >> (b)) | ((a) << (32 - (b))))
|
||||
|
||||
#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
|
||||
#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
|
||||
#define EP0(x) (ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22))
|
||||
#define EP1(x) (ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25))
|
||||
#define SIG0(x) (ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ ((x) >> 3))
|
||||
#define SIG1(x) (ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ ((x) >> 10))
|
||||
|
||||
/**************************** VARIABLES *****************************/
|
||||
static const WORD k[64] = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};
|
||||
|
||||
/*********************** FUNCTION DEFINITIONS ***********************/
|
||||
void sha256_transform(SHA256_CTX* ctx, const BYTE data[]) {
|
||||
WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
|
||||
|
||||
for (i = 0, j = 0; i < 16; ++i, j += 4)
|
||||
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
|
||||
for (; i < 64; ++i)
|
||||
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
|
||||
|
||||
a = ctx->state[0];
|
||||
b = ctx->state[1];
|
||||
c = ctx->state[2];
|
||||
d = ctx->state[3];
|
||||
e = ctx->state[4];
|
||||
f = ctx->state[5];
|
||||
g = ctx->state[6];
|
||||
h = ctx->state[7];
|
||||
|
||||
for (i = 0; i < 64; ++i) {
|
||||
t1 = h + EP1(e) + CH(e, f, g) + k[i] + m[i];
|
||||
t2 = EP0(a) + MAJ(a, b, c);
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + t1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = t1 + t2;
|
||||
}
|
||||
|
||||
ctx->state[0] += a;
|
||||
ctx->state[1] += b;
|
||||
ctx->state[2] += c;
|
||||
ctx->state[3] += d;
|
||||
ctx->state[4] += e;
|
||||
ctx->state[5] += f;
|
||||
ctx->state[6] += g;
|
||||
ctx->state[7] += h;
|
||||
}
|
||||
|
||||
void sha256_init(SHA256_CTX* ctx) {
|
||||
ctx->datalen = 0;
|
||||
ctx->bitlen = 0;
|
||||
ctx->state[0] = 0x6a09e667;
|
||||
ctx->state[1] = 0xbb67ae85;
|
||||
ctx->state[2] = 0x3c6ef372;
|
||||
ctx->state[3] = 0xa54ff53a;
|
||||
ctx->state[4] = 0x510e527f;
|
||||
ctx->state[5] = 0x9b05688c;
|
||||
ctx->state[6] = 0x1f83d9ab;
|
||||
ctx->state[7] = 0x5be0cd19;
|
||||
}
|
||||
|
||||
void sha256_update(SHA256_CTX* ctx, const BYTE data[], size_t len) {
|
||||
WORD i;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
ctx->data[ctx->datalen] = data[i];
|
||||
ctx->datalen++;
|
||||
if (ctx->datalen == 64) {
|
||||
sha256_transform(ctx, ctx->data);
|
||||
ctx->bitlen += 512;
|
||||
ctx->datalen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sha256_final(SHA256_CTX* ctx, BYTE hash[]) {
|
||||
WORD i;
|
||||
|
||||
i = ctx->datalen;
|
||||
|
||||
// Pad whatever data is left in the buffer.
|
||||
if (ctx->datalen < 56) {
|
||||
ctx->data[i++] = 0x80;
|
||||
while (i < 56)
|
||||
ctx->data[i++] = 0x00;
|
||||
} else {
|
||||
ctx->data[i++] = 0x80;
|
||||
while (i < 64)
|
||||
ctx->data[i++] = 0x00;
|
||||
sha256_transform(ctx, ctx->data);
|
||||
memset(ctx->data, 0, 56);
|
||||
}
|
||||
|
||||
// Append to the padding the total message's length in bits and transform.
|
||||
ctx->bitlen += ctx->datalen * 8;
|
||||
ctx->data[63] = (BYTE)ctx->bitlen;
|
||||
ctx->data[62] = (BYTE)(ctx->bitlen >> 8);
|
||||
ctx->data[61] = (BYTE)(ctx->bitlen >> 16);
|
||||
ctx->data[60] = (BYTE)(ctx->bitlen >> 24);
|
||||
ctx->data[59] = (BYTE)(ctx->bitlen >> 32);
|
||||
ctx->data[58] = (BYTE)(ctx->bitlen >> 40);
|
||||
ctx->data[57] = (BYTE)(ctx->bitlen >> 48);
|
||||
ctx->data[56] = (BYTE)(ctx->bitlen >> 56);
|
||||
sha256_transform(ctx, ctx->data);
|
||||
|
||||
// Since this implementation uses little endian byte ordering and SHA uses big endian,
|
||||
// reverse all the bytes when copying the final state to the output hash.
|
||||
for (i = 0; i < 4; ++i) {
|
||||
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
|
||||
hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
|
||||
hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
|
||||
hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
|
||||
hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
|
||||
hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
|
||||
hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
|
||||
hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
|
||||
}
|
||||
}
|
34
third_party/teakra/src/makedsp1/sha256.h
vendored
Normal file
34
third_party/teakra/src/makedsp1/sha256.h
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*********************************************************************
|
||||
* Filename: sha256.h
|
||||
* Author: Brad Conte (brad AT bradconte.com)
|
||||
* Copyright:
|
||||
* Disclaimer: This code is presented "as is" without any guarantees.
|
||||
* Details: Defines the API for the corresponding SHA1 implementation.
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef SHA256_H
|
||||
#define SHA256_H
|
||||
|
||||
/*************************** HEADER FILES ***************************/
|
||||
#include <stddef.h>
|
||||
|
||||
/****************************** MACROS ******************************/
|
||||
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
|
||||
|
||||
/**************************** DATA TYPES ****************************/
|
||||
typedef unsigned char BYTE; // 8-bit byte
|
||||
typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
|
||||
|
||||
typedef struct {
|
||||
BYTE data[64];
|
||||
WORD datalen;
|
||||
unsigned long long bitlen;
|
||||
WORD state[8];
|
||||
} SHA256_CTX;
|
||||
|
||||
/*********************** FUNCTION DECLARATIONS **********************/
|
||||
void sha256_init(SHA256_CTX* ctx);
|
||||
void sha256_update(SHA256_CTX* ctx, const BYTE data[], size_t len);
|
||||
void sha256_final(SHA256_CTX* ctx, BYTE hash[]);
|
||||
|
||||
#endif // SHA256_H
|
65
third_party/teakra/src/matcher.h
vendored
Normal file
65
third_party/teakra/src/matcher.h
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include "common_types.h"
|
||||
#include "crash.h"
|
||||
|
||||
struct Rejector {
|
||||
u16 mask;
|
||||
u16 unexpected;
|
||||
bool Rejects(u16 instruction) const {
|
||||
return (instruction & mask) == unexpected;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Visitor>
|
||||
class Matcher {
|
||||
public:
|
||||
using visitor_type = Visitor;
|
||||
using handler_return_type = typename Visitor::instruction_return_type;
|
||||
using handler_function = std::function<handler_return_type(Visitor&, u16, u16)>;
|
||||
|
||||
Matcher(const char* const name, u16 mask, u16 expected, bool expanded, handler_function func)
|
||||
: name{name}, mask{mask}, expected{expected}, expanded{expanded}, fn{std::move(func)} {}
|
||||
|
||||
static Matcher AllMatcher(handler_function func) {
|
||||
return Matcher("*", 0, 0, false, std::move(func));
|
||||
}
|
||||
|
||||
const char* GetName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
bool NeedExpansion() const {
|
||||
return expanded;
|
||||
}
|
||||
|
||||
bool Matches(u16 instruction) const {
|
||||
return (instruction & mask) == expected &&
|
||||
std::none_of(rejectors.begin(), rejectors.end(),
|
||||
[instruction](const Rejector& rejector) {
|
||||
return rejector.Rejects(instruction);
|
||||
});
|
||||
}
|
||||
|
||||
Matcher Except(Rejector rejector) const {
|
||||
Matcher new_matcher(*this);
|
||||
new_matcher.rejectors.push_back(rejector);
|
||||
return new_matcher;
|
||||
}
|
||||
|
||||
handler_return_type call(Visitor& v, u16 instruction, u16 instruction_expansion = 0) const {
|
||||
ASSERT(Matches(instruction));
|
||||
return fn(v, instruction, instruction_expansion);
|
||||
}
|
||||
|
||||
private:
|
||||
const char* name;
|
||||
u16 mask;
|
||||
u16 expected;
|
||||
bool expanded;
|
||||
handler_function fn;
|
||||
std::vector<Rejector> rejectors;
|
||||
};
|
57
third_party/teakra/src/memory_interface.cpp
vendored
Normal file
57
third_party/teakra/src/memory_interface.cpp
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include "memory_interface.h"
|
||||
#include "mmio.h"
|
||||
#include "shared_memory.h"
|
||||
|
||||
namespace Teakra {
|
||||
MemoryInterface::MemoryInterface(SharedMemory& shared_memory,
|
||||
MemoryInterfaceUnit& memory_interface_unit)
|
||||
: shared_memory(shared_memory), memory_interface_unit(memory_interface_unit) {}
|
||||
|
||||
void MemoryInterface::SetMMIO(MMIORegion& mmio) {
|
||||
this->mmio = &mmio;
|
||||
}
|
||||
|
||||
u16 MemoryInterface::ProgramRead(u32 address) const {
|
||||
return shared_memory.ReadWord(address);
|
||||
}
|
||||
void MemoryInterface::ProgramWrite(u32 address, u16 value) {
|
||||
shared_memory.WriteWord(address, value);
|
||||
}
|
||||
u16 MemoryInterface::DataRead(u16 address, bool bypass_mmio) {
|
||||
if (memory_interface_unit.InMMIO(address) && !bypass_mmio) {
|
||||
ASSERT(mmio != nullptr);
|
||||
return mmio->Read(memory_interface_unit.ToMMIO(address));
|
||||
}
|
||||
u32 converted = memory_interface_unit.ConvertDataAddress(address);
|
||||
u16 value = shared_memory.ReadWord(converted);
|
||||
return value;
|
||||
}
|
||||
void MemoryInterface::DataWrite(u16 address, u16 value, bool bypass_mmio) {
|
||||
if (memory_interface_unit.InMMIO(address) && !bypass_mmio) {
|
||||
ASSERT(mmio != nullptr);
|
||||
return mmio->Write(memory_interface_unit.ToMMIO(address), value);
|
||||
}
|
||||
u32 converted = memory_interface_unit.ConvertDataAddress(address);
|
||||
shared_memory.WriteWord(converted, value);
|
||||
}
|
||||
u16 MemoryInterface::DataReadA32(u32 address) const {
|
||||
u32 converted = (address & ((MemoryInterfaceUnit::DataMemoryBankSize*2)-1))
|
||||
+ MemoryInterfaceUnit::DataMemoryOffset;
|
||||
return shared_memory.ReadWord(converted);
|
||||
}
|
||||
void MemoryInterface::DataWriteA32(u32 address, u16 value) {
|
||||
u32 converted = (address & ((MemoryInterfaceUnit::DataMemoryBankSize*2)-1))
|
||||
+ MemoryInterfaceUnit::DataMemoryOffset;
|
||||
shared_memory.WriteWord(converted, value);
|
||||
}
|
||||
u16 MemoryInterface::MMIORead(u16 address) {
|
||||
ASSERT(mmio != nullptr);
|
||||
// according to GBATek ("DSi Teak I/O Ports (on ARM9 Side)"), these are mirrored
|
||||
return mmio->Read(address & (MemoryInterfaceUnit::MMIOSize - 1));
|
||||
}
|
||||
void MemoryInterface::MMIOWrite(u16 address, u16 value) {
|
||||
ASSERT(mmio != nullptr);
|
||||
mmio->Write(address & (MemoryInterfaceUnit::MMIOSize - 1), value);
|
||||
}
|
||||
|
||||
} // namespace Teakra
|
73
third_party/teakra/src/memory_interface.h
vendored
Normal file
73
third_party/teakra/src/memory_interface.h
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common_types.h"
|
||||
#include "crash.h"
|
||||
|
||||
namespace Teakra {
|
||||
|
||||
class MemoryInterfaceUnit {
|
||||
public:
|
||||
u16 x_page = 0, y_page = 0, z_page = 0;
|
||||
static constexpr u16 XYSizeResolution = 0x400;
|
||||
std::array<u16, 2> x_size{{0x20, 0x20}};
|
||||
std::array<u16, 2> y_size{{0x1E, 0x1E}};
|
||||
u16 page_mode = 0;
|
||||
u16 mmio_base = 0x8000;
|
||||
|
||||
static constexpr u16 MMIOSize = 0x0800;
|
||||
static constexpr u32 DataMemoryOffset = 0x20000;
|
||||
static constexpr u32 DataMemoryBankSize = 0x10000;
|
||||
|
||||
void Reset() {
|
||||
*this = MemoryInterfaceUnit();
|
||||
}
|
||||
|
||||
bool InMMIO(u16 addr) const {
|
||||
return addr >= mmio_base && addr < mmio_base + MMIOSize;
|
||||
}
|
||||
u16 ToMMIO(u16 addr) const {
|
||||
ASSERT(z_page == 0);
|
||||
// according to GBATek ("DSi Teak I/O Ports (on ARM9 Side)"), these are mirrored
|
||||
return (addr - mmio_base) & (MMIOSize - 1);
|
||||
}
|
||||
|
||||
u32 ConvertDataAddress(u16 addr) const {
|
||||
if (page_mode == 0) {
|
||||
ASSERT(z_page < 2);
|
||||
return DataMemoryOffset + addr + z_page * DataMemoryBankSize;
|
||||
} else {
|
||||
if (addr <= x_size[0] * XYSizeResolution) {
|
||||
ASSERT(x_page < 2);
|
||||
return DataMemoryOffset + addr + x_page * DataMemoryBankSize;
|
||||
} else {
|
||||
ASSERT(y_page < 2);
|
||||
return DataMemoryOffset + addr + y_page * DataMemoryBankSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct SharedMemory;
|
||||
class MMIORegion;
|
||||
|
||||
class MemoryInterface {
|
||||
public:
|
||||
MemoryInterface(SharedMemory& shared_memory, MemoryInterfaceUnit& memory_interface_unit);
|
||||
void SetMMIO(MMIORegion& mmio);
|
||||
u16 ProgramRead(u32 address) const;
|
||||
void ProgramWrite(u32 address, u16 value);
|
||||
u16 DataRead(u16 address, bool bypass_mmio = false); // not const because it can be a FIFO register
|
||||
void DataWrite(u16 address, u16 value, bool bypass_mmio = false);
|
||||
u16 DataReadA32(u32 address) const;
|
||||
void DataWriteA32(u32 address, u16 value);
|
||||
u16 MMIORead(u16 address);
|
||||
void MMIOWrite(u16 address, u16 value);
|
||||
|
||||
private:
|
||||
SharedMemory& shared_memory;
|
||||
MemoryInterfaceUnit& memory_interface_unit;
|
||||
MMIORegion* mmio;
|
||||
};
|
||||
|
||||
} // namespace Teakra
|
97
third_party/teakra/src/miu.md
vendored
Normal file
97
third_party/teakra/src/miu.md
vendored
Normal file
|
@ -0,0 +1,97 @@
|
|||
# MIU
|
||||
|
||||
## MMIO Layout
|
||||
|
||||
The following MMIO definition is extracted from Lauterbach's Teak debugger. Some of them are untested.
|
||||
```
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0100 | Z3WS | Z2WS | Z1WS | Z0WS |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0102 | | PRMWS | XYWS | ZDEFAULTWS |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0104 | Z0WSPAGE | Z0WSEA | Z0WSSA |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0106 | Z1WSPAGE | Z1WSEA | Z1WSSA |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0108 | Z2WSPAGE | Z2WSEA | Z2WSSA |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x010A | Z3WSPAGE | Z3WSEA | Z3WSSA |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x010C | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x010E | XPAGE |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0110 | | YPAGE |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0112 | ZPAGE |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0114 | | YPAGE0CFG | | XPAGE0CFG |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0116 | | YPAGE1CFG | | XPAGE1CFG |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0118 | | YOFFPAGECFG | | XOFFPAGECFG |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x011A | |PGM| |ZSP| |INP|TSP|PP |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x011C | |PDP| PDLPAGE |SDL|DLP|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x011E | MMIOBASE | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0120 | | OBSMOD |OBS|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0122 | |PRD|PZS|PXS|PXT|PZT|PZW|PZR|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|
||||
ZxWS: the number of wait-states for the x-th off-chip Z block
|
||||
ZDEFAULTWS: the default number of wait-states for Z off-chip transactions
|
||||
XYWS: the number of wait-states for X/Y off-chip memory transactions
|
||||
PRMWS: the number of wait-states for off-chip program-memory transactions
|
||||
ZxWSSA: the start address of the Z wait-states block #0 with a resolution of 1K-word.
|
||||
ZxWSEA: the end address of the Z wait-states block #0 with a resolution of 1K-word
|
||||
ZxWSPACE: the four LSBs of the Z wait-states block #0 page
|
||||
|
||||
XPAGE: the X memory page when working in paging mode 1
|
||||
YPAGE: the Y memory page when working in paging mode 1
|
||||
ZPAGE:
|
||||
- in paging mode 0: the absolute data memory page
|
||||
- in paging mode 1: the Z memory page
|
||||
XPAGE0CFG:
|
||||
- in paging mode 0: the X memory size for page #0
|
||||
- in paging mode 1: the X memory size for all pages
|
||||
YPAGE0CFG:
|
||||
- in paging mode 0: the Y memory size for page #0
|
||||
- in paging mode 1: the Y memory size for all pages
|
||||
XPAGE1CFG: in paging mode 0, the X memory size for page #1
|
||||
YPAGE1CFG: in paging mode 0, the Y memory size for page #1
|
||||
XOFFPAGECFG: in paging mode 0: the X memory size for all off-chip pages
|
||||
YOFFPAGECFG: in paging mode 0: the Y memory size for all off-chip pages
|
||||
|
||||
PP: 1 to enable the program protection mechanism
|
||||
TSP: "Test Program", 1 to latch the value of the DAZXE[1]/TESTP strap pin during reset. It configures the entire program space as off-chip or based on the INTP bit
|
||||
INP: "Internal Program", when a breakpoint interrupt occurs, the MIU forces a jump to page #1 (off-chip page) (when set to 0) or to page #0 (on-chip page) (when set to 1) and reads the Monitor program from this page
|
||||
ZSP: 1 to enable sngle access mode, where the Z data memory space's Core/DMA addresses use only the Z-even address bus
|
||||
PGM: paging mode
|
||||
|
||||
DLP: "Download Memory Select", this bit is used to select between two, parallel Z-space data memories, including Regular and Download memories. 0 - ZRDEN, 1- ZBRDN
|
||||
SDL: "Sticky Download Select", this bit is set one cycle after DLP is set. It is not cleared when clearing the DLP bit. It remains set until specifically writing low to it during a Trap routine
|
||||
PDLPAGE: "Alternative Program Page", this field is an alternative paging register for program write (movd) and program read (movp) transactions
|
||||
PDP: 1 to enable alternative program page
|
||||
|
||||
MMIOBASE: MMIO base address, with resolution of 512-word.
|
||||
|
||||
OBS: 1 to enable observability mode
|
||||
OBSMOD: observability mode, defines which Core/DMA address/data buses are reflected on the off-chip XZ buses
|
||||
- 0: Core XZ address/data buses
|
||||
- 1: Core Y address/data buses
|
||||
- 2: Core P address/data buses
|
||||
- 3: DMA DST address/data buses
|
||||
- 4: DMA SRC address/data buses
|
||||
|
||||
PRD: Signal Polarity Bit - This bit defines the polarity of RD_WR
|
||||
PZS: Z Select Polarity Bit - This bit defines the polarity of ZS
|
||||
PXS: X Select Polarity Bit - This bit defines the polarity of XS
|
||||
PXT: X Strobe Polarity Bit - This bit defines the polarity of XSTRB
|
||||
PZT: Z Strobe Polarity Bit - This bit defines the polarity of ZSTRB
|
||||
PZW: Z Write Polarity Bit - This bit defines the polarity of DWZON/DWZEN
|
||||
PZR: Z Read Polarity Bit - This bit defines the polarity of DRZON/DRZEN
|
||||
```
|
361
third_party/teakra/src/mmio.cpp
vendored
Normal file
361
third_party/teakra/src/mmio.cpp
vendored
Normal file
|
@ -0,0 +1,361 @@
|
|||
#include <functional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include "ahbm.h"
|
||||
#include "apbp.h"
|
||||
#include "btdmp.h"
|
||||
#include "dma.h"
|
||||
#include "memory_interface.h"
|
||||
#include "mmio.h"
|
||||
#include "timer.h"
|
||||
|
||||
namespace Teakra {
|
||||
|
||||
auto NoSet(const std::string& debug_string) {
|
||||
return [debug_string](u16) { printf("Warning: NoSet on %s\n", debug_string.data()); };
|
||||
}
|
||||
auto NoGet(const std::string& debug_string) {
|
||||
return [debug_string]() -> u16 {
|
||||
printf("Warning: NoGet on %s\n", debug_string.data());
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
struct BitFieldSlot {
|
||||
unsigned pos;
|
||||
unsigned length;
|
||||
std::function<void(u16)> set;
|
||||
std::function<u16(void)> get;
|
||||
|
||||
template <typename T>
|
||||
static BitFieldSlot RefSlot(unsigned pos, unsigned length, T& var) {
|
||||
static_assert(
|
||||
std::is_same_v<u16,
|
||||
typename std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>,
|
||||
std::enable_if<true, T>>::type>);
|
||||
BitFieldSlot slot{pos, length, {}, {}};
|
||||
slot.set = [&var](u16 value) { var = static_cast<T>(value); };
|
||||
slot.get = [&var]() -> u16 { return static_cast<u16>(var); };
|
||||
return slot;
|
||||
}
|
||||
};
|
||||
|
||||
struct Cell {
|
||||
std::function<void(u16)> set;
|
||||
std::function<u16(void)> get;
|
||||
u16 index = 0;
|
||||
|
||||
Cell(std::function<void(u16)> set, std::function<u16(void)> get)
|
||||
: set(std::move(set)), get(std::move(get)) {}
|
||||
Cell() {
|
||||
std::shared_ptr<u16> storage = std::make_shared<u16>(0);
|
||||
set = [storage, this](u16 value) {
|
||||
*storage = value;
|
||||
std::printf("MMIO: cell %04X set = %04X\n", index, value);
|
||||
};
|
||||
get = [storage, this]() -> u16 {
|
||||
std::printf("MMIO: cell %04X get\n", index);
|
||||
return *storage;
|
||||
};
|
||||
}
|
||||
static Cell ConstCell(u16 constant) {
|
||||
Cell cell({}, {});
|
||||
cell.set = NoSet("");
|
||||
cell.get = [constant]() -> u16 { return constant; };
|
||||
return cell;
|
||||
}
|
||||
static Cell RefCell(u16& var) {
|
||||
Cell cell({}, {});
|
||||
cell.set = [&var](u16 value) { var = value; };
|
||||
cell.get = [&var]() -> u16 { return var; };
|
||||
return cell;
|
||||
}
|
||||
|
||||
static Cell MirrorCell(Cell* mirror) {
|
||||
Cell cell({}, {});
|
||||
cell.set = [mirror](u16 value) { mirror->set(value); };
|
||||
cell.get = [mirror]() -> u16 { return mirror->get(); };
|
||||
return cell;
|
||||
}
|
||||
|
||||
static Cell BitFieldCell(const std::vector<BitFieldSlot>& slots) {
|
||||
Cell cell({}, {});
|
||||
std::shared_ptr<u16> storage = std::make_shared<u16>(0);
|
||||
cell.set = [storage, slots](u16 value) {
|
||||
for (const auto& slot : slots) {
|
||||
if (slot.set) {
|
||||
slot.set((value >> slot.pos) & ((1 << slot.length) - 1));
|
||||
}
|
||||
}
|
||||
*storage = value;
|
||||
};
|
||||
cell.get = [storage, slots]() -> u16 {
|
||||
u16 value = *storage;
|
||||
for (const auto& slot : slots) {
|
||||
if (slot.get) {
|
||||
value &= ~(((1 << slot.length) - 1) << slot.pos);
|
||||
value |= slot.get() << slot.pos;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
return cell;
|
||||
}
|
||||
};
|
||||
|
||||
class MMIORegion::Impl {
|
||||
public:
|
||||
std::array<Cell, 0x800> cells{};
|
||||
Impl() {
|
||||
for (std::size_t i = 0; i < cells.size(); ++i) {
|
||||
cells[i].index = (u16)i;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MMIORegion::MMIORegion(MemoryInterfaceUnit& miu, ICU& icu, Apbp& apbp_from_cpu, Apbp& apbp_from_dsp,
|
||||
std::array<Timer, 2>& timer, Dma& dma, Ahbm& ahbm,
|
||||
std::array<Btdmp, 2>& btdmp)
|
||||
: impl(new Impl) {
|
||||
using namespace std::placeholders;
|
||||
|
||||
impl->cells[0x01A] = Cell::ConstCell(0xC902); // chip detect
|
||||
|
||||
// Timer
|
||||
for (unsigned i = 0; i < 2; ++i) {
|
||||
impl->cells[0x20 + i * 0x10] = Cell::BitFieldCell({
|
||||
// TIMERx_CFG
|
||||
BitFieldSlot::RefSlot(0, 2, timer[i].scale), // TS
|
||||
BitFieldSlot::RefSlot(2, 3, timer[i].count_mode), // CM
|
||||
BitFieldSlot{6, 1, {}, {}}, // TP
|
||||
BitFieldSlot{7, 1, {}, {}}, // CT
|
||||
BitFieldSlot::RefSlot(8, 1, timer[i].pause), // PC
|
||||
BitFieldSlot::RefSlot(9, 1, timer[i].update_mmio), // MU
|
||||
BitFieldSlot{10, 1,
|
||||
[&timer, i](u16 v) {
|
||||
if (v)
|
||||
timer[i].Restart();
|
||||
},
|
||||
[]() -> u16 { return 0; }}, // RES
|
||||
BitFieldSlot{11, 1, {}, {}}, // BP
|
||||
BitFieldSlot{12, 1, {}, {}}, // CS
|
||||
BitFieldSlot{13, 1, {}, {}}, // GP
|
||||
BitFieldSlot{14, 2, {}, {}}, // TM
|
||||
});
|
||||
|
||||
impl->cells[0x22 + i * 0x10].set = [&timer, i](u16 v) {
|
||||
if (v)
|
||||
timer[i].TickEvent();
|
||||
}; // TIMERx_EW
|
||||
impl->cells[0x22 + i * 0x10].get = []() -> u16 { return 0; };
|
||||
impl->cells[0x24 + i * 0x10] = Cell::RefCell(timer[i].start_low); // TIMERx_SCL
|
||||
impl->cells[0x26 + i * 0x10] = Cell::RefCell(timer[i].start_high); // TIMERx_SCH
|
||||
impl->cells[0x28 + i * 0x10] = Cell::RefCell(timer[i].counter_low); // TIMERx_CCL
|
||||
impl->cells[0x2A + i * 0x10] = Cell::RefCell(timer[i].counter_high); // TIMERx_CCH
|
||||
impl->cells[0x2C + i * 0x10] = Cell(); // TIMERx_SPWMCL
|
||||
impl->cells[0x2E + i * 0x10] = Cell(); // TIMERx_SPWMCH
|
||||
}
|
||||
|
||||
// APBP
|
||||
for (unsigned i = 0; i < 3; ++i) {
|
||||
impl->cells[0x0C0 + i * 4].set = std::bind(&Apbp::SendData, &apbp_from_dsp, i, _1);
|
||||
impl->cells[0x0C0 + i * 4].get = std::bind(&Apbp::PeekData, &apbp_from_dsp, i);
|
||||
impl->cells[0x0C2 + i * 4].set = [](u16) {};
|
||||
impl->cells[0x0C2 + i * 4].get = std::bind(&Apbp::RecvData, &apbp_from_cpu, i);
|
||||
}
|
||||
impl->cells[0x0CC].set = std::bind(&Apbp::SetSemaphore, &apbp_from_dsp, _1);
|
||||
impl->cells[0x0CC].get = std::bind(&Apbp::GetSemaphore, &apbp_from_dsp);
|
||||
impl->cells[0x0CE].set = std::bind(&Apbp::MaskSemaphore, &apbp_from_cpu, _1);
|
||||
impl->cells[0x0CE].get = std::bind(&Apbp::GetSemaphoreMask, &apbp_from_cpu);
|
||||
impl->cells[0x0D0].set = std::bind(&Apbp::ClearSemaphore, &apbp_from_cpu, _1);
|
||||
impl->cells[0x0D0].get = []() -> u16 { return 0; };
|
||||
impl->cells[0x0D2].set = [](u16) {};
|
||||
impl->cells[0x0D2].get = std::bind(&Apbp::GetSemaphore, &apbp_from_cpu);
|
||||
impl->cells[0x0D4] = Cell::BitFieldCell({
|
||||
BitFieldSlot{2, 1, {}, {}}, // ARM side endianness flag
|
||||
BitFieldSlot{8, 1,
|
||||
[&apbp_from_cpu](u16 v) { return apbp_from_cpu.SetDisableInterrupt(0, v); },
|
||||
[&apbp_from_cpu]() -> u16 { return apbp_from_cpu.GetDisableInterrupt(0); }},
|
||||
BitFieldSlot{12, 1,
|
||||
[&apbp_from_cpu](u16 v) { return apbp_from_cpu.SetDisableInterrupt(1, v); },
|
||||
[&apbp_from_cpu]() -> u16 { return apbp_from_cpu.GetDisableInterrupt(1); }},
|
||||
BitFieldSlot{13, 1,
|
||||
[&apbp_from_cpu](u16 v) { return apbp_from_cpu.SetDisableInterrupt(2, v); },
|
||||
[&apbp_from_cpu]() -> u16 { return apbp_from_cpu.GetDisableInterrupt(2); }},
|
||||
});
|
||||
impl->cells[0x0D6] = Cell::BitFieldCell({
|
||||
BitFieldSlot{5, 1, {}, [&apbp_from_dsp]() -> u16 { return apbp_from_dsp.IsDataReady(0); }},
|
||||
BitFieldSlot{6, 1, {}, [&apbp_from_dsp]() -> u16 { return apbp_from_dsp.IsDataReady(1); }},
|
||||
BitFieldSlot{7, 1, {}, [&apbp_from_dsp]() -> u16 { return apbp_from_dsp.IsDataReady(2); }},
|
||||
BitFieldSlot{8, 1, {}, [&apbp_from_cpu]() -> u16 { return apbp_from_cpu.IsDataReady(0); }},
|
||||
BitFieldSlot{
|
||||
9, 1, {}, [&apbp_from_cpu]() -> u16 { return apbp_from_cpu.IsSemaphoreSignaled(); }},
|
||||
BitFieldSlot{12, 1, {}, [&apbp_from_cpu]() -> u16 { return apbp_from_cpu.IsDataReady(1); }},
|
||||
BitFieldSlot{13, 1, {}, [&apbp_from_cpu]() -> u16 { return apbp_from_cpu.IsDataReady(2); }},
|
||||
});
|
||||
|
||||
// This register is a mirror of CPU side register DSP_PSTS
|
||||
impl->cells[0x0D8] = Cell::BitFieldCell({
|
||||
BitFieldSlot{
|
||||
9, 1, {}, [&apbp_from_cpu]() -> u16 { return apbp_from_cpu.IsSemaphoreSignaled(); }},
|
||||
BitFieldSlot{10, 1, {}, [&apbp_from_dsp]() -> u16 { return apbp_from_dsp.IsDataReady(0); }},
|
||||
BitFieldSlot{11, 1, {}, [&apbp_from_dsp]() -> u16 { return apbp_from_dsp.IsDataReady(1); }},
|
||||
BitFieldSlot{12, 1, {}, [&apbp_from_dsp]() -> u16 { return apbp_from_dsp.IsDataReady(2); }},
|
||||
BitFieldSlot{13, 1, {}, [&apbp_from_cpu]() -> u16 { return apbp_from_cpu.IsDataReady(0); }},
|
||||
BitFieldSlot{14, 1, {}, [&apbp_from_cpu]() -> u16 { return apbp_from_cpu.IsDataReady(1); }},
|
||||
BitFieldSlot{15, 1, {}, [&apbp_from_cpu]() -> u16 { return apbp_from_cpu.IsDataReady(2); }},
|
||||
});
|
||||
|
||||
// AHBM
|
||||
impl->cells[0x0E0].set = NoSet("AHBM::BusyFlag");
|
||||
impl->cells[0x0E0].get = std::bind(&Ahbm::GetBusyFlag, &ahbm);
|
||||
for (u16 i = 0; i < 3; ++i) {
|
||||
impl->cells[0x0E2 + i * 6] = Cell::BitFieldCell({
|
||||
// BitFieldSlot{0, 1, ?, ?},
|
||||
BitFieldSlot{1, 2, std::bind(&Ahbm::SetBurstSize, &ahbm, i, _1),
|
||||
std::bind(&Ahbm::GetBurstSize, &ahbm, i)},
|
||||
BitFieldSlot{4, 2, std::bind(&Ahbm::SetUnitSize, &ahbm, i, _1),
|
||||
std::bind(&Ahbm::GetUnitSize, &ahbm, i)},
|
||||
});
|
||||
impl->cells[0x0E4 + i * 6] = Cell::BitFieldCell({
|
||||
BitFieldSlot{8, 1, std::bind(&Ahbm::SetDirection, &ahbm, i, _1),
|
||||
std::bind(&Ahbm::GetDirection, &ahbm, i)},
|
||||
// BitFieldSlot{9, 1, ?, ?},
|
||||
});
|
||||
impl->cells[0x0E6 + i * 6].set = std::bind(&Ahbm::SetDmaChannel, &ahbm, i, _1);
|
||||
impl->cells[0x0E6 + i * 6].get = std::bind(&Ahbm::GetDmaChannel, &ahbm, i);
|
||||
}
|
||||
|
||||
// MIU
|
||||
// impl->cells[0x100]; // MIU_WSCFG0
|
||||
// impl->cells[0x102]; // MIU_WSCFG1
|
||||
// impl->cells[0x104]; // MIU_Z0WSCFG
|
||||
// impl->cells[0x106]; // MIU_Z1WSCFG
|
||||
// impl->cells[0x108]; // MIU_Z2WSCFG
|
||||
// impl->cells[0x10C]; // MIU_Z3WSCFG
|
||||
impl->cells[0x10E] = Cell::RefCell(miu.x_page); // MIU_XPAGE
|
||||
impl->cells[0x110] = Cell::RefCell(miu.y_page); // MIU_YPAGE
|
||||
impl->cells[0x112] = Cell::RefCell(miu.z_page); // MIU_ZPAGE
|
||||
impl->cells[0x114] = Cell::BitFieldCell({
|
||||
// MIU_PAGE0CFG
|
||||
BitFieldSlot::RefSlot(0, 6, miu.x_size[0]),
|
||||
BitFieldSlot::RefSlot(8, 6, miu.y_size[0]),
|
||||
});
|
||||
impl->cells[0x116] = Cell::BitFieldCell({
|
||||
// MIU_PAGE1CFG
|
||||
BitFieldSlot::RefSlot(0, 6, miu.x_size[1]),
|
||||
BitFieldSlot::RefSlot(8, 6, miu.y_size[1]),
|
||||
});
|
||||
// impl->cells[0x118]; // MIU_OFFPAGECFG
|
||||
impl->cells[0x11A] = Cell::BitFieldCell({
|
||||
BitFieldSlot{0, 1, {}, {}}, // PP
|
||||
BitFieldSlot{1, 1, {}, {}}, // TESTP
|
||||
BitFieldSlot{2, 1, {}, {}}, // INTP
|
||||
BitFieldSlot{4, 1, {}, {}}, // ZSINGLEP
|
||||
BitFieldSlot::RefSlot(6, 1, miu.page_mode), // PAGEMODE
|
||||
});
|
||||
// impl->cells[0x11C]; // MIU_DLCFG
|
||||
impl->cells[0x11E] = Cell::RefCell(miu.mmio_base); // MIU_MMIOBASE
|
||||
// impl->cells[0x120]; // MIU_OBSCFG
|
||||
// impl->cells[0x122]; // MIU_POLARITY
|
||||
|
||||
// DMA
|
||||
impl->cells[0x184].set = std::bind(&Dma::EnableChannel, &dma, _1);
|
||||
impl->cells[0x184].get = std::bind(&Dma::GetChannelEnabled, &dma);
|
||||
|
||||
impl->cells[0x18C].get = []() -> u16 { return 0xFFFF; }; // SEOX ?
|
||||
|
||||
impl->cells[0x1BE].set = std::bind(&Dma::ActivateChannel, &dma, _1);
|
||||
impl->cells[0x1BE].get = std::bind(&Dma::GetActiveChannel, &dma);
|
||||
impl->cells[0x1C0].set = std::bind(&Dma::SetAddrSrcLow, &dma, _1);
|
||||
impl->cells[0x1C0].get = std::bind(&Dma::GetAddrSrcLow, &dma);
|
||||
impl->cells[0x1C2].set = std::bind(&Dma::SetAddrSrcHigh, &dma, _1);
|
||||
impl->cells[0x1C2].get = std::bind(&Dma::GetAddrSrcHigh, &dma);
|
||||
impl->cells[0x1C4].set = std::bind(&Dma::SetAddrDstLow, &dma, _1);
|
||||
impl->cells[0x1C4].get = std::bind(&Dma::GetAddrDstLow, &dma);
|
||||
impl->cells[0x1C6].set = std::bind(&Dma::SetAddrDstHigh, &dma, _1);
|
||||
impl->cells[0x1C6].get = std::bind(&Dma::GetAddrDstHigh, &dma);
|
||||
impl->cells[0x1C8].set = std::bind(&Dma::SetSize0, &dma, _1);
|
||||
impl->cells[0x1C8].get = std::bind(&Dma::GetSize0, &dma);
|
||||
impl->cells[0x1CA].set = std::bind(&Dma::SetSize1, &dma, _1);
|
||||
impl->cells[0x1CA].get = std::bind(&Dma::GetSize1, &dma);
|
||||
impl->cells[0x1CC].set = std::bind(&Dma::SetSize2, &dma, _1);
|
||||
impl->cells[0x1CC].get = std::bind(&Dma::GetSize2, &dma);
|
||||
impl->cells[0x1CE].set = std::bind(&Dma::SetSrcStep0, &dma, _1);
|
||||
impl->cells[0x1CE].get = std::bind(&Dma::GetSrcStep0, &dma);
|
||||
impl->cells[0x1D0].set = std::bind(&Dma::SetDstStep0, &dma, _1);
|
||||
impl->cells[0x1D0].get = std::bind(&Dma::GetDstStep0, &dma);
|
||||
impl->cells[0x1D2].set = std::bind(&Dma::SetSrcStep1, &dma, _1);
|
||||
impl->cells[0x1D2].get = std::bind(&Dma::GetSrcStep1, &dma);
|
||||
impl->cells[0x1D4].set = std::bind(&Dma::SetDstStep1, &dma, _1);
|
||||
impl->cells[0x1D4].get = std::bind(&Dma::GetDstStep1, &dma);
|
||||
impl->cells[0x1D6].set = std::bind(&Dma::SetSrcStep2, &dma, _1);
|
||||
impl->cells[0x1D6].get = std::bind(&Dma::GetSrcStep2, &dma);
|
||||
impl->cells[0x1D8].set = std::bind(&Dma::SetDstStep2, &dma, _1);
|
||||
impl->cells[0x1D8].get = std::bind(&Dma::GetDstStep2, &dma);
|
||||
impl->cells[0x1DA] = Cell::BitFieldCell({
|
||||
BitFieldSlot{0, 4, std::bind(&Dma::SetSrcSpace, &dma, _1),
|
||||
std::bind(&Dma::GetSrcSpace, &dma)},
|
||||
BitFieldSlot{4, 4, std::bind(&Dma::SetDstSpace, &dma, _1),
|
||||
std::bind(&Dma::GetDstSpace, &dma)},
|
||||
// BitFieldSlot{9, 1, ?, ?},
|
||||
BitFieldSlot{10, 1, std::bind(&Dma::SetDwordMode, &dma, _1),
|
||||
std::bind(&Dma::GetDwordMode, &dma)},
|
||||
});
|
||||
impl->cells[0x1DC].set = std::bind(&Dma::SetY, &dma, _1);
|
||||
impl->cells[0x1DC].get = std::bind(&Dma::GetY, &dma);
|
||||
impl->cells[0x1DE].set = std::bind(&Dma::SetZ, &dma, _1);
|
||||
impl->cells[0x1DE].get = std::bind(&Dma::GetZ, &dma);
|
||||
|
||||
// ICU
|
||||
impl->cells[0x200].set = NoSet("ICU::GetRequest");
|
||||
impl->cells[0x200].get = std::bind(&ICU::GetRequest, &icu);
|
||||
impl->cells[0x202].set = std::bind(&ICU::Acknowledge, &icu, _1);
|
||||
impl->cells[0x202].get = std::bind(&ICU::GetAcknowledge, &icu);
|
||||
impl->cells[0x204].set = std::bind(&ICU::Trigger, &icu, _1);
|
||||
impl->cells[0x204].get = std::bind(&ICU::GetTrigger, &icu);
|
||||
impl->cells[0x206].set = std::bind(&ICU::SetEnable, &icu, 0, _1);
|
||||
impl->cells[0x206].get = std::bind(&ICU::GetEnable, &icu, 0);
|
||||
impl->cells[0x208].set = std::bind(&ICU::SetEnable, &icu, 1, _1);
|
||||
impl->cells[0x208].get = std::bind(&ICU::GetEnable, &icu, 1);
|
||||
impl->cells[0x20A].set = std::bind(&ICU::SetEnable, &icu, 2, _1);
|
||||
impl->cells[0x20A].get = std::bind(&ICU::GetEnable, &icu, 2);
|
||||
impl->cells[0x20C].set = std::bind(&ICU::SetEnableVectored, &icu, _1);
|
||||
impl->cells[0x20C].get = std::bind(&ICU::GetEnableVectored, &icu);
|
||||
// impl->cells[0x20E]; // polarity for each interrupt?
|
||||
// impl->cells[0x210]; // source type for each interrupt?
|
||||
for (unsigned i = 0; i < 16; ++i) {
|
||||
impl->cells[0x212 + i * 4] = Cell::BitFieldCell({
|
||||
BitFieldSlot::RefSlot(0, 2, icu.vector_high[i]),
|
||||
BitFieldSlot::RefSlot(15, 1, icu.vector_context_switch[i]),
|
||||
});
|
||||
impl->cells[0x214 + i * 4] = Cell::RefCell(icu.vector_low[i]);
|
||||
}
|
||||
|
||||
// BTDMP
|
||||
for (u16 i = 0; i < 2; ++i) {
|
||||
impl->cells[0x2A2 + i * 0x80].set = std::bind(&Btdmp::SetTransmitClockConfig, &btdmp[i], _1);
|
||||
impl->cells[0x2A2 + i * 0x80].get = std::bind(&Btdmp::GetTransmitClockConfig, &btdmp[i]);
|
||||
impl->cells[0x2BE + i * 0x80].set = std::bind(&Btdmp::SetTransmitEnable, &btdmp[i], _1);
|
||||
impl->cells[0x2BE + i * 0x80].get = std::bind(&Btdmp::GetTransmitEnable, &btdmp[i]);
|
||||
impl->cells[0x2C2 + i * 0x80] = Cell::BitFieldCell({
|
||||
BitFieldSlot{3, 1, {}, std::bind(&Btdmp::GetTransmitFull, &btdmp[i])},
|
||||
BitFieldSlot{4, 1, {}, std::bind(&Btdmp::GetTransmitEmpty, &btdmp[i])},
|
||||
});
|
||||
impl->cells[0x2C6 + i * 0x80].set = std::bind(&Btdmp::Send, &btdmp[i], _1);
|
||||
impl->cells[0x2CA + i * 0x80].set = std::bind(&Btdmp::SetTransmitFlush, &btdmp[i], _1);
|
||||
impl->cells[0x2CA + i * 0x80].get = std::bind(&Btdmp::GetTransmitFlush, &btdmp[i]);
|
||||
}
|
||||
}
|
||||
|
||||
MMIORegion::~MMIORegion() = default;
|
||||
|
||||
u16 MMIORegion::Read(u16 addr) {
|
||||
u16 value = impl->cells[addr].get();
|
||||
return value;
|
||||
}
|
||||
void MMIORegion::Write(u16 addr, u16 value) {
|
||||
impl->cells[addr].set(value);
|
||||
}
|
||||
} // namespace Teakra
|
29
third_party/teakra/src/mmio.h
vendored
Normal file
29
third_party/teakra/src/mmio.h
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include "common_types.h"
|
||||
#include "icu.h"
|
||||
|
||||
namespace Teakra {
|
||||
|
||||
class MemoryInterfaceUnit;
|
||||
class Apbp;
|
||||
class Timer;
|
||||
class Dma;
|
||||
class Ahbm;
|
||||
class Btdmp;
|
||||
|
||||
class MMIORegion {
|
||||
public:
|
||||
MMIORegion(MemoryInterfaceUnit& miu, ICU& icu, Apbp& apbp_from_cpu, Apbp& apbp_from_dsp,
|
||||
std::array<Timer, 2>& timer, Dma& dma, Ahbm& ahbm, std::array<Btdmp, 2>& btdmp);
|
||||
~MMIORegion();
|
||||
u16 Read(u16 addr); // not const because it can be a FIFO register
|
||||
void Write(u16 addr, u16 value);
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
} // namespace Teakra
|
18
third_party/teakra/src/mmio.md
vendored
Normal file
18
third_party/teakra/src/mmio.md
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
# MMIO
|
||||
|
||||
The core processor communicates with peripherals (and indirectly external hardware such as CPU) via MMIO. The MMIO region occupies 0x0800-word region in the 16-bit address space, initially starting at 0x8000. The location of MMIO region can be configured in MIU (...yes, via MMIO). MMIO registers usually use even addresses only, which seems to be a hardware optimization as there is a register in MIU that enables forcing this rule. Each peripheral corresponds to a sub-region in MMIO, listed below. The detail of registers are in their corresponding peripheral documents. The register addresses in these documents are offsets to the start of the MMIO region.
|
||||
|
||||
- `+0x0000` ?
|
||||
- `+0x0004` JAM
|
||||
- `+0x0010` GLUE
|
||||
- `+0x0020` [Timer](timer.md)
|
||||
- `+0x0050` [SIO](sio.md), Serial IO
|
||||
- `+0x0060` [OCEM](ocem.md), On-chip Emulation Module
|
||||
- `+0x0080` [PMU](pmu.md), Power Management Unit
|
||||
- `+0x00C0` [APBP](apbp.md), Advanced Peripheral Bus Port?
|
||||
- `+0x00E0` [AHBM](ahbm.md), Advanced High Performance Bus Master
|
||||
- `+0x0100` [MIU](miu.md), Memory Interface Unit
|
||||
- `+0x0140` [CRU](cru.md), Code Replacement Unit
|
||||
- `+0x0180` [DMA](dma.md), Direct Memory Access
|
||||
- `+0x0200` [ICU](icu.md), Interrupt Control Unit
|
||||
- `+0x0280` [BTDMP](btdmp.md), Buffered Time Division Multiplexing Port
|
9
third_party/teakra/src/mod_test_generator/CMakeLists.txt
vendored
Normal file
9
third_party/teakra/src/mod_test_generator/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
include(CreateDirectoryGroups)
|
||||
|
||||
add_executable(mod_test_generator
|
||||
main.cpp
|
||||
)
|
||||
create_target_directory_groups(mod_test_generator)
|
||||
target_link_libraries(mod_test_generator PRIVATE teakra)
|
||||
target_include_directories(mod_test_generator PRIVATE .)
|
||||
target_compile_options(mod_test_generator PRIVATE ${TEAKRA_CXX_FLAGS})
|
57
third_party/teakra/src/mod_test_generator/main.cpp
vendored
Normal file
57
third_party/teakra/src/mod_test_generator/main.cpp
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include "../test.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
std::fprintf(stderr, "A file path argument must be provided. Exiting...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::unique_ptr<std::FILE, fclose_deleter> f{std::fopen(argv[1], "wb")};
|
||||
if (!f) {
|
||||
std::fprintf(stderr, "Unable to open file %s. Exiting...\n", argv[1]);
|
||||
return -2;
|
||||
}
|
||||
|
||||
TestCase test_case{};
|
||||
test_case.opcode = 0x4DA0; // mpy y0, MemR04@3 || mpyus y1, MemR04@3offsZI@2 || sub3 p0, p1,
|
||||
// Ax@4 || R04@3stepII2@2
|
||||
test_case.expand = 0;
|
||||
test_case.before.mod2 = 1; // enable mod for r0; disable brv
|
||||
for (u16 i = 0; i < TestSpaceSize; ++i) {
|
||||
test_case.before.test_space_x[i] = TestSpaceX + i;
|
||||
}
|
||||
for (u16 i = 0; i < 0x20; ++i) {
|
||||
test_case.before.r[0] = TestSpaceX + i + 0xF0;
|
||||
for (u16 legacy = 0; legacy < 2; ++legacy) {
|
||||
test_case.before.mod1 = legacy << 13;
|
||||
for (u16 offset_mode = 0; offset_mode < 4; ++offset_mode) {
|
||||
for (u16 step_mode = 0; step_mode < 8; ++step_mode) {
|
||||
/*!!!*/ if (step_mode == 3)
|
||||
continue;
|
||||
test_case.before.ar[0] = (step_mode << 5) | (offset_mode << 8);
|
||||
u16 step_min = 0, step_max = 0x20;
|
||||
if (step_mode != 3) {
|
||||
step_min = step_max = std::rand() % 0x20;
|
||||
++step_max;
|
||||
}
|
||||
for (u16 step = step_min; step < step_max; ++step) {
|
||||
u16 step_true = SignExtend<5>(step) & 0x7F;
|
||||
for (u16 mod = 0; mod < 0x10; ++mod) {
|
||||
test_case.before.cfgi = step_true | (mod << 7);
|
||||
if (std::fwrite(&test_case, sizeof(test_case), 1, f.get()) == 0) {
|
||||
std::fprintf(stderr,
|
||||
"Unable to completely write test case. Exiting...\n");
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
87
third_party/teakra/src/ocem.md
vendored
Normal file
87
third_party/teakra/src/ocem.md
vendored
Normal file
|
@ -0,0 +1,87 @@
|
|||
# OCEM
|
||||
|
||||
## MMIO Layout
|
||||
|
||||
The following MMIO definition is derived from Lauterbach's Teak debugger with modification according to the Teak architecture. The different layout around program address breakpoint is tested. Note that this is the only MMIO region that uses odd-address registers
|
||||
|
||||
```
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0060 | PFT |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0061 | |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0062 | PAB1_L |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0063 | | PAP1 | |PAB1_H |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0064 | PAB2_L |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0065 | | PAP2 | |PAB2_H |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0066 | PAB3_L |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0067 | | PAP3 | |PAB3_H |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0068 | | PACNT1 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0069 | | PACNT2 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x006A | | PACNT3 |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x006B | DAM |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x006C | DAB |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x006D |SSE|ILE|BRE|TBE|INE|BRE|P3E|P2E|P1E|EXR|EXW|CDE|DAR|DAW|DVR|DVW|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x006E |DBG|BOT|ERR|MVD| |TRE|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x006F |SFT|ILL|TBF|INT|BR | |PA3|PA2|PA1|ABT|ERG|CD |DA |DV |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|
||||
PFT: Program Flow Trace
|
||||
PAB1_L, PAB1_H: Program Address Break Point #1
|
||||
PAB2_L, PAB2_H: Program Address Break Point #2
|
||||
PAB3_L, PAB3_H: Program Address Break Point #3
|
||||
PAP1, PAP2, PAP3: Program page for Program Address Break Point #1/#2/#3 (?)
|
||||
PACNT1, PACNT2, PACNT3: Program Address Break Point Counter #1/#2/#3
|
||||
DAM: Data Address Mask
|
||||
DAB: Data Address Break Point
|
||||
|
||||
DVW: 1 to enable data value break point on data write transaction
|
||||
DVR: 1 to enable data value break point on data read transaction
|
||||
DAW: 1 to enable data address break point as a result on data write transaction
|
||||
DAR: 1 to enable data address break point as a result on data read transaction
|
||||
CDE: 1 to enable break point as a result of simultaneous data address and data value match
|
||||
EXW: 1 to enable break point as a result of external register write transaction
|
||||
EXR: 1 to enable break point as a result of external register read transaction
|
||||
P1E: 1 to enable program break point #1
|
||||
P2E: 1 to enable program break point #2
|
||||
P3E: 1 to enable program break point #3
|
||||
BRE: 1 to enable break point every time the program jumps instead of executing the next address
|
||||
INE: 1 to enable break point upon detection of interrupt service routine
|
||||
TBE: 1 to enable break point as a result of program flow trace buffer full
|
||||
BRE: 1 to enable break point when returning to the beginning of block repeat loop
|
||||
ILE: 1 to enable break point on illegal condition
|
||||
SSE: 1 to enable single step
|
||||
|
||||
DBG: 1 indicates the debug mode
|
||||
BOT: 1 indicates the boot mode
|
||||
ERR: 1 on detection of user reset that is being activated during execution of break point service routine
|
||||
MVD: 1 on detection of MOVD instruction
|
||||
TRE: 1 indicates that the current TRACE entry has to be combined with the next TRACE entry
|
||||
|
||||
SFT: 1 on detection of software trap
|
||||
ILL: 1 on detection of illegal break point
|
||||
TBF: 1 indicates program flow Trace Buffer Full
|
||||
INT: 1 on detection of interrupt break point
|
||||
BR: 1 on detection of branch break point
|
||||
PA3: 1 on detection of program address break point #3
|
||||
PA2: 1 on detection of program address break point #2
|
||||
PA1: 1 on detection of program address break point #1
|
||||
ABT: 1 on detection of break point due to an external event
|
||||
ERG: 1 on detection of break point due to user defined register transaction
|
||||
CD: 1 on detection of break point due to matched data value and matched data address
|
||||
DA: 1 on detection of break point due to matched data address
|
||||
DV: 1 on detection of break point due to matched data value
|
||||
```
|
698
third_party/teakra/src/operand.h
vendored
Normal file
698
third_party/teakra/src/operand.h
vendored
Normal file
|
@ -0,0 +1,698 @@
|
|||
#pragma once
|
||||
#include "common_types.h"
|
||||
|
||||
template <typename T, T... values>
|
||||
inline constexpr bool NoOverlap = (values + ...) == (values | ...);
|
||||
|
||||
template <unsigned bits>
|
||||
struct Operand {
|
||||
static_assert(bits > 0 && bits <= 16);
|
||||
static constexpr unsigned Bits = bits;
|
||||
|
||||
protected:
|
||||
u16 storage{};
|
||||
|
||||
template <typename OperandT, unsigned pos>
|
||||
friend struct At;
|
||||
|
||||
template <typename OperandT, u16 value>
|
||||
friend struct Const;
|
||||
};
|
||||
|
||||
template <typename OperandT, unsigned pos>
|
||||
struct At {
|
||||
static constexpr unsigned Bits = OperandT::Bits;
|
||||
static_assert((Bits < 16 && pos < 16 && Bits + pos <= 16) || (Bits == 16 && pos == 16));
|
||||
static constexpr u16 Mask = (((1 << Bits) - 1) << pos) & 0xFFFF;
|
||||
static constexpr bool NeedExpansion = pos == 16;
|
||||
static constexpr bool PassAsParameter = true;
|
||||
using FilterResult = OperandT;
|
||||
static constexpr OperandT Extract(u16 opcode, u16 expansion) {
|
||||
OperandT operand{};
|
||||
if (NeedExpansion)
|
||||
operand.storage = expansion;
|
||||
else
|
||||
operand.storage = (u16)((opcode & Mask) >> pos);
|
||||
return operand;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename OperandT, unsigned pos>
|
||||
struct AtNamed {
|
||||
using BaseType = At<OperandT, pos>;
|
||||
static constexpr unsigned Bits = BaseType::Bits;
|
||||
static constexpr u16 Mask = BaseType::Mask;
|
||||
static constexpr bool NeedExpansion = BaseType::NeedExpansion;
|
||||
static constexpr bool PassAsParameter = BaseType::PassAsParameter;
|
||||
using FilterResult = typename BaseType::FilterResult::NameType;
|
||||
static constexpr auto Extract(u16 opcode, u16 expansion) {
|
||||
return BaseType::Extract(opcode, expansion).GetName();
|
||||
}
|
||||
};
|
||||
|
||||
template <unsigned pos>
|
||||
struct Unused {
|
||||
static_assert(pos < 16);
|
||||
static constexpr u16 Mask = 1 << pos;
|
||||
static constexpr bool NeedExpansion = false;
|
||||
static constexpr bool PassAsParameter = false;
|
||||
};
|
||||
|
||||
template <typename OperandT, u16 value>
|
||||
struct Const {
|
||||
static constexpr u16 Mask = 0;
|
||||
static constexpr bool NeedExpansion = false;
|
||||
static constexpr bool PassAsParameter = true;
|
||||
using FilterResult = OperandT;
|
||||
static constexpr OperandT Extract(u16, u16) {
|
||||
OperandT operand{};
|
||||
operand.storage = value;
|
||||
return operand;
|
||||
}
|
||||
};
|
||||
|
||||
enum class SumBase {
|
||||
Zero,
|
||||
Acc,
|
||||
Sv,
|
||||
SvRnd,
|
||||
};
|
||||
|
||||
template <typename T, T value>
|
||||
struct Cn {
|
||||
static constexpr u16 Mask = 0;
|
||||
static constexpr bool NeedExpansion = false;
|
||||
static constexpr bool PassAsParameter = true;
|
||||
using FilterResult = T;
|
||||
static constexpr T Extract(u16, u16) {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
using SX = Cn<bool, true>;
|
||||
using UX = Cn<bool, false>;
|
||||
using SY = Cn<bool, true>;
|
||||
using UY = Cn<bool, false>;
|
||||
using BZr = Cn<SumBase, SumBase::Zero>;
|
||||
using BAc = Cn<SumBase, SumBase::Acc>;
|
||||
using BSv = Cn<SumBase, SumBase::Sv>;
|
||||
using BSr = Cn<SumBase, SumBase::SvRnd>;
|
||||
using PA = Cn<bool, true>;
|
||||
using PP = Cn<bool, false>;
|
||||
using Sub = Cn<bool, true>;
|
||||
using Add = Cn<bool, false>;
|
||||
using EMod = Cn<bool, false>;
|
||||
using DMod = Cn<bool, true>;
|
||||
|
||||
template <typename OperandT, unsigned pos, u16 value>
|
||||
struct AtConst {
|
||||
using Base = At<OperandT, pos>;
|
||||
static_assert(Base::NeedExpansion == false, "");
|
||||
static constexpr u16 Mask = Base::Mask;
|
||||
static constexpr u16 Pad = value << pos;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
constexpr unsigned intlog2(unsigned n) {
|
||||
if (n % 2 != 0)
|
||||
throw "wtf";
|
||||
return (n == 2) ? 1 : 1 + intlog2(n / 2);
|
||||
}
|
||||
|
||||
template <typename EnumT, EnumT... names>
|
||||
struct EnumOperand : Operand<intlog2(sizeof...(names))> {
|
||||
using NameType = EnumT;
|
||||
static constexpr EnumT values[] = {names...};
|
||||
constexpr EnumT GetName() const {
|
||||
return values[this->storage];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename EnumT>
|
||||
struct EnumAllOperand : Operand<intlog2((unsigned)EnumT::EnumEnd)> {
|
||||
using NameType = EnumT;
|
||||
constexpr EnumT GetName() const {
|
||||
return (EnumT)this->storage;
|
||||
}
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
|
||||
enum class RegName {
|
||||
a0, a0l, a0h, a0e,
|
||||
a1, a1l, a1h, a1e,
|
||||
b0, b0l, b0h, b0e,
|
||||
b1, b1l, b1h, b1e,
|
||||
|
||||
r0, r1, r2, r3, r4, r5, r6, r7,
|
||||
|
||||
y0, p,
|
||||
|
||||
pc, sp, sv, lc,
|
||||
|
||||
ar0, ar1,
|
||||
arp0, arp1, arp2, arp3,
|
||||
|
||||
ext0, ext1, ext2, ext3,
|
||||
|
||||
stt0, stt1, stt2,
|
||||
st0, st1, st2,
|
||||
cfgi, cfgj,
|
||||
mod0, mod1, mod2, mod3,
|
||||
|
||||
undefine,
|
||||
};
|
||||
|
||||
template <RegName ... reg_names>
|
||||
using RegOperand = EnumOperand <RegName, reg_names...>;
|
||||
|
||||
struct Register : RegOperand<
|
||||
RegName::r0,
|
||||
RegName::r1,
|
||||
RegName::r2,
|
||||
RegName::r3,
|
||||
RegName::r4,
|
||||
RegName::r5,
|
||||
RegName::r7,
|
||||
RegName::y0,
|
||||
RegName::st0,
|
||||
RegName::st1,
|
||||
RegName::st2,
|
||||
RegName::p, // take special care of this as src operand
|
||||
RegName::pc,
|
||||
RegName::sp,
|
||||
RegName::cfgi,
|
||||
RegName::cfgj,
|
||||
RegName::b0h,
|
||||
RegName::b1h,
|
||||
RegName::b0l,
|
||||
RegName::b1l,
|
||||
RegName::ext0,
|
||||
RegName::ext1,
|
||||
RegName::ext2,
|
||||
RegName::ext3,
|
||||
RegName::a0, // take special care of this as src operand
|
||||
RegName::a1, // take special care of this as src operand
|
||||
RegName::a0l,
|
||||
RegName::a1l,
|
||||
RegName::a0h,
|
||||
RegName::a1h,
|
||||
RegName::lc,
|
||||
RegName::sv
|
||||
> {
|
||||
// only used in mov(Register, Register)
|
||||
constexpr RegName GetNameForMovFromP() {
|
||||
return (this->storage & 1) ? RegName::a1 : RegName::a0;
|
||||
}
|
||||
};
|
||||
struct Ax : RegOperand<
|
||||
RegName::a0,
|
||||
RegName::a1
|
||||
> {};
|
||||
struct Axl : RegOperand<
|
||||
RegName::a0l,
|
||||
RegName::a1l
|
||||
> {};
|
||||
struct Axh : RegOperand<
|
||||
RegName::a0h,
|
||||
RegName::a1h
|
||||
> {};
|
||||
struct Bx : RegOperand<
|
||||
RegName::b0,
|
||||
RegName::b1
|
||||
> {};
|
||||
struct Bxl : RegOperand<
|
||||
RegName::b0l,
|
||||
RegName::b1l
|
||||
> {};
|
||||
struct Bxh : RegOperand<
|
||||
RegName::b0h,
|
||||
RegName::b1h
|
||||
> {};
|
||||
struct Px : Operand<1> {
|
||||
constexpr Px() = default;
|
||||
constexpr Px(u16 index) {
|
||||
this->storage = index;
|
||||
}
|
||||
constexpr u16 Index() const {
|
||||
return this->storage;
|
||||
}
|
||||
};
|
||||
struct Ab : RegOperand<
|
||||
RegName::b0,
|
||||
RegName::b1,
|
||||
RegName::a0,
|
||||
RegName::a1
|
||||
> {};
|
||||
struct Abl : RegOperand<
|
||||
RegName::b0l,
|
||||
RegName::b1l,
|
||||
RegName::a0l,
|
||||
RegName::a1l
|
||||
> {};
|
||||
struct Abh : RegOperand<
|
||||
RegName::b0h,
|
||||
RegName::b1h,
|
||||
RegName::a0h,
|
||||
RegName::a1h
|
||||
> {};
|
||||
struct Abe : RegOperand<
|
||||
RegName::b0e,
|
||||
RegName::b1e,
|
||||
RegName::a0e,
|
||||
RegName::a1e
|
||||
> {};
|
||||
struct Ablh : RegOperand<
|
||||
RegName::b0l,
|
||||
RegName::b0h,
|
||||
RegName::b1l,
|
||||
RegName::b1h,
|
||||
RegName::a0l,
|
||||
RegName::a0h,
|
||||
RegName::a1l,
|
||||
RegName::a1h
|
||||
> {};
|
||||
struct RnOld : RegOperand<
|
||||
RegName::r0,
|
||||
RegName::r1,
|
||||
RegName::r2,
|
||||
RegName::r3,
|
||||
RegName::r4,
|
||||
RegName::r5,
|
||||
RegName::r7,
|
||||
RegName::y0
|
||||
> {};
|
||||
struct Rn : RegOperand<
|
||||
RegName::r0,
|
||||
RegName::r1,
|
||||
RegName::r2,
|
||||
RegName::r3,
|
||||
RegName::r4,
|
||||
RegName::r5,
|
||||
RegName::r6,
|
||||
RegName::r7
|
||||
> {
|
||||
constexpr Rn() = default;
|
||||
constexpr Rn(u16 index) {
|
||||
this->storage = index;
|
||||
}
|
||||
constexpr u16 Index() const {
|
||||
return this->storage;
|
||||
}
|
||||
};
|
||||
|
||||
struct R45 : RegOperand<
|
||||
RegName::r4,
|
||||
RegName::r5
|
||||
> {
|
||||
constexpr u16 Index() const {
|
||||
return this->storage + 4;
|
||||
}
|
||||
};
|
||||
|
||||
struct R0123 : RegOperand<
|
||||
RegName::r0,
|
||||
RegName::r1,
|
||||
RegName::r2,
|
||||
RegName::r3
|
||||
> {
|
||||
constexpr u16 Index() const {
|
||||
return this->storage;
|
||||
}
|
||||
};
|
||||
|
||||
struct ArArpSttMod : RegOperand<
|
||||
RegName::ar0,
|
||||
RegName::ar1,
|
||||
RegName::arp0,
|
||||
RegName::arp1,
|
||||
RegName::arp2,
|
||||
RegName::arp3,
|
||||
RegName::undefine,
|
||||
RegName::undefine,
|
||||
RegName::stt0,
|
||||
RegName::stt1,
|
||||
RegName::stt2,
|
||||
RegName::undefine,
|
||||
RegName::mod0,
|
||||
RegName::mod1,
|
||||
RegName::mod2,
|
||||
RegName::mod3
|
||||
> {};
|
||||
struct ArArp : RegOperand<
|
||||
RegName::ar0,
|
||||
RegName::ar1,
|
||||
RegName::arp0,
|
||||
RegName::arp1,
|
||||
RegName::arp2,
|
||||
RegName::arp3,
|
||||
RegName::undefine,
|
||||
RegName::undefine
|
||||
> {};
|
||||
struct SttMod : RegOperand<
|
||||
RegName::stt0,
|
||||
RegName::stt1,
|
||||
RegName::stt2,
|
||||
RegName::undefine,
|
||||
RegName::mod0,
|
||||
RegName::mod1,
|
||||
RegName::mod2,
|
||||
RegName::mod3
|
||||
> {};
|
||||
|
||||
struct Ar : RegOperand<
|
||||
RegName::ar0,
|
||||
RegName::ar1
|
||||
> {
|
||||
constexpr u16 Index() const {
|
||||
return this->storage;
|
||||
}
|
||||
};
|
||||
|
||||
struct Arp : RegOperand<
|
||||
RegName::arp0,
|
||||
RegName::arp1,
|
||||
RegName::arp2,
|
||||
RegName::arp3
|
||||
> {
|
||||
constexpr u16 Index() const {
|
||||
return this->storage;
|
||||
}
|
||||
};
|
||||
|
||||
enum SwapTypeValue {
|
||||
a0b0,
|
||||
a0b1,
|
||||
a1b0,
|
||||
a1b1,
|
||||
a0b0a1b1,
|
||||
a0b1a1b0,
|
||||
a0b0a1,
|
||||
a0b1a1,
|
||||
a1b0a0,
|
||||
a1b1a0,
|
||||
b0a0b1,
|
||||
b0a1b1,
|
||||
b1a0b0,
|
||||
b1a1b0,
|
||||
reserved0,
|
||||
reserved1,
|
||||
|
||||
EnumEnd,
|
||||
};
|
||||
|
||||
using SwapType = EnumAllOperand<SwapTypeValue>;
|
||||
|
||||
enum class StepValue {
|
||||
Zero,
|
||||
Increase,
|
||||
Decrease,
|
||||
PlusStep,
|
||||
Increase2Mode1,
|
||||
Decrease2Mode1,
|
||||
Increase2Mode2,
|
||||
Decrease2Mode2,
|
||||
};
|
||||
|
||||
using StepZIDS = EnumOperand<StepValue,
|
||||
StepValue::Zero,
|
||||
StepValue::Increase,
|
||||
StepValue::Decrease,
|
||||
StepValue::PlusStep
|
||||
>;
|
||||
|
||||
template<unsigned bits, u16 offset = 0>
|
||||
struct ArIndex : Operand<bits>{
|
||||
constexpr u16 Index() const {
|
||||
return this->storage + offset;
|
||||
}
|
||||
};
|
||||
|
||||
struct ArRn1 : ArIndex<1> {};
|
||||
struct ArRn2 : ArIndex<2> {};
|
||||
struct ArStep1 : ArIndex<1> {};
|
||||
struct ArStep1Alt : ArIndex<1, 2> {};
|
||||
struct ArStep2 : ArIndex<2> {};
|
||||
struct ArpRn1 : ArIndex<1> {};
|
||||
struct ArpRn2 : ArIndex<2> {};
|
||||
struct ArpStep1 : ArIndex<1> {};
|
||||
struct ArpStep2 : ArIndex<2> {};
|
||||
|
||||
struct Address18_2 : Operand<2> {
|
||||
constexpr u32 Address32() const {
|
||||
return (u32)(this->storage) << 16;
|
||||
}
|
||||
};
|
||||
struct Address18_16 : Operand<16> {
|
||||
constexpr u32 Address32() const {
|
||||
return this->storage;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr u32 Address32(Address18_16 low, Address18_2 high) {
|
||||
return low.Address32() | high.Address32();
|
||||
}
|
||||
|
||||
struct Address16 : Operand<16> {
|
||||
constexpr u32 Address32() {
|
||||
return this->storage;
|
||||
}
|
||||
};
|
||||
|
||||
struct RelAddr7 : Operand<7> {
|
||||
constexpr u32 Relative32() {
|
||||
return SignExtend<7, u32>(this->storage);
|
||||
}
|
||||
};
|
||||
|
||||
template <unsigned bits>
|
||||
struct Imm : Operand<bits> {
|
||||
constexpr u16 Unsigned16() const {
|
||||
return this->storage;
|
||||
}
|
||||
};
|
||||
|
||||
template <unsigned bits>
|
||||
struct Imms : Operand<bits> {
|
||||
constexpr u16 Signed16() const {
|
||||
return SignExtend<bits, u16>(this->storage);
|
||||
}
|
||||
};
|
||||
|
||||
struct Imm2 : Imm<2> {};
|
||||
struct Imm4 : Imm<4> {};
|
||||
struct Imm5 : Imm<5> {};
|
||||
struct Imm5s : Imms<5> {};
|
||||
struct Imm6s : Imms<6> {};
|
||||
struct Imm7s : Imms<7> {};
|
||||
struct Imm8 : Imm<8> {};
|
||||
struct Imm8s : Imms<8> {};
|
||||
struct Imm9 : Imm<9> {};
|
||||
struct Imm16 : Imm<16> {};
|
||||
|
||||
struct MemImm8 : Imm8 {};
|
||||
struct MemImm16 : Imm16 {};
|
||||
struct MemR7Imm7s : Imm7s {};
|
||||
struct MemR7Imm16 : Imm16 {};
|
||||
|
||||
|
||||
enum class AlmOp {
|
||||
Or,
|
||||
And,
|
||||
Xor,
|
||||
Add,
|
||||
Tst0,
|
||||
Tst1,
|
||||
Cmp,
|
||||
Sub,
|
||||
Msu,
|
||||
Addh,
|
||||
Addl,
|
||||
Subh,
|
||||
Subl,
|
||||
Sqr,
|
||||
Sqra,
|
||||
Cmpu,
|
||||
|
||||
Reserved
|
||||
};
|
||||
|
||||
using Alm = EnumOperand<AlmOp,
|
||||
AlmOp::Or,
|
||||
AlmOp::And,
|
||||
AlmOp::Xor,
|
||||
AlmOp::Add,
|
||||
AlmOp::Tst0,
|
||||
AlmOp::Tst1,
|
||||
AlmOp::Cmp,
|
||||
AlmOp::Sub,
|
||||
AlmOp::Msu,
|
||||
AlmOp::Addh,
|
||||
AlmOp::Addl,
|
||||
AlmOp::Subh,
|
||||
AlmOp::Subl,
|
||||
AlmOp::Sqr,
|
||||
AlmOp::Sqra,
|
||||
AlmOp::Cmpu
|
||||
>;
|
||||
|
||||
using Alu = EnumOperand<AlmOp,
|
||||
AlmOp::Or,
|
||||
AlmOp::And,
|
||||
AlmOp::Xor,
|
||||
AlmOp::Add,
|
||||
AlmOp::Reserved,
|
||||
AlmOp::Reserved,
|
||||
AlmOp::Cmp,
|
||||
AlmOp::Sub
|
||||
>;
|
||||
|
||||
enum class AlbOp {
|
||||
Set,
|
||||
Rst,
|
||||
Chng,
|
||||
Addv,
|
||||
Tst0,
|
||||
Tst1,
|
||||
Cmpv,
|
||||
Subv,
|
||||
|
||||
EnumEnd
|
||||
};
|
||||
|
||||
using Alb = EnumAllOperand<AlbOp>;
|
||||
|
||||
enum class MulOp {
|
||||
Mpy,
|
||||
Mpysu,
|
||||
Mac,
|
||||
Macus,
|
||||
Maa,
|
||||
Macuu,
|
||||
Macsu,
|
||||
Maasu,
|
||||
};
|
||||
|
||||
using Mul3 = EnumOperand<MulOp,
|
||||
MulOp::Mpy,
|
||||
MulOp::Mpysu,
|
||||
MulOp::Mac,
|
||||
MulOp::Macus,
|
||||
MulOp::Maa,
|
||||
MulOp::Macuu,
|
||||
MulOp::Macsu,
|
||||
MulOp::Maasu
|
||||
>;
|
||||
|
||||
using Mul2 = EnumOperand<MulOp,
|
||||
MulOp::Mpy,
|
||||
MulOp::Mac,
|
||||
MulOp::Maa,
|
||||
MulOp::Macsu
|
||||
>;
|
||||
|
||||
enum class ModaOp {
|
||||
Shr,
|
||||
Shr4,
|
||||
Shl,
|
||||
Shl4,
|
||||
Ror,
|
||||
Rol,
|
||||
Clr,
|
||||
Reserved,
|
||||
Not,
|
||||
Neg,
|
||||
Rnd,
|
||||
Pacr,
|
||||
Clrr,
|
||||
Inc,
|
||||
Dec,
|
||||
Copy,
|
||||
|
||||
};
|
||||
|
||||
using Moda4 = EnumOperand<ModaOp,
|
||||
ModaOp::Shr,
|
||||
ModaOp::Shr4,
|
||||
ModaOp::Shl,
|
||||
ModaOp::Shl4,
|
||||
ModaOp::Ror,
|
||||
ModaOp::Rol,
|
||||
ModaOp::Clr,
|
||||
ModaOp::Reserved,
|
||||
ModaOp::Not,
|
||||
ModaOp::Neg,
|
||||
ModaOp::Rnd,
|
||||
ModaOp::Pacr,
|
||||
ModaOp::Clrr,
|
||||
ModaOp::Inc,
|
||||
ModaOp::Dec,
|
||||
ModaOp::Copy
|
||||
>;
|
||||
|
||||
using Moda3 = EnumOperand<ModaOp,
|
||||
ModaOp::Shr,
|
||||
ModaOp::Shr4,
|
||||
ModaOp::Shl,
|
||||
ModaOp::Shl4,
|
||||
ModaOp::Ror,
|
||||
ModaOp::Rol,
|
||||
ModaOp::Clr,
|
||||
ModaOp::Clrr
|
||||
>;
|
||||
|
||||
enum class CondValue {
|
||||
True,
|
||||
Eq,
|
||||
Neq,
|
||||
Gt,
|
||||
Ge,
|
||||
Lt,
|
||||
Le,
|
||||
Nn,
|
||||
C,
|
||||
V,
|
||||
E,
|
||||
L,
|
||||
Nr,
|
||||
Niu0,
|
||||
Iu0,
|
||||
Iu1,
|
||||
|
||||
EnumEnd
|
||||
};
|
||||
|
||||
using Cond = EnumAllOperand<CondValue>;
|
||||
|
||||
struct BankFlags : Operand<6>{
|
||||
constexpr bool Cfgi() const {
|
||||
return (this->storage & 1) != 0;
|
||||
}
|
||||
constexpr bool R4() const {
|
||||
return (this->storage & 2) != 0;
|
||||
}
|
||||
constexpr bool R1() const {
|
||||
return (this->storage & 4) != 0;
|
||||
}
|
||||
constexpr bool R0() const {
|
||||
return (this->storage & 8) != 0;
|
||||
}
|
||||
constexpr bool R7() const {
|
||||
return (this->storage & 16) != 0;
|
||||
}
|
||||
constexpr bool Cfgj() const {
|
||||
return (this->storage & 32) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
enum class CbsCondValue {
|
||||
Ge,
|
||||
Gt,
|
||||
|
||||
EnumEnd
|
||||
};
|
||||
|
||||
using CbsCond = EnumAllOperand<CbsCondValue>;
|
||||
|
||||
// clang-format on
|
92
third_party/teakra/src/parser.cpp
vendored
Normal file
92
third_party/teakra/src/parser.cpp
vendored
Normal file
|
@ -0,0 +1,92 @@
|
|||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include "../include/teakra/disassembler.h"
|
||||
#include "common_types.h"
|
||||
#include "crash.h"
|
||||
#include "parser.h"
|
||||
|
||||
using NodeAsConst = std::string;
|
||||
struct NodeAsExpansion {};
|
||||
|
||||
bool operator==(NodeAsExpansion, NodeAsExpansion) {
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<NodeAsExpansion> {
|
||||
typedef NodeAsExpansion argument_type;
|
||||
typedef std::size_t result_type;
|
||||
result_type operator()(argument_type const& s) const {
|
||||
return 0x12345678;
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
namespace Teakra {
|
||||
class ParserImpl : public Parser {
|
||||
public:
|
||||
using NodeKey = std::variant<NodeAsConst, NodeAsExpansion>;
|
||||
|
||||
struct Node {
|
||||
bool end = false;
|
||||
u16 opcode = 0;
|
||||
bool expansion = false;
|
||||
std::unordered_map<NodeKey, std::unique_ptr<Node>> children;
|
||||
};
|
||||
|
||||
Node root;
|
||||
|
||||
Opcode Parse(const std::vector<std::string>& tokens) override {
|
||||
Node* current = &root;
|
||||
for (auto& token : tokens) {
|
||||
auto const_find = current->children.find(token);
|
||||
if (const_find != current->children.end()) {
|
||||
current = const_find->second.get();
|
||||
} else {
|
||||
return Opcode{Opcode::Invalid};
|
||||
}
|
||||
}
|
||||
if (!current->end) {
|
||||
return Opcode{Opcode::Invalid};
|
||||
}
|
||||
return Opcode{current->expansion ? Opcode::ValidWithExpansion : Opcode::Valid,
|
||||
current->opcode};
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Parser> GenerateParser() {
|
||||
std::unique_ptr<ParserImpl> parser = std::make_unique<ParserImpl>();
|
||||
for (u32 opcode = 0; opcode < 0x10000; ++opcode) {
|
||||
u16 o = (u16)opcode;
|
||||
bool expansion = Disassembler::NeedExpansion(o);
|
||||
auto tokens = Disassembler::GetTokenList(o);
|
||||
|
||||
if (std::any_of(tokens.begin(), tokens.end(), [](const auto& token) {
|
||||
return token.find("[ERROR]") != std::string::npos;
|
||||
}))
|
||||
continue;
|
||||
|
||||
ParserImpl::Node* current = &parser->root;
|
||||
for (const auto& token : tokens) {
|
||||
auto& next = current->children[token];
|
||||
if (!next)
|
||||
next = std::make_unique<ParserImpl::Node>();
|
||||
current = next.get();
|
||||
}
|
||||
|
||||
if (current->end) {
|
||||
ASSERT((current->opcode & (u16)(~o)) == 0);
|
||||
continue;
|
||||
}
|
||||
current->end = true;
|
||||
current->opcode = o;
|
||||
current->expansion = expansion;
|
||||
}
|
||||
return parser;
|
||||
}
|
||||
|
||||
} // namespace Teakra
|
26
third_party/teakra/src/parser.h
vendored
Normal file
26
third_party/teakra/src/parser.h
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common_types.h"
|
||||
|
||||
namespace Teakra {
|
||||
|
||||
class Parser {
|
||||
public:
|
||||
virtual ~Parser() = default;
|
||||
struct Opcode {
|
||||
enum {
|
||||
Invalid,
|
||||
Valid,
|
||||
ValidWithExpansion,
|
||||
} status = Invalid;
|
||||
u16 opcode = 0;
|
||||
};
|
||||
|
||||
virtual Opcode Parse(const std::vector<std::string>& tokens) = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<Parser> GenerateParser();
|
||||
|
||||
} // namespace Teakra
|
78
third_party/teakra/src/pmu.md
vendored
Normal file
78
third_party/teakra/src/pmu.md
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
# PMU
|
||||
|
||||
## MMIO Layout
|
||||
|
||||
The following MMIO definition is extracted from Lauterbach's Teak debugger and is not tested at all.
|
||||
|
||||
```
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0080 | PLLMUL |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0082 | |PLO|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0084 |PLB| | CLKD |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0086 | |SDJ|SD1|SD0|SDO| |SDA|SDH|SDG|SDS|SDD|SDC|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0088 | |I0J|I01|I00|I0O| |I0A|I0H|I0G|I0S|I0D|I0C|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x008A | |I1J|I11|I10|I1O| |I1A|I1H|I1G|I1S|I1D|I1C|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x008C | |I2J|I21|I20|I2O| |I2A|I2H|I2G|I2S|I2D|I2C|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x008E | |IVJ|IV1|IV0|IVO| |IVA|IVH|IVG|IVS|IVD|IVC|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0090 | |T0J|T01| |T0O| |T0A|T0H|T0G|T0S|T0D|T0C|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0092 | |T1J| |T10|T1O| |T1A|T1H|T1G|T1S|T1D|T1C|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0094 | |NMJ|NM1|NM0|NMO| |NMA|NMH|NMG|NMS|NMD|NMC|
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0096 | | | | | | | | | | |EXD| |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x0098 | | |BM1|BM0| | | | | | |BMD| |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x009A | | SDB |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x009C | | I0B |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x009E | | I1B |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00A0 | | I2B |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00A2 | | IVB |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00A4 | | T0B |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|+0x00A6 | | T1B |
|
||||
+-----------#---+---+---+---#---+---+---+---#---+---+---+---#---+---+---+---#
|
||||
|
||||
PLLMUL: Configuration of the PLL clock multiplication
|
||||
PLO: PLL power-on configuration value for PLL use
|
||||
CLKD: Clock Division Factor. When writing a value of 0 or 1 to CLKD, then the clock frequency is divided by 1
|
||||
PLB: 1 to bypass PLL
|
||||
|
||||
SDx: 1 to shut down module x
|
||||
I0x: 1 to enable module x recovery on interrupt 0
|
||||
I1x: 1 to enable module x recovery on interrupt 1
|
||||
I2x: 1 to enable module x recovery on interrupt 2
|
||||
IVx: 1 to enable module x recovery on vectored interrupt
|
||||
T0x: 1 to enable module x recovery on timer 0
|
||||
T1x: 1 to enable module x recovery on timer 1
|
||||
NMx: 1 to enable module x recovery on non-maskable interrupt
|
||||
EXx: 1 to enable module x recovery on external signal
|
||||
BMx: 1 to enable module x breakpoint mask
|
||||
|
||||
The module "x" in registers above means
|
||||
- C: core
|
||||
- D: DMA
|
||||
- S: SIO
|
||||
- G: GLUE
|
||||
- H: APBP/HPI
|
||||
- A: AHBM
|
||||
- O: OCEM
|
||||
- 0: Timer 0
|
||||
- 1: Timer 1
|
||||
- J: JAM
|
||||
- B: BTDMP 0/1/2/3, for bit 0/1/2/3 respectively
|
||||
```
|
42
third_party/teakra/src/processor.cpp
vendored
Normal file
42
third_party/teakra/src/processor.cpp
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include "interpreter.h"
|
||||
#include "processor.h"
|
||||
#include "register.h"
|
||||
|
||||
namespace Teakra {
|
||||
|
||||
struct Processor::Impl {
|
||||
Impl(CoreTiming& core_timing, MemoryInterface& memory_interface)
|
||||
: core_timing(core_timing), interpreter(core_timing, regs, memory_interface) {}
|
||||
CoreTiming& core_timing;
|
||||
RegisterState regs;
|
||||
Interpreter interpreter;
|
||||
};
|
||||
|
||||
Processor::Processor(CoreTiming& core_timing, MemoryInterface& memory_interface)
|
||||
: impl(new Impl(core_timing, memory_interface)) {}
|
||||
Processor::~Processor() = default;
|
||||
|
||||
void Processor::Reset() {
|
||||
impl->regs = RegisterState();
|
||||
}
|
||||
|
||||
void Processor::Run(unsigned cycles) {
|
||||
impl->interpreter.Run(cycles);
|
||||
}
|
||||
|
||||
void Processor::SignalInterrupt(u32 i) {
|
||||
impl->interpreter.SignalInterrupt(i);
|
||||
}
|
||||
void Processor::SignalVectoredInterrupt(u32 address, bool context_switch) {
|
||||
impl->interpreter.SignalVectoredInterrupt(address, context_switch);
|
||||
}
|
||||
|
||||
Teakra::RegisterState& Processor::GetRegisterState() {
|
||||
return impl->regs;
|
||||
}
|
||||
|
||||
const Teakra::RegisterState& Processor::GetRegisterState() const {
|
||||
return impl->regs;
|
||||
}
|
||||
|
||||
} // namespace Teakra
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue