Vendor Teakra

This commit is contained in:
wheremyfoodat 2025-08-21 21:44:39 +03:00
commit d65617356f
125 changed files with 37732 additions and 4 deletions

3
.gitmodules vendored
View file

@ -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 +0,0 @@
Subproject commit e34a86799efd65e3c44b915a4d65b3514d34df4f

88
third_party/teakra/.clang-format vendored Normal file
View 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
...

View 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
View 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
View 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
)

View 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
View 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
View file

@ -0,0 +1,21 @@
# Teakra
[![Build Status](https://github.com/wwylele/teakra/actions/workflows/build.yaml/badge.svg)](https://github.com/wwylele/teakra/actions/workflows/build.yaml)
[![Build status](https://ci.appveyor.com/api/projects/status/mxr5tg4v8dafyqec/branch/master?svg=true)](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
View 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 ..

View 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()

File diff suppressed because it is too large Load diff

View 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

View 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
#---------------------------------------------------------------------------------------

Binary file not shown.

Binary file not shown.

View 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

View 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

View 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))

View 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;
}

View 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

View 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
#---------------------------------------------------------------------------------------

Binary file not shown.

Binary file not shown.

View 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

View 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

View 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))

View 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;
}

View 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

View 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
#---------------------------------------------------------------------------------------

Binary file not shown.

Binary file not shown.

View 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

View file

226
third_party/teakra/hwtest/dsptester/rsf.rsf vendored Executable file
View 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

View 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))

View 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;
}

View 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

View 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
#---------------------------------------------------------------------------------------

Binary file not shown.

Binary file not shown.

View 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
View 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

View 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))

View 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;
}

View 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

View 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

View 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

View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
```

View 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})

View 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;
};

View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

View 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
View 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
View 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
View 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.

View 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})

View 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
View 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
View 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

File diff suppressed because it is too large Load diff

View 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
View 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);
}

View 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;
}
}

View 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
View 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;
};

View 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

View 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
View 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
View 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
View 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
View 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

View 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})

View 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
View 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
View 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
View 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
View 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
View 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
View 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