mirror of
https://github.com/PoroCYon/teakra.git
synced 2025-06-18 14:25:33 -04:00
new interface
This commit is contained in:
parent
ca1cbec70b
commit
ad521387d2
67
CMakeLists.txt
Normal file
67
CMakeLists.txt
Normal 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)
|
17
CMakeModules/CreateDirectoryGroups.cmake
Normal file
17
CMakeModules/CreateDirectoryGroups.cmake
Normal file
@ -0,0 +1,17 @@
|
||||
# This function should be passed a name of an existing target. It will automatically generate
|
||||
# file groups following the directory hierarchy, so that the layout of the files in IDEs matches the
|
||||
# one in the filesystem.
|
||||
function(create_target_directory_groups target_name)
|
||||
# Place any files that aren't in the source list in a separate group so that they don't get in
|
||||
# the way.
|
||||
source_group("Other Files" REGULAR_EXPRESSION ".")
|
||||
|
||||
get_target_property(target_sources "${target_name}" SOURCES)
|
||||
|
||||
foreach(file_name IN LISTS target_sources)
|
||||
get_filename_component(dir_name "${file_name}" PATH)
|
||||
# Group names use '\' as a separator even though the entire rest of CMake uses '/'...
|
||||
string(REPLACE "/" "\\" group_name "${dir_name}")
|
||||
source_group("${group_name}" FILES "${file_name}")
|
||||
endforeach()
|
||||
endfunction()
|
38
Makefile
38
Makefile
@ -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
34
include/teakra/teakra.h
Normal 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
38
src/CMakeLists.txt
Normal 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})
|
@ -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
31
src/core.cpp
Normal 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
23
src/core.h
Normal 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
|
@ -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()) {
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
@ -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 = ®s.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
|
||||
|
20
src/main.cpp
20
src/main.cpp
@ -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);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
32
src/memory.h
32
src/memory.h
@ -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
35
src/memory_interface.cpp
Normal 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
54
src/memory_interface.h
Normal 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
|
18
src/mmio.cpp
18
src/mmio.cpp
@ -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
|
||||
|
36
src/mmio.h
36
src/mmio.h
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
22
src/shared_memory.h
Normal 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
65
src/teakra.cpp
Normal 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
|
Loading…
Reference in New Issue
Block a user