new interface

This commit is contained in:
wwylele 2018-02-09 17:09:30 +02:00
parent ca1cbec70b
commit ad521387d2
22 changed files with 538 additions and 246 deletions

67
CMakeLists.txt Normal file
View File

@ -0,0 +1,67 @@
cmake_minimum_required(VERSION 3.8)
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()
# 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(DYNARMIC_CXX_FLAGS
/std:c++latest # CMAKE_CXX_STANDARD as no effect on MSVC until CMake 3.10.
/W4
/w34263 # Non-virtual member function hides base class virtual function
/w44265 # Class has virtual functions, but destructor is not virtual
/w34456 # Declaration of 'var' hides previous local declaration
/w34457 # Declaration of 'var' hides function parameter
/w34458 # Declaration of 'var' hides class member
/w34459 # Declaration of 'var' hides global definition
/w34946 # Reinterpret-cast between related types
/wd4592 # Symbol will be dynamically initialized (implementation limitation)
/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)
else()
set(TEAKRA_CXX_FLAGS
-Wall
-Wextra
-Wcast-qual
-pedantic
-pedantic-errors
-Wfatal-errors
-Wno-missing-braces
-Wno-unused-parameter)
endif()
# Prefer the -pthread flag on Linux.
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
# Teakra project files
add_subdirectory(src)

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

View File

@ -1,38 +0,0 @@
CXX = c++
CXX_FLAGS = -Wall -ggdb -DDEBUG -std=c++17 -I/opt/boost
LDFLAGS :=
# Final binary
BIN = teakra
# Put all auto generated stuff to this build dir.
BUILD_DIR = ./build
# List of all .cpp source files.
CPP = $(wildcard src/*.cpp)
# All .o files go to build dir.
OBJ = $(CPP:%.cpp=$(BUILD_DIR)/%.o)
# Gcc/Clang will create these .d files containing dependencies.
DEP = $(OBJ:%.o=%.d)
# Default target named after the binary.
$(BIN) : $(BUILD_DIR)/$(BIN)
# Actual target of the binary - depends on all .o files.
$(BUILD_DIR)/$(BIN) : $(OBJ)
mkdir -p $(@D)
$(CXX) $^ -o $@ $(LDFLAGS)
# Include all .d files
-include $(DEP)
# Build target for every single object file.
# The potential dependency on header files is covered
# by calling `-include $(DEP)`.
$(BUILD_DIR)/%.o : %.cpp
mkdir -p $(@D)
$(CXX) $(CXX_FLAGS) -MMD -c $< -o $@
.PHONY : clean
clean :
-rm $(BUILD_DIR)/$(BIN) $(OBJ) $(DEP)

34
include/teakra/teakra.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <cstdint>
#include <functional>
#include <array>
#include <memory>
namespace Teakra {
class Teakra {
public:
Teakra();
~Teakra();
std::array<std::uint8_t, 0x80000>& GetDspMemory();
// 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);
void SetRecvDataHandler(std::uint8_t index, std::function<void()> handler);
// APBP Semaphore
void SetSemaphore(std::uint16_t value);
void SetSemaphoreHandler(std::uint8_t index, std::function<void()> handler);
// core
void Stop();
void Start();
private:
struct Impl;
std::unique_ptr<Impl> impl;
};
} // namespace Teakra

38
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,38 @@
include(CreateDirectoryGroups)
add_library(teakra
../include/teakra/teakra.h
common_types.h
core.cpp
core.h
decoder.h
disassembler.h
icu.h
interpreter.h
matcher.h
memory_interface.cpp
memory_interface.h
mmio.cpp
mmio.h
oprand.h
register.h
shared_memory.h
teakra.cpp
)
create_target_directory_groups(teakra)
target_link_libraries(teakra PRIVATE Threads::Threads)
target_include_directories(teakra
PUBLIC ../include
PRIVATE .)
target_compile_options(teakra PRIVATE ${TEAKRA_CXX_FLAGS})
#####
add_executable(teakra_tests
main.cpp
)
create_target_directory_groups(teakra_tests)
target_link_libraries(teakra_tests PRIVATE teakra)
target_include_directories(teakra_tests PRIVATE .)
target_compile_options(teakra_tests PRIVATE ${TEAKRA_CXX_FLAGS})

View File

@ -1,4 +1,4 @@
#include "citra.h"
/*#include "citra.h"
#include <sys/mman.h>
#include <sys/types.h>
@ -35,4 +35,4 @@ void DspMemorySharedWithCitra::DWrite(u16 addr, u16 value) {
return;
}
data[miu.ConvertAddressByBank(addr)] = value;
}
}*/

31
src/core.cpp Normal file
View File

@ -0,0 +1,31 @@
#include "core.h"
#include "interpreter.h"
#include "register.h"
namespace Teakra {
struct Core::Impl {
Impl(MemoryInterface& memory_interface) : interpreter(regs, memory_interface) {}
RegisterState regs;
Interpreter interpreter;
};
Core::Core(MemoryInterface& memory_interface) : impl(new Impl(memory_interface)) {}
Core::~Core() = default;
void Core::Reset() {
impl->regs = RegisterState();
}
void Core::Run(unsigned cycles) {
impl->interpreter.Run(cycles);
}
void Core::SignalInterrupt(u32 i) {
impl->interpreter.SignalInterrupt(i);
}
void Core::SignalVectoredInterrupt(u32 address) {
impl->interpreter.SignalVectoredInterrupt(address);
}
} // namespace Teakra

23
src/core.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include "common_types.h"
#include <memory>
namespace Teakra {
class MemoryInterface;
class Core {
public:
Core(MemoryInterface& memory_interface);
~Core();
void Reset();
void Run(unsigned cycles);
void SignalInterrupt(u32 i);
void SignalVectoredInterrupt(u32 address);
private:
struct Impl;
std::unique_ptr<Impl> impl;
};
} // namespace Teakra

View File

@ -1,30 +1,16 @@
#pragma once
#include "matcher.h"
#include "oprand.h"
#include <boost/mp11.hpp>
#include <boost/optional.hpp>
#include <optional>
#include <type_traits>
#include <vector>
template <typename OprandAtTA, typename OprandAtTB>
struct OprandAtNoOverlap {
static constexpr bool value =
(OprandAtTA::Mask & OprandAtTB::Mask) == 0
|| std::is_same<OprandAtTA, OprandAtTB>::value;
};
template <typename V, typename F, u16 expected, typename ... OprandAtT>
struct MatcherCreator {
static Matcher<V> Create(const char* name, F func) {
// Oprands shouldn't overlap each other
using L = boost::mp11::mp_list<OprandAtT...>;
static_assert(boost::mp11::mp_apply<boost::mp11::mp_all,
boost::mp11::mp_product<OprandAtNoOverlap, L, L>>::value, "Error");
// Expected code should be zero on oprands position
static_assert(((OprandAtT::Mask | ...) & expected) == 0, "Error");
// Oprands shouldn't overlap each other, nor overlap with the expected ones
static_assert((OprandAtT::Mask + ... + expected) == (OprandAtT::Mask | ... | expected), "Error");
auto proxy = [func](V& visitor, u16 opcode, u16 expansion) {
return (visitor.*func)(OprandAtT::Filter(opcode, expansion) ...);
@ -409,14 +395,14 @@ std::vector<Matcher<V>> GetDecodeTable() {
}
template<typename V>
boost::optional<const Matcher<V>&> Decode(u16 instruction) {
std::optional<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 boost::none;
return std::nullopt;
} else {
auto other = std::find_if(iter + 1, table.end(), matches_instruction);
if (other != table.end()) {

View File

@ -1,4 +1,4 @@
#include "dsp1.h"
/*#include "dsp1.h"
#include <cstdio>
#include <cstring>
#include <utility>
@ -20,8 +20,7 @@ Dsp1::Dsp1(const std::vector<u8>& raw) {
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);*/
segment.target = header.segments[i].address;
printf("[Segment %u]\n", i);
printf("memory_type = %d\n", segment.memory_type);
@ -30,4 +29,4 @@ Dsp1::Dsp1(const std::vector<u8>& raw) {
segments.push_back(std::move(segment));
}
}
}*/

View File

View File

@ -2,24 +2,25 @@
#include "decoder.h"
#include "oprand.h"
#include "register.h"
#include "memory.h"
#include "memory_interface.h"
#include <unordered_set>
#include <tuple>
namespace Teakra {
class Interpreter {
public:
Interpreter (RegisterState& regs, MemoryInterface& mem) : regs(regs), mem(mem) {}
Interpreter(RegisterState& regs, MemoryInterface& mem) : regs(regs), mem(mem) {}
void Run(unsigned cycles) {
for (unsigned i = 0; i < cycles; ++i) {
u16 opcode = mem.PRead(regs.pc++);
u16 opcode = mem.ProgramRead(regs.pc++);
auto decoder = Decode<Interpreter>(opcode);
if (!decoder)
throw "unknown code!";
u16 expand_value = 0;
if (decoder->NeedExpansion()) {
expand_value = mem.PRead(regs.pc++);
expand_value = mem.ProgramRead(regs.pc++);
}
if (regs.rep) {
@ -53,8 +54,8 @@ public:
regs.ie = 0;
u16 l = regs.GetPcL();
u16 h = regs.GetPcH();
mem.DWrite(--regs.sp, l);
mem.DWrite(--regs.sp, h);
mem.DataWrite(--regs.sp, l);
mem.DataWrite(--regs.sp, h);
regs.pc = 0x0006 + i * 8;
interrupt_handled = true;
if (regs.ic[i]) {
@ -68,8 +69,8 @@ public:
regs.ie = 0;
u16 l = regs.GetPcL();
u16 h = regs.GetPcH();
mem.DWrite(--regs.sp, l);
mem.DWrite(--regs.sp, h);
mem.DataWrite(--regs.sp, l);
mem.DataWrite(--regs.sp, h);
regs.pc = regs.viaddr;
if (regs.vic) {
ContextStore();
@ -235,7 +236,7 @@ public:
}
void alm(Alm op, Rn a, StepZIDS as, Ax b) {
u16 address = RnAddressAndModify(GetRnUnit(a.GetName()), as.GetName());
u16 value = mem.DRead(address);
u16 value = mem.DataRead(address);
AlmGeneric(op.GetName(), ExtendOprandForAlm(op.GetName(), value), b);
}
void alm(Alm op, Register a, Ax b) {
@ -377,10 +378,10 @@ public:
}
void alb(Alb op, Imm16 a, Rn b, StepZIDS bs) {
u16 address = RnAddressAndModify(GetRnUnit(b.GetName()), bs.GetName());
u16 bv = mem.DRead(address);
u16 bv = mem.DataRead(address);
u16 result = GenericAlb(op, a.storage, bv);
if (IsAlbModifying(op))
mem.DWrite(address, result);
mem.DataWrite(address, result);
}
void alb(Alb op, Imm16 a, Register b) {
u16 bv;
@ -729,7 +730,7 @@ public:
regs.bkrep_stack.begin() + 1);
++regs.bcn;
}
u32 flag = mem.DRead(++address_reg);
u32 flag = mem.DataRead(++address_reg);
u16 valid = flag >> 15;
if (regs.lp) {
if (!valid)
@ -738,18 +739,18 @@ public:
if (valid)
regs.lp = regs.bcn = 1;
}
regs.bkrep_stack[0].end = mem.DRead(++address_reg) | (((flag >> 8) & 3) << 16);
regs.bkrep_stack[0].start = mem.DRead(++address_reg) | ((flag & 3) << 16);
regs.bkrep_stack[0].lc = mem.DRead(++address_reg);
regs.bkrep_stack[0].end = mem.DataRead(++address_reg) | (((flag >> 8) & 3) << 16);
regs.bkrep_stack[0].start = mem.DataRead(++address_reg) | ((flag & 3) << 16);
regs.bkrep_stack[0].lc = mem.DataRead(++address_reg);
}
void StoreBlockRepeat(u16& address_reg) {
mem.DWrite(address_reg--, regs.bkrep_stack[0].lc);
mem.DWrite(address_reg--, regs.bkrep_stack[0].start & 0xFFFF);
mem.DWrite(address_reg--, regs.bkrep_stack[0].end & 0xFFFF);
mem.DataWrite(address_reg--, regs.bkrep_stack[0].lc);
mem.DataWrite(address_reg--, regs.bkrep_stack[0].start & 0xFFFF);
mem.DataWrite(address_reg--, regs.bkrep_stack[0].end & 0xFFFF);
u16 flag = regs.lp << 15;
flag |= regs.bkrep_stack[0].start >> 16;
flag |= (regs.bkrep_stack[0].start >> 16) << 8;
mem.DWrite(address_reg--, flag);
mem.DataWrite(address_reg--, flag);
if (regs.lp) {
std::copy(regs.bkrep_stack.begin() + 1, regs.bkrep_stack.begin() + regs.bcn,
regs.bkrep_stack.begin());
@ -858,31 +859,31 @@ public:
if (regs.ConditionPass(cond)) {
u16 l = regs.GetPcL();
u16 h = regs.GetPcH();
mem.DWrite(--regs.sp, l);
mem.DWrite(--regs.sp, h);
mem.DataWrite(--regs.sp, l);
mem.DataWrite(--regs.sp, h);
regs.SetPC(addr_low.storage, addr_high.storage);
}
}
void calla(Axl a) {
u16 l = regs.GetPcL();
u16 h = regs.GetPcH();
mem.DWrite(--regs.sp, l);
mem.DWrite(--regs.sp, h);
mem.DataWrite(--regs.sp, l);
mem.DataWrite(--regs.sp, h);
regs.pc = RegToBus16(a.GetName()); // use movpd?
}
void calla(Ax a) {
u16 l = regs.GetPcL();
u16 h = regs.GetPcH();
mem.DWrite(--regs.sp, l);
mem.DWrite(--regs.sp, h);
mem.DataWrite(--regs.sp, l);
mem.DataWrite(--regs.sp, h);
regs.pc = GetAcc(a.GetName()) & 0x3FFFF; // no saturation ?
}
void callr(RelAddr7 addr, Cond cond) {
if (regs.ConditionPass(cond)) {
u16 l = regs.GetPcL();
u16 h = regs.GetPcH();
mem.DWrite(--regs.sp, l);
mem.DWrite(--regs.sp, h);
mem.DataWrite(--regs.sp, l);
mem.DataWrite(--regs.sp, h);
regs.pc += SignExtend<7, u32>(addr.storage);
}
}
@ -915,8 +916,8 @@ public:
void ret(Cond c) {
if (regs.ConditionPass(c)) {
u16 h = mem.DRead(regs.sp++);
u16 l = mem.DRead(regs.sp++);
u16 h = mem.DataRead(regs.sp++);
u16 l = mem.DataRead(regs.sp++);
regs.SetPC(l, h);
}
}
@ -925,16 +926,16 @@ public:
}
void reti(Cond c) {
if (regs.ConditionPass(c)) {
u16 h = mem.DRead(regs.sp++);
u16 l = mem.DRead(regs.sp++);
u16 h = mem.DataRead(regs.sp++);
u16 l = mem.DataRead(regs.sp++);
regs.SetPC(l, h);
regs.ie = 1;
}
}
void retic(Cond c) {
if (regs.ConditionPass(c)) {
u16 h = mem.DRead(regs.sp++);
u16 l = mem.DRead(regs.sp++);
u16 h = mem.DataRead(regs.sp++);
u16 l = mem.DataRead(regs.sp++);
regs.SetPC(l, h);
regs.ie = 1;
ContextRestore();
@ -947,8 +948,8 @@ public:
throw "unimplemented";
}
void rets(Imm8 a) {
u16 h = mem.DRead(regs.sp++);
u16 l = mem.DRead(regs.sp++);
u16 h = mem.DataRead(regs.sp++);
u16 l = mem.DataRead(regs.sp++);
regs.SetPC(l, h);
regs.sp += a.storage;
}
@ -980,20 +981,20 @@ public:
}
void push(Imm16 a) {
mem.DWrite(--regs.sp, a.storage);
mem.DataWrite(--regs.sp, a.storage);
}
void push(Register a) {
// need test: p0, aX
u16 value = RegToBus16(a.GetName());
mem.DWrite(--regs.sp, value);
mem.DataWrite(--regs.sp, value);
}
void push(Abe a) {
u16 value = (SaturateAcc(GetAcc(a.GetName()), false) >> 32) & 0xFFFF;
mem.DWrite(--regs.sp, value);
mem.DataWrite(--regs.sp, value);
}
void push(ArArpSttMod a) {
u16 value = RegToBus16(a.GetName());
mem.DWrite(--regs.sp, value);
mem.DataWrite(--regs.sp, value);
}
void push_prpage(Dummy) {
throw "unimplemented";
@ -1002,51 +1003,51 @@ public:
u32 value = (u32)ProductToBus40(a.GetName());
u16 h = value >> 16;
u16 l = value & 0xFFFF;
mem.DWrite(--regs.sp, l);
mem.DWrite(--regs.sp, h);
mem.DataWrite(--regs.sp, l);
mem.DataWrite(--regs.sp, h);
}
void push_r6(Dummy) {
u16 value = regs.r[6];
mem.DWrite(--regs.sp, value);
mem.DataWrite(--regs.sp, value);
}
void push_repc(Dummy) {
u16 value = regs.repc;
mem.DWrite(--regs.sp, value);
mem.DataWrite(--regs.sp, value);
}
void push_x0(Dummy) {
u16 value = regs.x[0];
mem.DWrite(--regs.sp, value);
mem.DataWrite(--regs.sp, value);
}
void push_x1(Dummy) {
u16 value = regs.x[1];
mem.DWrite(--regs.sp, value);
mem.DataWrite(--regs.sp, value);
}
void push_y1(Dummy) {
u16 value = regs.y[1];
mem.DWrite(--regs.sp, value);
mem.DataWrite(--regs.sp, value);
}
void pusha(Ax a) {
u32 value = SaturateAcc(GetAcc(a.GetName()), false) & 0xFFFF'FFFF;
u16 h = value >> 16;
u16 l = value & 0xFFFF;
mem.DWrite(--regs.sp, l);
mem.DWrite(--regs.sp, h);
mem.DataWrite(--regs.sp, l);
mem.DataWrite(--regs.sp, h);
}
void pusha(Bx a) {
u32 value = SaturateAcc(GetAcc(a.GetName()), false) & 0xFFFF'FFFF;
u16 h = value >> 16;
u16 l = value & 0xFFFF;
mem.DWrite(--regs.sp, l);
mem.DWrite(--regs.sp, h);
mem.DataWrite(--regs.sp, l);
mem.DataWrite(--regs.sp, h);
}
void pop(Register a) {
// need test: p0
u16 value = mem.DRead(regs.sp++);
u16 value = mem.DataRead(regs.sp++);
RegFromBus16(a.GetName(), value);
}
void pop(Abe a) {
u32 value32 = SignExtend<8, u32>(mem.DRead(regs.sp++) & 0xFF);
u32 value32 = SignExtend<8, u32>(mem.DataRead(regs.sp++) & 0xFF);
RegisterState::Accumulator* target;
switch(a.GetName()) {
case RegName::a0e: target = &regs.a[0]; break;
@ -1058,45 +1059,45 @@ public:
SetAcc(a.GetName(), (target->value & 0xFFFFFFFF) | (u64)value32 << 32);
}
void pop(ArArpSttMod a) {
u16 value = mem.DRead(regs.sp++);
u16 value = mem.DataRead(regs.sp++);
RegFromBus16(a.GetName(), value);
}
void pop(Bx a) {
u16 value = mem.DRead(regs.sp++);
u16 value = mem.DataRead(regs.sp++);
RegFromBus16(a.GetName(), value);
}
void pop_prpage(Dummy) {
throw "unimplemented";
}
void pop(Px a) {
u16 h = mem.DRead(regs.sp++);
u16 l = mem.DRead(regs.sp++);
u16 h = mem.DataRead(regs.sp++);
u16 l = mem.DataRead(regs.sp++);
u32 value = ((u32)h << 16) | l;
ProductFromBus32(a.GetName(), value);
}
void pop_r6(Dummy) {
u16 value = mem.DRead(regs.sp++);
u16 value = mem.DataRead(regs.sp++);
regs.r[6] = value;
}
void pop_repc(Dummy) {
u16 value = mem.DRead(regs.sp++);
u16 value = mem.DataRead(regs.sp++);
regs.repc = value;
}
void pop_x0(Dummy) {
u16 value = mem.DRead(regs.sp++);
u16 value = mem.DataRead(regs.sp++);
regs.x[0] = value;
}
void pop_x1(Dummy) {
u16 value = mem.DRead(regs.sp++);
u16 value = mem.DataRead(regs.sp++);
regs.x[1] = value;
}
void pop_y1(Dummy) {
u16 value = mem.DRead(regs.sp++);
u16 value = mem.DataRead(regs.sp++);
regs.y[1] = value;
}
void popa(Ab a) {
u16 h = mem.DRead(regs.sp++);
u16 l = mem.DRead(regs.sp++);
u16 h = mem.DataRead(regs.sp++);
u16 l = mem.DataRead(regs.sp++);
u64 value = SignExtend<32, u64>((h << 16) | l);
SetAcc(a.GetName(), value);
}
@ -1141,7 +1142,7 @@ public:
}
void tstb(Rn a, StepZIDS as, Imm4 b) {
u16 address = RnAddressAndModify(GetRnUnit(a.GetName()), as.GetName());
u16 value = mem.DRead(address);
u16 value = mem.DataRead(address);
regs.fz = (value >> b.storage) & 1;
}
void tstb(Register a, Imm4 b) {
@ -1200,13 +1201,13 @@ public:
void mul(Mul3 op, Rn y, StepZIDS ys, Imm16 x, Ax a) {
u16 address = RnAddressAndModify(GetRnUnit(y.GetName()), ys.GetName());
regs.y[0] = mem.DRead(address);
regs.y[0] = mem.DataRead(address);
regs.x[0] = x.storage;
MulGeneric(op.GetName(), a);
}
void mul_y0(Mul3 op, Rn x, StepZIDS xs, Ax a) {
u16 address = RnAddressAndModify(GetRnUnit(x.GetName()), xs.GetName());
regs.x[0] = mem.DRead(address);
regs.x[0] = mem.DataRead(address);
MulGeneric(op.GetName(), a);
}
void mul_y0(Mul3 op, Register x, Ax a) {
@ -1217,8 +1218,8 @@ public:
void mul(Mul3 op, R45 y, StepZIDS ys, R0123 x, StepZIDS xs, Ax a) {
u16 address_y = RnAddressAndModify(GetRnUnit(y.GetName()), ys.GetName());
u16 address_x = RnAddressAndModify(GetRnUnit(x.GetName()), xs.GetName());
regs.y[0] = mem.DRead(address_y);
regs.x[0] = mem.DRead(address_x);
regs.y[0] = mem.DataRead(address_y);
regs.x[0] = mem.DataRead(address_x);
MulGeneric(op.GetName(), a);
}
void mul_y0_r6(Mul3 op, Ax a) {
@ -1302,29 +1303,29 @@ public:
u16 address_s = RnAddressAndModify(GetRnUnit(a.GetName()), as.GetName());
u32 address_d = RnAddressAndModify(GetRnUnit(b.GetName()), bs.GetName());
address_d |= (u32)regs.movpd << 16;
mem.PWrite(address_d, mem.DRead(address_s));
mem.ProgramWrite(address_d, mem.DataRead(address_s));
}
void movp(Axl a, Register b) {
u32 address = RegToBus16(a.GetName());
address |= (u32)regs.movpd << 16;
u16 value = mem.PRead(address);
u16 value = mem.ProgramRead(address);
RegFromBus16(b.GetName(), value);
}
void movp(Ax a, Register b) {
u32 address = GetAcc(a.GetName()) & 0x3FFFF; // no saturation
u16 value = mem.PRead(address);
u16 value = mem.ProgramRead(address);
RegFromBus16(b.GetName(), value);
}
void movp(Rn a, StepZIDS as, R0123 b, StepZIDS bs) {
u32 address_s = RnAddressAndModify(GetRnUnit(a.GetName()), as.GetName());
u16 address_d = RnAddressAndModify(GetRnUnit(b.GetName()), bs.GetName());
address_s |= (u32)regs.movpd << 16;
mem.DWrite(address_d, mem.PRead(address_s));
mem.DataWrite(address_d, mem.ProgramRead(address_s));
}
void movpdw(Ax a) {
u32 address = GetAcc(a.GetName()) & 0x3FFFF; // no saturation
u16 h = mem.PRead(address);
u16 l = mem.PRead(address);
u16 h = mem.ProgramRead(address);
u16 l = mem.ProgramRead(address);
regs.SetPC(l, h);
}
@ -1349,16 +1350,16 @@ public:
}
void StoreToMemory(MemImm8 addr, u16 value) {
mem.DWrite(addr.storage + (regs.page << 8), value);
mem.DataWrite(addr.storage + (regs.page << 8), value);
}
void StoreToMemory(MemImm16 addr, u16 value) {
mem.DWrite(addr.storage, value);
mem.DataWrite(addr.storage, value);
}
void StoreToMemory(MemR7Imm16 addr, u16 value) {
mem.DWrite(addr.storage + regs.r[7], value);
mem.DataWrite(addr.storage + regs.r[7], value);
}
void StoreToMemory(MemR7Imm7s addr, u16 value) {
mem.DWrite(addr.storage + regs.r[7], value);
mem.DataWrite(addr.storage + regs.r[7], value);
}
void mov(Ablh a, MemImm8 b) {
@ -1379,16 +1380,16 @@ public:
}
u16 LoadFromMemory(MemImm8 addr) {
return mem.DRead(addr.storage + (regs.page << 8));
return mem.DataRead(addr.storage + (regs.page << 8));
}
u16 LoadFromMemory(MemImm16 addr) {
return mem.DRead(addr.storage);
return mem.DataRead(addr.storage);
}
u16 LoadFromMemory(MemR7Imm16 addr) {
return mem.DRead(addr.storage + regs.r[7]);
return mem.DataRead(addr.storage + regs.r[7]);
}
u16 LoadFromMemory(MemR7Imm7s addr) {
return mem.DRead(addr.storage + regs.r[7]);
return mem.DataRead(addr.storage + regs.r[7]);
}
void mov(MemImm16 a, Ax b) {
@ -1458,16 +1459,16 @@ public:
}
void mov(Rn a, StepZIDS as, Bx b) {
u16 address = RnAddressAndModify(GetRnUnit(a.GetName()), as.GetName());
u16 value = mem.DRead(address);
u16 value = mem.DataRead(address);
RegFromBus16(b.GetName(), value);
}
void mov(Rn a, StepZIDS as, Register b) {
u16 address = RnAddressAndModify(GetRnUnit(a.GetName()), as.GetName());
u16 value = mem.DRead(address);
u16 value = mem.DataRead(address);
RegFromBus16(b.GetName(), value);
}
void mov_memsp_to(Register b) {
u16 value = mem.DRead(regs.sp);
u16 value = mem.DataRead(regs.sp);
RegFromBus16(b.GetName(), value);
}
void mov_mixp_to(Register b) {
@ -1491,7 +1492,7 @@ public:
// a = p0 untested
u16 value = RegToBus16(a.GetName());
u16 address = RnAddressAndModify(GetRnUnit(b.GetName()), bs.GetName());
mem.DWrite(address, value);
mem.DataWrite(address, value);
}
void mov(Register a, Bx b) {
if (a.GetName() == RegName::p) {
@ -1624,33 +1625,33 @@ public:
void mov_repc_to(ArRn1 b, ArStep1 bs) {
u16 address = RnAddressAndModify(GetArRnUnit(b.storage), GetArStep(bs.storage));
u16 value = regs.repc;
mem.DWrite(address, value);
mem.DataWrite(address, value);
}
void mov(ArArp a, ArRn1 b, ArStep1 bs) {
u16 address = RnAddressAndModify(GetArRnUnit(b.storage), GetArStep(bs.storage));
u16 value = RegToBus16(a.GetName());
mem.DWrite(address, value);
mem.DataWrite(address, value);
}
void mov(SttMod a, ArRn1 b, ArStep1 bs) {
u16 address = RnAddressAndModify(GetArRnUnit(b.storage), GetArStep(bs.storage));
u16 value = RegToBus16(a.GetName());
mem.DWrite(address, value);
mem.DataWrite(address, value);
}
void mov_repc(ArRn1 a, ArStep1 as) {
u16 address = RnAddressAndModify(GetArRnUnit(a.storage), GetArStep(as.storage));
u16 value = mem.DRead(address);
u16 value = mem.DataRead(address);
regs.repc = value;
}
void mov(ArRn1 a, ArStep1 as, ArArp b) {
// are you sure it is ok to both use and modify ar registers?
u16 address = RnAddressAndModify(GetArRnUnit(a.storage), GetArStep(as.storage));
u16 value = mem.DRead(address);
u16 value = mem.DataRead(address);
RegFromBus16(b.GetName(), value);
}
void mov(ArRn1 a, ArStep1 as, SttMod b) {
u16 address = RnAddressAndModify(GetArRnUnit(a.storage), GetArStep(as.storage));
u16 value = mem.DRead(address);
u16 value = mem.DataRead(address);
RegFromBus16(b.GetName(), value);
}
@ -1716,8 +1717,8 @@ public:
u16 h = (value >> 16) & 0xFFFF;
u16 address = RnAddressAndModify(GetArRnUnit(b.storage), GetArStep(bs.storage));
u16 address2 = address + GetArOffset(bs.storage);
mem.DWrite(address, l);
mem.DWrite(address2, h);
mem.DataWrite(address, l);
mem.DataWrite(address2, h);
}
void mov2s(Px a, ArRn2 b, ArStep2 bs) {
u64 value = ProductToBus40(a.GetName());
@ -1725,14 +1726,14 @@ public:
u16 h = (value >> 16) & 0xFFFF;
u16 address = RnAddressAndModify(GetArRnUnit(b.storage), GetArStep(bs.storage));
u16 address2 = address + GetArOffset(bs.storage);
mem.DWrite(address, l);
mem.DWrite(address2, h);
mem.DataWrite(address, l);
mem.DataWrite(address2, h);
}
void mov2(ArRn2 a, ArStep2 as, Px b) {
u16 address = RnAddressAndModify(GetArRnUnit(a.storage), GetArStep(as.storage));
u16 address2 = address + GetArOffset(as.storage);
u16 l = mem.DRead(address);
u16 h = mem.DRead(address2);
u16 l = mem.DataRead(address);
u16 h = mem.DataRead(address2);
u64 value = SignExtend<32, u64>((h << 16) | l);
ProductFromBus32(b.GetName(), value);
}
@ -1742,14 +1743,14 @@ public:
u16 h = (value >> 16) & 0xFFFF;
u16 address = RnAddressAndModify(GetArRnUnit(b.storage), GetArStep(bs.storage));
u16 address2 = address + GetArOffset(bs.storage);
mem.DWrite(address, l);
mem.DWrite(address2, h);
mem.DataWrite(address, l);
mem.DataWrite(address2, h);
}
void mova(ArRn2 a, ArStep2 as, Ab b) {
u16 address = RnAddressAndModify(GetArRnUnit(a.storage), GetArStep(as.storage));
u16 address2 = address + GetArOffset(as.storage);
u16 l = mem.DRead(address);
u16 h = mem.DRead(address2);
u16 l = mem.DataRead(address);
u16 h = mem.DataRead(address2);
u64 value = SignExtend<32, u64>((h << 16) | l);
SetAcc(b.GetName(), value);
}
@ -1771,17 +1772,17 @@ public:
regs.r[6] = value;
}
void mov_memsp_r6(Dummy) {
u16 value = mem.DRead(regs.sp);
u16 value = mem.DataRead(regs.sp);
regs.r[6] = value;
}
void mov_r6_to(Rn b, StepZIDS bs) {
u16 value = regs.r[6];
u16 address = RnAddressAndModify(GetRnUnit(b.GetName()), bs.GetName());
mem.DWrite(address, value);
mem.DataWrite(address, value);
}
void mov_r6(Rn a, StepZIDS as) {
u16 address = RnAddressAndModify(GetRnUnit(a.GetName()), as.GetName());
u16 value = mem.DRead(address);
u16 value = mem.DataRead(address);
regs.r[6] = value;
}
@ -1838,7 +1839,7 @@ public:
}
void movs(Rn a, StepZIDS as, Ab b) {
u16 address = RnAddressAndModify(GetRnUnit(a.GetName()), as.GetName());
u16 value = mem.DRead(address);
u16 value = mem.DataRead(address);
u16 sv = regs.sv;
SetAcc(b.GetName(), ShiftBus40(value, sv), /*No saturation if logic shift*/regs.s == 1);
}
@ -2228,3 +2229,5 @@ private:
regs.psign[unit] = value >> 31;
}
};
} // namespace Teakra

View File

@ -1,4 +1,4 @@
#include <cstdio>
/*#include <cstdio>
#include <string>
#include <vector>
@ -8,20 +8,26 @@
#include "interpreter.h"
#include "oprand.h"
#include "register.h"
#include "citra.h"
#include "citra.h"*/
#include "teakra/teakra.h"
#include "decoder.h"
#include "disassembler.h"
int main() {
Teakra::Teakra teakra;
for (u32 opcode = 0; opcode < 0x10000; ++opcode) {
Decode<Disassembler>((u16)opcode);
}
/*
FILE* file = fopen("/media/wwylele/学习_娱乐/3DS/PokemonY.romfs/sound/dspaudio.cdc", "rb");
std::vector<u8> raw(217976);
fread(raw.data(), raw.size(), 1, file);
fclose(file);
Dsp1 dsp(raw);
Disassembler dsm;
for (u32 opcode = 0; opcode < 0x10000; ++opcode) {
Decode<Disassembler>((u16)opcode);
}
file = fopen("/home/wwylele/teakra/teakra.out", "wt");
@ -75,5 +81,5 @@ int main() {
r.Reset();
while(1) {
interpreter.Run(1);
}
}*/
}

View File

@ -1,32 +0,0 @@
#pragma once
#include <array>
#include "common_types.h"
class MemoryInterface {
public:
virtual ~MemoryInterface() = default;
virtual u16 PRead(u32 addr) const = 0;
virtual void PWrite(u32 addr, u16 value) = 0;
virtual u16 DRead(u16 addr) = 0; // not const because it can be a FIFO register
virtual void DWrite(u16 addr, u16 value) = 0;
};
class DspMemory : public MemoryInterface {
public:
std::array<u16, 0x20000> program;
std::array<u16, 0x20000> data;
u16 PRead(u32 addr) const override {
return program[addr];
}
void PWrite(u32 addr, u16 value) override {
program[addr] = value;
}
u16 DRead(u16 addr) override {
return data[addr];
}
void DWrite(u16 addr, u16 value) override {
data[addr] = value;
}
};

35
src/memory_interface.cpp Normal file
View File

@ -0,0 +1,35 @@
#include "memory_interface.h"
#include "shared_memory.h"
#include "mmio.h"
namespace Teakra {
MemoryInterface::MemoryInterface(SharedMemory& shared_memory,
DataMemoryController& data_memory_controller, MMIORegion& mmio) : shared_memory(shared_memory),
data_memory_controller(data_memory_controller), 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) {
if (data_memory_controller.InMMIO(address)) {
return mmio.Read(data_memory_controller.ToMMIO(address));
}
u32 converted = data_memory_controller.ConvertAddressByBank(address);
u16 value = shared_memory.ReadWord(converted);
//printf("READ @ %04X -> %04X\n", address, value);
return value;
}
void MemoryInterface::DataWrite(u16 address, u16 value) {
if (data_memory_controller.InMMIO(address)) {
return mmio.Write(data_memory_controller.ToMMIO(address), value);
}
u32 converted = data_memory_controller.ConvertAddressByBank(address);
//printf("READ @ %04X <- %04X\n", address, value);
shared_memory.WriteWord(converted, value);
}
} // namespace Teakra

54
src/memory_interface.h Normal file
View File

@ -0,0 +1,54 @@
#pragma once
#include "common_types.h"
#include <array>
namespace Teakra {
class DataMemoryController {
public:
u16 GetMMIOLocation() const {
return mmio_location;
}
void SetMMIOLocation(u16 value) {
mmio_location = value;
}
bool InMMIO(u16 addr) const {
return addr >= mmio_location && addr < mmio_location + 0x800;
}
u16 ToMMIO(u16 addr) const {
return addr - mmio_location;
}
void SetMemoryBank(u8 region, u16 bank) {
memory_bank[region] = bank;
}
u16 GetMemoryBank(u8 region) {
return memory_bank[region];
}
u32 ConvertAddressByBank(u16 address) {
return 0x20000 + address + memory_bank[address / 0x8000] * 0x10000;
}
private:
u16 mmio_location = 0x8000;
std::array<u8, 2> memory_bank{};
};
struct SharedMemory;
class MMIORegion;
class MemoryInterface {
public:
MemoryInterface(SharedMemory& shared_memory, DataMemoryController& data_memory_controller,
MMIORegion& mmio);
u16 ProgramRead(u32 address) const;
void ProgramWrite(u32 address, u16 value);
u16 DataRead(u16 address); // not const because it can be a FIFO register
void DataWrite(u16 address, u16 value);
private:
SharedMemory& shared_memory;
DataMemoryController& data_memory_controller;
MMIORegion& mmio;
};
} // namespace Teakra

View File

@ -1,6 +1,9 @@
#include "mmio.h"
#include "memory_interface.h"
#include <functional>
namespace Teakra {
auto NoSet = [](u16) {printf("Warning: NoSet\n");};
auto NoGet = []()->u16 {printf("Warning: NoGet\n"); return 0;};
@ -27,20 +30,20 @@ public:
std::array<Cell, 0x800> cells{};
};
MMIORegion::MMIORegion(MIU& miu, ICU& icu) : impl(new Impl), miu(miu), icu(icu) {
MMIORegion::MMIORegion(DataMemoryController& miu, ICU& icu) : impl(new Impl), miu(miu), icu(icu) {
using namespace std::placeholders;
impl->cells[0x01A] = Cell(0xC902); // chip detect
// memory bank setter has a delay of about 1 cycle. Game usually has a nop after the write instruction
impl->cells[0x10E].set = std::bind(&MIU::SetMemoryBank, &miu, 0, _1);
impl->cells[0x10E].get = std::bind(&MIU::GetMemoryBank, &miu, 0);
impl->cells[0x110].set = std::bind(&MIU::SetMemoryBank, &miu, 1, _1);
impl->cells[0x110].get = std::bind(&MIU::GetMemoryBank, &miu, 1);
impl->cells[0x10E].set = std::bind(&DataMemoryController::SetMemoryBank, &miu, 0, _1);
impl->cells[0x10E].get = std::bind(&DataMemoryController::GetMemoryBank, &miu, 0);
impl->cells[0x110].set = std::bind(&DataMemoryController::SetMemoryBank, &miu, 1, _1);
impl->cells[0x110].get = std::bind(&DataMemoryController::GetMemoryBank, &miu, 1);
impl->cells[0x114].set(0x1E20); // low = X space size, high = Y space size (both in 0x400)
impl->cells[0x11A].set(0x0014); // bit 6 enables memory bank exchanging?
impl->cells[0x11E].set = std::bind(&MIU::SetMMIOLocation, &miu, _1);
impl->cells[0x11E].get = std::bind(&MIU::GetMMIOLocation, &miu);
impl->cells[0x11E].set = std::bind(&DataMemoryController::SetMMIOLocation, &miu, _1);
impl->cells[0x11E].get = std::bind(&DataMemoryController::GetMMIOLocation, &miu);
impl->cells[0x200].set = NoSet;
impl->cells[0x200].get = std::bind(&ICU::GetRequest, &icu);
@ -77,3 +80,4 @@ void MMIORegion::Write(u16 addr, u16 value) {
printf(">>>>>>>>> MMIO Write @%04X <- %04X\n", addr, value);
impl->cells[addr].set(value);
}
} // namespace Teakra

View File

@ -4,37 +4,13 @@
#include <array>
#include "icu.h"
class MIU {
public:
u16 GetMMIOLocation() const {
return mmio_location;
}
void SetMMIOLocation(u16 value) {
mmio_location = value;
}
bool InMMIO(u16 addr) const {
return addr >= mmio_location && addr < mmio_location + 0x800;
}
u16 ToMMIO(u16 addr) const {
return addr - mmio_location;
}
void SetMemoryBank(u8 region, u16 bank) {
memory_bank[region] = bank;
}
u16 GetMemoryBank(u8 region) {
return memory_bank[region];
}
u32 ConvertAddressByBank(u16 address) {
return address + memory_bank[address / 0x8000] * 0x10000;
}
private:
u16 mmio_location = 0x8000;
std::array<u8, 2> memory_bank{};
};
namespace Teakra {
class DataMemoryController;
class MMIORegion {
public:
MMIORegion(MIU& miu, ICU& icu);
MMIORegion(DataMemoryController& miu, ICU& icu);
~MMIORegion();
u16 Read(u16 addr); // not const because it can be a FIFO register
void Write(u16 addr, u16 value);
@ -42,6 +18,8 @@ private:
class Impl;
std::unique_ptr<Impl> impl;
MIU& miu;
DataMemoryController& miu;
ICU& icu;
};
} // namespace Teakra

View File

@ -32,7 +32,7 @@ struct Const {
using OprandType = OprandT;
static constexpr u16 Mask = 0;
static constexpr bool NeedExpansion = false;
static OprandT Filter(u16 opcode, u16 expansion) {
static OprandT Filter(u16, u16) {
OprandT oprand;
oprand.storage = value;
return oprand;

View File

@ -102,7 +102,7 @@ struct RegisterState {
class RORedirector : public Redirector {
using Redirector::Redirector;
void Set(u16 value) override {}
void Set(u16) override {}
};
class AccEProxy : public RegisterProxy {

22
src/shared_memory.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include "common_types.h"
#include <array>
namespace Teakra {
struct SharedMemory {
std::array<u8, 0x80000> raw;
u16 ReadWord(u32 word_address) const {
u32 byte_address = word_address * 2;
u8 low = raw[byte_address];
u8 high = raw[byte_address + 1];
return low | ((u16)high << 8);
}
void WriteWord(u32 word_address, u16 value) {
u8 low = value & 0xFF;
u8 high = value >> 8;
u32 byte_address = word_address * 2;
raw[byte_address] = low;
raw[byte_address + 1] = high;
}
};
} // namespace Teakra

65
src/teakra.cpp Normal file
View File

@ -0,0 +1,65 @@
#include "teakra/teakra.h"
#include "shared_memory.h"
#include "memory_interface.h"
#include "mmio.h"
#include "icu.h"
#include "core.h"
#include <thread>
#include <atomic>
namespace Teakra {
struct Teakra::Impl {
SharedMemory shared_memory;
DataMemoryController data_memory_controller;
ICU icu;
MMIORegion mmio{data_memory_controller, icu};
MemoryInterface memory_interface{shared_memory, data_memory_controller, mmio};
Core core{memory_interface};
Impl() {
using namespace std::placeholders;
icu.OnInterrupt = std::bind(&Core::SignalInterrupt, &core, _1);
icu.OnVectoredInterrupt = std::bind(&Core::SignalVectoredInterrupt, &core, _1);
}
std::unique_ptr<std::thread> core_thread = nullptr;
std::atomic_flag running_flag = ATOMIC_FLAG_INIT;
void CoreThread() {
core.Reset();
while(running_flag.test_and_set()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
core.Run(1);
}
running_flag.clear();
}
};
Teakra::Teakra() : impl(new Impl) {}
Teakra::~Teakra() {
if (impl->core_thread)
Stop();
}
std::array<std::uint8_t, 0x80000>& Teakra::GetDspMemory() {
return impl->shared_memory.raw;
}
void Teakra::Start() {
if (impl->core_thread)
Stop();
impl->core.Reset();
impl->running_flag.test_and_set();
impl->core_thread = std::make_unique<std::thread>(&Impl::CoreThread, impl.get());
}
void Teakra::Stop() {
if (!impl->core_thread)
return;
impl->running_flag.clear();
impl->core_thread->join();
impl->core_thread = nullptr;
}
} // namespace Teakra