Fix merge conflicts

This commit is contained in:
darkf 2014-12-29 19:47:41 -08:00
commit 8ba9ac0f74
288 changed files with 17413 additions and 13969 deletions

View file

@ -18,35 +18,46 @@ set(SRCS
arm/skyeye_common/vfp/vfpinstr.cpp
arm/skyeye_common/vfp/vfpsingle.cpp
file_sys/archive_romfs.cpp
file_sys/archive_savedata.cpp
file_sys/archive_sdmc.cpp
file_sys/archive_systemsavedata.cpp
file_sys/disk_archive.cpp
file_sys/file_romfs.cpp
file_sys/file_sdmc.cpp
file_sys/directory_romfs.cpp
file_sys/directory_sdmc.cpp
hle/kernel/address_arbiter.cpp
hle/kernel/archive.cpp
hle/kernel/event.cpp
hle/kernel/kernel.cpp
hle/kernel/mutex.cpp
hle/kernel/semaphore.cpp
hle/kernel/shared_memory.cpp
hle/kernel/thread.cpp
hle/service/ac_u.cpp
hle/service/act_u.cpp
hle/service/am_app.cpp
hle/service/am_net.cpp
hle/service/apt_a.cpp
hle/service/apt_u.cpp
hle/service/boss_u.cpp
hle/service/cfg_i.cpp
hle/service/cfg_u.cpp
hle/service/cecd_u.cpp
hle/service/cfg/cfg.cpp
hle/service/cfg/cfg_i.cpp
hle/service/cfg/cfg_u.cpp
hle/service/csnd_snd.cpp
hle/service/dsp_dsp.cpp
hle/service/err_f.cpp
hle/service/fs_user.cpp
hle/service/frd_u.cpp
hle/service/fs/archive.cpp
hle/service/fs/fs_user.cpp
hle/service/gsp_gpu.cpp
hle/service/hid_user.cpp
hle/service/http_c.cpp
hle/service/ir_rst.cpp
hle/service/ir_u.cpp
hle/service/ldr_ro.cpp
hle/service/mic_u.cpp
hle/service/ndm_u.cpp
hle/service/news_u.cpp
hle/service/nim_aoc.cpp
hle/service/nwm_uds.cpp
hle/service/pm_app.cpp
hle/service/ptm_u.cpp
@ -59,10 +70,10 @@ set(SRCS
hle/svc.cpp
hw/gpu.cpp
hw/hw.cpp
hw/ndma.cpp
loader/elf.cpp
loader/loader.cpp
loader/ncch.cpp
loader/3dsx.cpp
core.cpp
core_timing.cpp
mem_map.cpp
@ -92,39 +103,51 @@ set(HEADERS
arm/skyeye_common/vfp/vfp.h
arm/skyeye_common/vfp/vfp_helper.h
arm/arm_interface.h
file_sys/archive.h
file_sys/archive_backend.h
file_sys/archive_romfs.h
file_sys/archive_savedata.h
file_sys/archive_sdmc.h
file_sys/file.h
file_sys/archive_systemsavedata.h
file_sys/disk_archive.h
file_sys/file_backend.h
file_sys/file_romfs.h
file_sys/file_sdmc.h
file_sys/directory.h
file_sys/directory_backend.h
file_sys/directory_romfs.h
file_sys/directory_sdmc.h
hle/kernel/address_arbiter.h
hle/kernel/archive.h
hle/kernel/event.h
hle/kernel/kernel.h
hle/kernel/mutex.h
hle/kernel/semaphore.h
hle/kernel/session.h
hle/kernel/shared_memory.h
hle/kernel/thread.h
hle/service/ac_u.h
hle/service/act_u.h
hle/service/am_app.h
hle/service/am_net.h
hle/service/apt_a.h
hle/service/apt_u.h
hle/service/boss_u.h
hle/service/cfg_i.h
hle/service/cfg_u.h
hle/service/cecd_u.h
hle/service/cfg/cfg.h
hle/service/cfg/cfg_i.h
hle/service/cfg/cfg_u.h
hle/service/csnd_snd.h
hle/service/dsp_dsp.h
hle/service/err_f.h
hle/service/fs_user.h
hle/service/frd_u.h
hle/service/fs/archive.h
hle/service/fs/fs_user.h
hle/service/gsp_gpu.h
hle/service/hid_user.h
hle/service/http_c.h
hle/service/ir_rst.h
hle/service/ir_u.h
hle/service/ldr_ro.h
hle/service/mic_u.h
hle/service/ndm_u.h
hle/service/news_u.h
hle/service/nim_aoc.h
hle/service/nwm_uds.h
hle/service/pm_app.h
hle/service/ptm_u.h
@ -139,10 +162,10 @@ set(HEADERS
hle/svc.h
hw/gpu.h
hw/hw.h
hw/ndma.h
loader/elf.h
loader/loader.h
loader/ncch.h
loader/3dsx.h
core.h
core_timing.h
mem_map.h

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -77,6 +77,12 @@ public:
*/
virtual u64 GetTicks() const = 0;
/**
* Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time)
* @param ticks Number of ticks to advance the CPU core
*/
virtual void AddTicks(u64 ticks) = 0;
/**
* Saves the current CPU context
* @param ctx Thread context to save

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <string>

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/skyeye_common/armcpu.h"
@ -47,68 +47,38 @@ ARM_DynCom::ARM_DynCom() : ticks(0) {
ARM_DynCom::~ARM_DynCom() {
}
/**
* Set the Program Counter to an address
* @param addr Address to set PC to
*/
void ARM_DynCom::SetPC(u32 pc) {
state->pc = state->Reg[15] = pc;
}
/*
* Get the current Program Counter
* @return Returns current PC
*/
u32 ARM_DynCom::GetPC() const {
return state->Reg[15];
}
/**
* Get an ARM register
* @param index Register index (0-15)
* @return Returns the value in the register
*/
u32 ARM_DynCom::GetReg(int index) const {
return state->Reg[index];
}
/**
* Set an ARM register
* @param index Register index (0-15)
* @param value Value to set register to
*/
void ARM_DynCom::SetReg(int index, u32 value) {
state->Reg[index] = value;
}
/**
* Get the current CPSR register
* @return Returns the value of the CPSR register
*/
u32 ARM_DynCom::GetCPSR() const {
return state->Cpsr;
}
/**
* Set the current CPSR register
* @param cpsr Value to set CPSR to
*/
void ARM_DynCom::SetCPSR(u32 cpsr) {
state->Cpsr = cpsr;
}
/**
* Returns the number of clock ticks since the last reset
* @return Returns number of clock ticks
*/
u64 ARM_DynCom::GetTicks() const {
return ticks;
}
/**
* Executes the given number of instructions
* @param num_instructions Number of instructions to executes
*/
void ARM_DynCom::AddTicks(u64 ticks) {
this->ticks += ticks;
}
void ARM_DynCom::ExecuteInstructions(int num_instructions) {
state->NumInstrsToExecute = num_instructions;
@ -118,11 +88,6 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) {
ticks += InterpreterMainLoop(state.get());
}
/**
* Saves the current CPU context
* @param ctx Thread context to save
* @todo Do we need to save Reg[15] and NextInstr?
*/
void ARM_DynCom::SaveContext(ThreadContext& ctx) {
memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers));
memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers));
@ -139,11 +104,6 @@ void ARM_DynCom::SaveContext(ThreadContext& ctx) {
ctx.mode = state->NextInstr;
}
/**
* Loads a CPU context
* @param ctx Thread context to load
* @param Do we need to load Reg[15] and NextInstr?
*/
void ARM_DynCom::LoadContext(const ThreadContext& ctx) {
memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers));
memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers));
@ -160,7 +120,6 @@ void ARM_DynCom::LoadContext(const ThreadContext& ctx) {
state->NextInstr = ctx.mode;
}
/// Prepare core for thread reschedule (if needed to correctly handle state)
void ARM_DynCom::PrepareReschedule() {
state->NumInstrsToExecute = 0;
}

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -27,14 +27,14 @@ public:
* Get the current Program Counter
* @return Returns current PC
*/
u32 GetPC() const;
u32 GetPC() const override;
/**
* Get an ARM register
* @param index Register index (0-15)
* @return Returns the value in the register
*/
u32 GetReg(int index) const;
u32 GetReg(int index) const override;
/**
* Set an ARM register
@ -47,7 +47,7 @@ public:
* Get the current CPSR register
* @return Returns the value of the CPSR register
*/
u32 GetCPSR() const;
u32 GetCPSR() const override;
/**
* Set the current CPSR register
@ -59,7 +59,13 @@ public:
* Returns the number of clock ticks since the last reset
* @return Returns number of clock ticks
*/
u64 GetTicks() const;
u64 GetTicks() const override;
/**
* Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time)
* @param ticks Number of ticks to advance the CPU core
*/
void AddTicks(u64 ticks) override;
/**
* Saves the current CPU context

View file

@ -1,402 +1,443 @@
/* Copyright (C)
* 2012 - Michael.Kang blackfin.kang@gmail.com
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
/**
* @file arm_dyncom_dec.cpp
* @brief Some common utility for arm decoder
* @author Michael.Kang blackfin.kang@gmail.com
* @version 7849
* @date 2012-03-15
*/
// Copyright 2012 Michael Kang, 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/skyeye_common/arm_regformat.h"
#include "core/arm/skyeye_common/armdefs.h"
#include "core/arm/dyncom/arm_dyncom_dec.h"
const ISEITEM arm_instruction[] = {
#define VFP_DECODE
#include "core/arm/skyeye_common/vfp/vfpinstr.cpp"
#undef VFP_DECODE
{"srs" , 4 , 6 , 25, 31, 0x0000007c, 22, 22, 0x00000001, 16, 20, 0x0000000d, 8, 11, 0x00000005},
{"rfe" , 4 , 6 , 25, 31, 0x0000007c, 22, 22, 0x00000000, 20, 20, 0x00000001, 8, 11, 0x0000000a},
{"bkpt" , 2 , 3 , 20, 31, 0x00000e12, 4, 7, 0x00000007},
{"blx" , 1 , 3 , 25, 31, 0x0000007d},
{"cps" , 3 , 6 , 20, 31, 0x00000f10, 16, 16, 0x00000000, 5, 5, 0x00000000},
{"pld" , 4 , 4 , 26, 31, 0x0000003d, 24, 24, 0x00000001, 20, 22, 0x00000005, 12, 15, 0x0000000f},
{"setend" , 2 , 6 , 16, 31, 0x0000f101, 4, 7, 0x00000000},
{"clrex" , 1 , 6 , 0, 31, 0xf57ff01f},
{"rev16" , 2 , 6 , 16, 27, 0x000006bf, 4, 11, 0x000000fb},
{"usad8" , 3 , 6 , 20, 27, 0x00000078, 12, 15, 0x0000000f, 4, 7, 0x00000001},
{"sxtb" , 2 , 6 , 16, 27, 0x000006af, 4, 7, 0x00000007},
{"uxtb" , 2 , 6 , 16, 27, 0x000006ef, 4, 7, 0x00000007},
{"sxth" , 2 , 6 , 16, 27, 0x000006bf, 4, 7, 0x00000007},
{"sxtb16" , 2 , 6 , 16, 27, 0x0000068f, 4, 7, 0x00000007},
{"uxth" , 2 , 6 , 16, 27, 0x000006ff, 4, 7, 0x00000007},
{"uxtb16" , 2 , 6 , 16, 27, 0x000006cf, 4, 7, 0x00000007},
{"cpy" , 2 , 6 , 20, 27, 0x0000001a, 4, 11, 0x00000000},
{"uxtab" , 2 , 6 , 20, 27, 0x0000006e, 4, 9, 0x00000007},
{"ssub8" , 2 , 6 , 20, 27, 0x00000061, 4, 7, 0x0000000f},
{"shsub8" , 2 , 6 , 20, 27, 0x00000063, 4, 7, 0x0000000f},
{"ssubaddx" , 2 , 6 , 20, 27, 0x00000061, 4, 7, 0x00000005},
{"strex" , 2 , 6 , 20, 27, 0x00000018, 4, 7, 0x00000009},
{"strexb" , 2 , 7 , 20, 27, 0x0000001c, 4, 7, 0x00000009},
{"swp" , 2 , 0 , 20, 27, 0x00000010, 4, 7, 0x00000009},
{"swpb" , 2 , 0 , 20, 27, 0x00000014, 4, 7, 0x00000009},
{"ssub16" , 2 , 6 , 20, 27, 0x00000061, 4, 7, 0x00000007},
{"ssat16" , 2 , 6 , 20, 27, 0x0000006a, 4, 7, 0x00000003},
{"shsubaddx" , 2 , 6 , 20, 27, 0x00000063, 4, 7, 0x00000005},
{"qsubaddx" , 2 , 6 , 20, 27, 0x00000062, 4, 7, 0x00000005},
{"shaddsubx" , 2 , 6 , 20, 27, 0x00000063, 4, 7, 0x00000003},
{"shadd8" , 2 , 6 , 20, 27, 0x00000063, 4, 7, 0x00000009},
{"shadd16" , 2 , 6 , 20, 27, 0x00000063, 4, 7, 0x00000001},
{"sel" , 2 , 6 , 20, 27, 0x00000068, 4, 7, 0x0000000b},
{"saddsubx" , 2 , 6 , 20, 27, 0x00000061, 4, 7, 0x00000003},
{"sadd8" , 2 , 6 , 20, 27, 0x00000061, 4, 7, 0x00000009},
{"sadd16" , 2 , 6 , 20, 27, 0x00000061, 4, 7, 0x00000001},
{"shsub16" , 2 , 6 , 20, 27, 0x00000063, 4, 7, 0x00000007},
{"umaal" , 2 , 6 , 20, 27, 0x00000004, 4, 7, 0x00000009},
{"uxtab16" , 2 , 6 , 20, 27, 0x0000006c, 4, 7, 0x00000007},
{"usubaddx" , 2 , 6 , 20, 27, 0x00000065, 4, 7, 0x00000005},
{"usub8" , 2 , 6 , 20, 27, 0x00000065, 4, 7, 0x0000000f},
{"usub16" , 2 , 6 , 20, 27, 0x00000065, 4, 7, 0x00000007},
{"usat16" , 2 , 6 , 20, 27, 0x0000006e, 4, 7, 0x00000003},
{"usada8" , 2 , 6 , 20, 27, 0x00000078, 4, 7, 0x00000001},
{"uqsubaddx" , 2 , 6 , 20, 27, 0x00000066, 4, 7, 0x00000005},
{"uqsub8" , 2 , 6 , 20, 27, 0x00000066, 4, 7, 0x0000000f},
{"uqsub16" , 2 , 6 , 20, 27, 0x00000066, 4, 7, 0x00000007},
{"uqaddsubx" , 2 , 6 , 20, 27, 0x00000066, 4, 7, 0x00000003},
{"uqadd8" , 2 , 6 , 20, 27, 0x00000066, 4, 7, 0x00000009},
{"uqadd16" , 2 , 6 , 20, 27, 0x00000066, 4, 7, 0x00000001},
{"sxtab" , 2 , 6 , 20, 27, 0x0000006a, 4, 7, 0x00000007},
{"uhsubaddx" , 2 , 6 , 20, 27, 0x00000067, 4, 7, 0x00000005},
{"uhsub8" , 2 , 6 , 20, 27, 0x00000067, 4, 7, 0x0000000f},
{"uhsub16" , 2 , 6 , 20, 27, 0x00000067, 4, 7, 0x00000007},
{"uhaddsubx" , 2 , 6 , 20, 27, 0x00000067, 4, 7, 0x00000003},
{"uhadd8" , 2 , 6 , 20, 27, 0x00000067, 4, 7, 0x00000009},
{"uhadd16" , 2 , 6 , 20, 27, 0x00000067, 4, 7, 0x00000001},
{"uaddsubx" , 2 , 6 , 20, 27, 0x00000065, 4, 7, 0x00000003},
{"uadd8" , 2 , 6 , 20, 27, 0x00000065, 4, 7, 0x00000009},
{"uadd16" , 2 , 6 , 20, 27, 0x00000065, 4, 7, 0x00000001},
{"sxtah" , 2 , 6 , 20, 27, 0x0000006b, 4, 7, 0x00000007},
{"sxtab16" , 2 , 6 , 20, 27, 0x00000068, 4, 7, 0x00000007},
{"qadd8" , 2 , 6 , 20, 27, 0x00000062, 4, 7, 0x00000009},
{"bxj" , 2 , 5 , 20, 27, 0x00000012, 4, 7, 0x00000002},
{"clz" , 2 , 3 , 20, 27, 0x00000016, 4, 7, 0x00000001},
{"uxtah" , 2 , 6 , 20, 27, 0x0000006f, 4, 7, 0x00000007},
{"bx" , 2 , 2 , 20, 27, 0x00000012, 4, 7, 0x00000001},
{"rev" , 2 , 6 , 20, 27, 0x0000006b, 4, 7, 0x00000003},
{"blx" , 2 , 3 , 20, 27, 0x00000012, 4, 7, 0x00000003},
{"revsh" , 2 , 6 , 20, 27, 0x0000006f, 4, 7, 0x0000000b},
{"qadd" , 2 , 4 , 20, 27, 0x00000010, 4, 7, 0x00000005},
{"qadd16" , 2 , 6 , 20, 27, 0x00000062, 4, 7, 0x00000001},
{"qaddsubx" , 2 , 6 , 20, 27, 0x00000062, 4, 7, 0x00000003},
{"ldrex" , 2 , 0 , 20, 27, 0x00000019, 4, 7, 0x00000009},
{"qdadd" , 2 , 4 , 20, 27, 0x00000014, 4, 7, 0x00000005},
{"qdsub" , 2 , 4 , 20, 27, 0x00000016, 4, 7, 0x00000005},
{"qsub" , 2 , 4 , 20, 27, 0x00000012, 4, 7, 0x00000005},
{"ldrexb" , 2 , 7 , 20, 27, 0x0000001d, 4, 7, 0x00000009},
{"qsub8" , 2 , 6 , 20, 27, 0x00000062, 4, 7, 0x0000000f},
{"qsub16" , 2 , 6 , 20, 27, 0x00000062, 4, 7, 0x00000007},
{"smuad" , 4 , 6 , 20, 27, 0x00000070, 12, 15, 0x0000000f, 6, 7, 0x00000000, 4, 4, 0x00000001},
{"smmul" , 4 , 6 , 20, 27, 0x00000075, 12, 15, 0x0000000f, 6, 7, 0x00000000, 4, 4, 0x00000001},
{"smusd" , 4 , 6 , 20, 27, 0x00000070, 12, 15, 0x0000000f, 6, 7, 0x00000001, 4, 4, 0x00000001},
{"smlsd" , 3 , 6 , 20, 27, 0x00000070, 6, 7, 0x00000001, 4, 4, 0x00000001},
{"smlsld" , 3 , 6 , 20, 27, 0x00000074, 6, 7, 0x00000001, 4, 4, 0x00000001},
{"smmla" , 3 , 6 , 20, 27, 0x00000075, 6, 7, 0x00000000, 4, 4, 0x00000001},
{"smmls" , 3 , 6 , 20, 27, 0x00000075, 6, 7, 0x00000003, 4, 4, 0x00000001},
{"smlald" , 3 , 6 , 20, 27, 0x00000074, 6, 7, 0x00000000, 4, 4, 0x00000001},
{"smlad" , 3 , 6 , 20, 27, 0x00000070, 6, 7, 0x00000000, 4, 4, 0x00000001},
{"smlaw" , 3 , 4 , 20, 27, 0x00000012, 7, 7, 0x00000001, 4, 5, 0x00000000},
{"smulw" , 3 , 4 , 20, 27, 0x00000012, 7, 7, 0x00000001, 4, 5, 0x00000002},
{"pkhtb" , 2 , 6 , 20, 27, 0x00000068, 4, 6, 0x00000005},
{"pkhbt" , 2 , 6 , 20, 27, 0x00000068, 4, 6, 0x00000001},
{"smul" , 3 , 4 , 20, 27, 0x00000016, 7, 7, 0x00000001, 4, 4, 0x00000000},
{"smlalxy" , 3 , 4 , 20, 27, 0x00000014, 7, 7, 0x00000001, 4, 4, 0x00000000},
// {"smlal" , 2 , 4 , 21, 27, 0x00000007, 4, 7, 0x00000009},
{"smla" , 3 , 4 , 20, 27, 0x00000010, 7, 7, 0x00000001, 4, 4, 0x00000000},
{"mcrr" , 1 , 6 , 20, 27, 0x000000c4},
{"mrrc" , 1 , 6 , 20, 27, 0x000000c5},
{"cmp" , 2 , 0 , 26, 27, 0x00000000, 20, 24, 0x00000015},
{"tst" , 2 , 0 , 26, 27, 0x00000000, 20, 24, 0x00000011},
{"teq" , 2 , 0 , 26, 27, 0x00000000, 20, 24, 0x00000013},
{"cmn" , 2 , 0 , 26, 27, 0x00000000, 20, 24, 0x00000017},
{"smull" , 2 , 0 , 21, 27, 0x00000006, 4, 7, 0x00000009},
{"umull" , 2 , 0 , 21, 27, 0x00000004, 4, 7, 0x00000009},
{"umlal" , 2 , 0 , 21, 27, 0x00000005, 4, 7, 0x00000009},
{"smlal" , 2 , 0 , 21, 27, 0x00000007, 4, 7, 0x00000009},
{"mul" , 2 , 0 , 21, 27, 0x00000000, 4, 7, 0x00000009},
{"mla" , 2 , 0 , 21, 27, 0x00000001, 4, 7, 0x00000009},
{"ssat" , 2 , 6 , 21, 27, 0x00000035, 4, 5, 0x00000001},
{"usat" , 2 , 6 , 21, 27, 0x00000037, 4, 5, 0x00000001},
{"mrs" , 4 , 0 , 23, 27, 0x00000002, 20, 21, 0x00000000, 16, 19, 0x0000000f, 0, 11, 0x00000000},
{"msr" , 3 , 0 , 23, 27, 0x00000002, 20, 21, 0x00000002, 4, 7, 0x00000000},
{"and" , 2 , 0 , 26, 27, 0x00000000, 21, 24, 0x00000000},
{"bic" , 2 , 0 , 26, 27, 0x00000000, 21, 24, 0x0000000e},
{"ldm" , 3 , 0 , 25, 27, 0x00000004, 20, 22, 0x00000005, 15, 15, 0x00000000},
{"eor" , 2 , 0 , 26, 27, 0x00000000, 21, 24, 0x00000001},
{"add" , 2 , 0 , 26, 27, 0x00000000, 21, 24, 0x00000004},
{"rsb" , 2 , 0 , 26, 27, 0x00000000, 21, 24, 0x00000003},
{"rsc" , 2 , 0 , 26, 27, 0x00000000, 21, 24, 0x00000007},
{"sbc" , 2 , 0 , 26, 27, 0x00000000, 21, 24, 0x00000006},
{"adc" , 2 , 0 , 26, 27, 0x00000000, 21, 24, 0x00000005},
{"sub" , 2 , 0 , 26, 27, 0x00000000, 21, 24, 0x00000002},
{"orr" , 2 , 0 , 26, 27, 0x00000000, 21, 24, 0x0000000c},
{"mvn" , 2 , 0 , 26, 27, 0x00000000, 21, 24, 0x0000000f},
{"mov" , 2 , 0 , 26, 27, 0x00000000, 21, 24, 0x0000000d},
{"stm" , 2 , 0 , 25, 27, 0x00000004, 20, 22, 0x00000004},
{"ldm" , 4 , 0 , 25, 27, 0x00000004, 22, 22, 0x00000001, 20, 20, 0x00000001, 15, 15, 0x00000001},
{"ldrsh" , 3 , 2 , 25, 27, 0x00000000, 20, 20, 0x00000001, 4, 7, 0x0000000f},
{"stm" , 3 , 0 , 25, 27, 0x00000004, 22, 22, 0x00000000, 20, 20, 0x00000000},
{"ldm" , 3 , 0 , 25, 27, 0x00000004, 22, 22, 0x00000000, 20, 20, 0x00000001},
{"ldrsb" , 3 , 2 , 25, 27, 0x00000000, 20, 20, 0x00000001, 4, 7, 0x0000000d},
{"strd" , 3 , 4 , 25, 27, 0x00000000, 20, 20, 0x00000000, 4, 7, 0x0000000f},
{"ldrh" , 3 , 0 , 25, 27, 0x00000000, 20, 20, 0x00000001, 4, 7, 0x0000000b},
{"strh" , 3 , 0 , 25, 27, 0x00000000, 20, 20, 0x00000000, 4, 7, 0x0000000b},
{"ldrd" , 3 , 4 , 25, 27, 0x00000000, 20, 20, 0x00000000, 4, 7, 0x0000000d},
{"strt" , 3 , 0 , 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000002},
{"strbt" , 3 , 0 , 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000006},
{"ldrbt" , 3 , 0 , 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000007},
{"ldrt" , 3 , 0 , 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000003},
{"mrc" , 3 , 6 , 24, 27, 0x0000000e, 20, 20, 0x00000001, 4, 4, 0x00000001},
{"mcr" , 3 , 0 , 24, 27, 0x0000000e, 20, 20, 0x00000000, 4, 4, 0x00000001},
{"msr" , 2 , 0 , 23, 27, 0x00000006, 20, 21, 0x00000002},
{"ldrb" , 3 , 0 , 26, 27, 0x00000001, 22, 22, 0x00000001, 20, 20, 0x00000001},
{"strb" , 3 , 0 , 26, 27, 0x00000001, 22, 22, 0x00000001, 20, 20, 0x00000000},
{"ldr" , 4 , 0 , 28, 31, 0x0000000e, 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000001},
{"ldrcond" , 3 , 0 , 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000001},
{"str" , 3 , 0 , 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000000},
{"cdp" , 2 , 0 , 24, 27, 0x0000000e, 4, 4, 0x00000000},
{"stc" , 2 , 0 , 25, 27, 0x00000006, 20, 20, 0x00000000},
{"ldc" , 2 , 0 , 25, 27, 0x00000006, 20, 20, 0x00000001},
{"swi" , 1 , 0 , 24, 27, 0x0000000f},
{"bbl" , 1 , 0 , 25, 27, 0x00000005},
{ "vmla", 4, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x0, 9, 11, 0x5, 4, 4, 0 },
{ "vmls", 7, ARMVFP2, 28, 31, 0xF, 25, 27, 0x1, 23, 23, 1, 11, 11, 0, 8, 9, 0x2, 6, 6, 1, 4, 4, 0 },
{ "vnmla", 4, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x1, 9, 11, 0x5, 4, 4, 0 },
{ "vnmla", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 },
{ "vnmls", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x1, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 },
{ "vnmul", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 },
{ "vmul", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 },
{ "vadd", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x3, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 },
{ "vsub", 5, ARMVFP2, 23, 27, 0x1C, 20, 21, 0x3, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 },
{ "vdiv", 5, ARMVFP2, 23, 27, 0x1D, 20, 21, 0x0, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 },
{ "vmov(i)", 4, ARMVFP3, 23, 27, 0x1D, 20, 21, 0x3, 9, 11, 0x5, 4, 7, 0 },
{ "vmov(r)", 5, ARMVFP3, 23, 27, 0x1D, 16, 21, 0x30, 9, 11, 0x5, 6, 7, 1, 4, 4, 0 },
{ "vabs", 5, ARMVFP2, 23, 27, 0x1D, 16, 21, 0x30, 9, 11, 0x5, 6, 7, 3, 4, 4, 0 },
{ "vneg", 5, ARMVFP2, 23, 27, 0x1D, 17, 21, 0x18, 9, 11, 0x5, 6, 7, 1, 4, 4, 0 },
{ "vsqrt", 5, ARMVFP2, 23, 27, 0x1D, 16, 21, 0x31, 9, 11, 0x5, 6, 7, 3, 4, 4, 0 },
{ "vcmp", 5, ARMVFP2, 23, 27, 0x1D, 16, 21, 0x34, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 },
{ "vcmp2", 5, ARMVFP2, 23, 27, 0x1D, 16, 21, 0x35, 9, 11, 0x5, 0, 6, 0x40 },
{ "vcvt(bds)", 5, ARMVFP2, 23, 27, 0x1D, 16, 21, 0x37, 9, 11, 0x5, 6, 7, 3, 4, 4, 0 },
{ "vcvt(bff)", 6, ARMVFP3, 23, 27, 0x1D, 19, 21, 0x7, 17, 17, 0x1, 9, 11, 5, 6, 6, 1 },
{ "vcvt(bfi)", 5, ARMVFP2, 23, 27, 0x1D, 19, 21, 0x7, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 },
{ "vmovbrs", 3, ARMVFP2, 21, 27, 0x70, 8, 11, 0xA, 0, 6, 0x10 },
{ "vmsr", 2, ARMVFP2, 20, 27, 0xEE, 0, 11, 0xA10 },
{ "vmovbrc", 4, ARMVFP2, 23, 27, 0x1C, 20, 20, 0x0, 8, 11, 0xB, 0, 4, 0x10 },
{ "vmrs", 2, ARMVFP2, 20, 27, 0xEF, 0, 11, 0xA10 },
{ "vmovbcr", 4, ARMVFP2, 24, 27, 0xE, 20, 20, 1, 8, 11, 0xB, 0, 4, 0x10 },
{ "vmovbrrss", 3, ARMVFP2, 21, 27, 0x62, 8, 11, 0xA, 4, 4, 1 },
{ "vmovbrrd", 3, ARMVFP2, 21, 27, 0x62, 6, 11, 0x2C, 4, 4, 1 },
{ "vstr", 3, ARMVFP2, 24, 27, 0xD, 20, 21, 0, 9, 11, 5 },
{ "vpush", 3, ARMVFP2, 23, 27, 0x1A, 16, 21, 0x2D, 9, 11, 5 },
{ "vstm", 3, ARMVFP2, 25, 27, 0x6, 20, 20, 0, 9, 11, 5 },
{ "vpop", 3, ARMVFP2, 23, 27, 0x19, 16, 21, 0x3D, 9, 11, 5 },
{ "vldr", 3, ARMVFP2, 24, 27, 0xD, 20, 21, 1, 9, 11, 5 },
{ "vldm", 3, ARMVFP2, 25, 27, 0x6, 20, 20, 1, 9, 11, 5 },
{ "srs", 4, 6, 25, 31, 0x0000007c, 22, 22, 0x00000001, 16, 20, 0x0000000d, 8, 11, 0x00000005 },
{ "rfe", 4, 6, 25, 31, 0x0000007c, 22, 22, 0x00000000, 20, 20, 0x00000001, 8, 11, 0x0000000a },
{ "bkpt", 2, 3, 20, 31, 0x00000e12, 4, 7, 0x00000007 },
{ "blx", 1, 3, 25, 31, 0x0000007d },
{ "cps", 3, 6, 20, 31, 0x00000f10, 16, 16, 0x00000000, 5, 5, 0x00000000 },
{ "pld", 4, 4, 26, 31, 0x0000003d, 24, 24, 0x00000001, 20, 22, 0x00000005, 12, 15, 0x0000000f },
{ "setend", 2, 6, 16, 31, 0x0000f101, 4, 7, 0x00000000 },
{ "clrex", 1, 6, 0, 31, 0xf57ff01f },
{ "rev16", 2, 6, 16, 27, 0x000006bf, 4, 11, 0x000000fb },
{ "usad8", 3, 6, 20, 27, 0x00000078, 12, 15, 0x0000000f, 4, 7, 0x00000001 },
{ "sxtb", 2, 6, 16, 27, 0x000006af, 4, 7, 0x00000007 },
{ "uxtb", 2, 6, 16, 27, 0x000006ef, 4, 7, 0x00000007 },
{ "sxth", 2, 6, 16, 27, 0x000006bf, 4, 7, 0x00000007 },
{ "sxtb16", 2, 6, 16, 27, 0x0000068f, 4, 7, 0x00000007 },
{ "uxth", 2, 6, 16, 27, 0x000006ff, 4, 7, 0x00000007 },
{ "uxtb16", 2, 6, 16, 27, 0x000006cf, 4, 7, 0x00000007 },
{ "cpy", 2, 6, 20, 27, 0x0000001a, 4, 11, 0x00000000 },
{ "uxtab", 2, 6, 20, 27, 0x0000006e, 4, 9, 0x00000007 },
{ "ssub8", 2, 6, 20, 27, 0x00000061, 4, 7, 0x0000000f },
{ "shsub8", 2, 6, 20, 27, 0x00000063, 4, 7, 0x0000000f },
{ "ssubaddx", 2, 6, 20, 27, 0x00000061, 4, 7, 0x00000005 },
{ "strex", 2, 6, 20, 27, 0x00000018, 4, 7, 0x00000009 },
{ "strexb", 2, 7, 20, 27, 0x0000001c, 4, 7, 0x00000009 },
{ "swp", 2, 0, 20, 27, 0x00000010, 4, 7, 0x00000009 },
{ "swpb", 2, 0, 20, 27, 0x00000014, 4, 7, 0x00000009 },
{ "ssub16", 2, 6, 20, 27, 0x00000061, 4, 7, 0x00000007 },
{ "ssat16", 2, 6, 20, 27, 0x0000006a, 4, 7, 0x00000003 },
{ "shsubaddx", 2, 6, 20, 27, 0x00000063, 4, 7, 0x00000005 },
{ "qsubaddx", 2, 6, 20, 27, 0x00000062, 4, 7, 0x00000005 },
{ "shaddsubx", 2, 6, 20, 27, 0x00000063, 4, 7, 0x00000003 },
{ "shadd8", 2, 6, 20, 27, 0x00000063, 4, 7, 0x00000009 },
{ "shadd16", 2, 6, 20, 27, 0x00000063, 4, 7, 0x00000001 },
{ "sel", 2, 6, 20, 27, 0x00000068, 4, 7, 0x0000000b },
{ "saddsubx", 2, 6, 20, 27, 0x00000061, 4, 7, 0x00000003 },
{ "sadd8", 2, 6, 20, 27, 0x00000061, 4, 7, 0x00000009 },
{ "sadd16", 2, 6, 20, 27, 0x00000061, 4, 7, 0x00000001 },
{ "shsub16", 2, 6, 20, 27, 0x00000063, 4, 7, 0x00000007 },
{ "umaal", 2, 6, 20, 27, 0x00000004, 4, 7, 0x00000009 },
{ "uxtab16", 2, 6, 20, 27, 0x0000006c, 4, 7, 0x00000007 },
{ "usubaddx", 2, 6, 20, 27, 0x00000065, 4, 7, 0x00000005 },
{ "usub8", 2, 6, 20, 27, 0x00000065, 4, 7, 0x0000000f },
{ "usub16", 2, 6, 20, 27, 0x00000065, 4, 7, 0x00000007 },
{ "usat16", 2, 6, 20, 27, 0x0000006e, 4, 7, 0x00000003 },
{ "usada8", 2, 6, 20, 27, 0x00000078, 4, 7, 0x00000001 },
{ "uqsubaddx", 2, 6, 20, 27, 0x00000066, 4, 7, 0x00000005 },
{ "uqsub8", 2, 6, 20, 27, 0x00000066, 4, 7, 0x0000000f },
{ "uqsub16", 2, 6, 20, 27, 0x00000066, 4, 7, 0x00000007 },
{ "uqaddsubx", 2, 6, 20, 27, 0x00000066, 4, 7, 0x00000003 },
{ "uqadd8", 2, 6, 20, 27, 0x00000066, 4, 7, 0x00000009 },
{ "uqadd16", 2, 6, 20, 27, 0x00000066, 4, 7, 0x00000001 },
{ "sxtab", 2, 6, 20, 27, 0x0000006a, 4, 7, 0x00000007 },
{ "uhsubaddx", 2, 6, 20, 27, 0x00000067, 4, 7, 0x00000005 },
{ "uhsub8", 2, 6, 20, 27, 0x00000067, 4, 7, 0x0000000f },
{ "uhsub16", 2, 6, 20, 27, 0x00000067, 4, 7, 0x00000007 },
{ "uhaddsubx", 2, 6, 20, 27, 0x00000067, 4, 7, 0x00000003 },
{ "uhadd8", 2, 6, 20, 27, 0x00000067, 4, 7, 0x00000009 },
{ "uhadd16", 2, 6, 20, 27, 0x00000067, 4, 7, 0x00000001 },
{ "uaddsubx", 2, 6, 20, 27, 0x00000065, 4, 7, 0x00000003 },
{ "uadd8", 2, 6, 20, 27, 0x00000065, 4, 7, 0x00000009 },
{ "uadd16", 2, 6, 20, 27, 0x00000065, 4, 7, 0x00000001 },
{ "sxtah", 2, 6, 20, 27, 0x0000006b, 4, 7, 0x00000007 },
{ "sxtab16", 2, 6, 20, 27, 0x00000068, 4, 7, 0x00000007 },
{ "qadd8", 2, 6, 20, 27, 0x00000062, 4, 7, 0x00000009 },
{ "bxj", 2, 5, 20, 27, 0x00000012, 4, 7, 0x00000002 },
{ "clz", 2, 3, 20, 27, 0x00000016, 4, 7, 0x00000001 },
{ "uxtah", 2, 6, 20, 27, 0x0000006f, 4, 7, 0x00000007 },
{ "bx", 2, 2, 20, 27, 0x00000012, 4, 7, 0x00000001 },
{ "rev", 2, 6, 20, 27, 0x0000006b, 4, 7, 0x00000003 },
{ "blx", 2, 3, 20, 27, 0x00000012, 4, 7, 0x00000003 },
{ "revsh", 2, 6, 20, 27, 0x0000006f, 4, 7, 0x0000000b },
{ "qadd", 2, 4, 20, 27, 0x00000010, 4, 7, 0x00000005 },
{ "qadd16", 2, 6, 20, 27, 0x00000062, 4, 7, 0x00000001 },
{ "qaddsubx", 2, 6, 20, 27, 0x00000062, 4, 7, 0x00000003 },
{ "ldrex", 2, 0, 20, 27, 0x00000019, 4, 7, 0x00000009 },
{ "qdadd", 2, 4, 20, 27, 0x00000014, 4, 7, 0x00000005 },
{ "qdsub", 2, 4, 20, 27, 0x00000016, 4, 7, 0x00000005 },
{ "qsub", 2, 4, 20, 27, 0x00000012, 4, 7, 0x00000005 },
{ "ldrexb", 2, 7, 20, 27, 0x0000001d, 4, 7, 0x00000009 },
{ "qsub8", 2, 6, 20, 27, 0x00000062, 4, 7, 0x0000000f },
{ "qsub16", 2, 6, 20, 27, 0x00000062, 4, 7, 0x00000007 },
{ "smuad", 4, 6, 20, 27, 0x00000070, 12, 15, 0x0000000f, 6, 7, 0x00000000, 4, 4, 0x00000001 },
{ "smmul", 4, 6, 20, 27, 0x00000075, 12, 15, 0x0000000f, 6, 7, 0x00000000, 4, 4, 0x00000001 },
{ "smusd", 4, 6, 20, 27, 0x00000070, 12, 15, 0x0000000f, 6, 7, 0x00000001, 4, 4, 0x00000001 },
{ "smlsd", 3, 6, 20, 27, 0x00000070, 6, 7, 0x00000001, 4, 4, 0x00000001 },
{ "smlsld", 3, 6, 20, 27, 0x00000074, 6, 7, 0x00000001, 4, 4, 0x00000001 },
{ "smmla", 3, 6, 20, 27, 0x00000075, 6, 7, 0x00000000, 4, 4, 0x00000001 },
{ "smmls", 3, 6, 20, 27, 0x00000075, 6, 7, 0x00000003, 4, 4, 0x00000001 },
{ "smlald", 3, 6, 20, 27, 0x00000074, 6, 7, 0x00000000, 4, 4, 0x00000001 },
{ "smlad", 3, 6, 20, 27, 0x00000070, 6, 7, 0x00000000, 4, 4, 0x00000001 },
{ "smlaw", 3, 4, 20, 27, 0x00000012, 7, 7, 0x00000001, 4, 5, 0x00000000 },
{ "smulw", 3, 4, 20, 27, 0x00000012, 7, 7, 0x00000001, 4, 5, 0x00000002 },
{ "pkhtb", 2, 6, 20, 27, 0x00000068, 4, 6, 0x00000005 },
{ "pkhbt", 2, 6, 20, 27, 0x00000068, 4, 6, 0x00000001 },
{ "smul", 3, 4, 20, 27, 0x00000016, 7, 7, 0x00000001, 4, 4, 0x00000000 },
{ "smlalxy", 3, 4, 20, 27, 0x00000014, 7, 7, 0x00000001, 4, 4, 0x00000000 },
// {"smlal" , 2 , 4 , 21, 27, 0x00000007, 4, 7, 0x00000009},
{ "smla", 3, 4, 20, 27, 0x00000010, 7, 7, 0x00000001, 4, 4, 0x00000000 },
{ "mcrr", 1, 6, 20, 27, 0x000000c4 },
{ "mrrc", 1, 6, 20, 27, 0x000000c5 },
{ "cmp", 2, 0, 26, 27, 0x00000000, 20, 24, 0x00000015 },
{ "tst", 2, 0, 26, 27, 0x00000000, 20, 24, 0x00000011 },
{ "teq", 2, 0, 26, 27, 0x00000000, 20, 24, 0x00000013 },
{ "cmn", 2, 0, 26, 27, 0x00000000, 20, 24, 0x00000017 },
{ "smull", 2, 0, 21, 27, 0x00000006, 4, 7, 0x00000009 },
{ "umull", 2, 0, 21, 27, 0x00000004, 4, 7, 0x00000009 },
{ "umlal", 2, 0, 21, 27, 0x00000005, 4, 7, 0x00000009 },
{ "smlal", 2, 0, 21, 27, 0x00000007, 4, 7, 0x00000009 },
{ "mul", 2, 0, 21, 27, 0x00000000, 4, 7, 0x00000009 },
{ "mla", 2, 0, 21, 27, 0x00000001, 4, 7, 0x00000009 },
{ "ssat", 2, 6, 21, 27, 0x00000035, 4, 5, 0x00000001 },
{ "usat", 2, 6, 21, 27, 0x00000037, 4, 5, 0x00000001 },
{ "mrs", 4, 0, 23, 27, 0x00000002, 20, 21, 0x00000000, 16, 19, 0x0000000f, 0, 11, 0x00000000 },
{ "msr", 3, 0, 23, 27, 0x00000002, 20, 21, 0x00000002, 4, 7, 0x00000000 },
{ "and", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000000 },
{ "bic", 2, 0, 26, 27, 0x00000000, 21, 24, 0x0000000e },
{ "ldm", 3, 0, 25, 27, 0x00000004, 20, 22, 0x00000005, 15, 15, 0x00000000 },
{ "eor", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000001 },
{ "add", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000004 },
{ "rsb", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000003 },
{ "rsc", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000007 },
{ "sbc", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000006 },
{ "adc", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000005 },
{ "sub", 2, 0, 26, 27, 0x00000000, 21, 24, 0x00000002 },
{ "orr", 2, 0, 26, 27, 0x00000000, 21, 24, 0x0000000c },
{ "mvn", 2, 0, 26, 27, 0x00000000, 21, 24, 0x0000000f },
{ "mov", 2, 0, 26, 27, 0x00000000, 21, 24, 0x0000000d },
{ "stm", 2, 0, 25, 27, 0x00000004, 20, 22, 0x00000004 },
{ "ldm", 4, 0, 25, 27, 0x00000004, 22, 22, 0x00000001, 20, 20, 0x00000001, 15, 15, 0x00000001 },
{ "ldrsh", 3, 2, 25, 27, 0x00000000, 20, 20, 0x00000001, 4, 7, 0x0000000f },
{ "stm", 3, 0, 25, 27, 0x00000004, 22, 22, 0x00000000, 20, 20, 0x00000000 },
{ "ldm", 3, 0, 25, 27, 0x00000004, 22, 22, 0x00000000, 20, 20, 0x00000001 },
{ "ldrsb", 3, 2, 25, 27, 0x00000000, 20, 20, 0x00000001, 4, 7, 0x0000000d },
{ "strd", 3, 4, 25, 27, 0x00000000, 20, 20, 0x00000000, 4, 7, 0x0000000f },
{ "ldrh", 3, 0, 25, 27, 0x00000000, 20, 20, 0x00000001, 4, 7, 0x0000000b },
{ "strh", 3, 0, 25, 27, 0x00000000, 20, 20, 0x00000000, 4, 7, 0x0000000b },
{ "ldrd", 3, 4, 25, 27, 0x00000000, 20, 20, 0x00000000, 4, 7, 0x0000000d },
{ "strt", 3, 0, 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000002 },
{ "strbt", 3, 0, 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000006 },
{ "ldrbt", 3, 0, 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000007 },
{ "ldrt", 3, 0, 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000003 },
{ "mrc", 3, 6, 24, 27, 0x0000000e, 20, 20, 0x00000001, 4, 4, 0x00000001 },
{ "mcr", 3, 0, 24, 27, 0x0000000e, 20, 20, 0x00000000, 4, 4, 0x00000001 },
{ "msr", 2, 0, 23, 27, 0x00000006, 20, 21, 0x00000002 },
{ "ldrb", 3, 0, 26, 27, 0x00000001, 22, 22, 0x00000001, 20, 20, 0x00000001 },
{ "strb", 3, 0, 26, 27, 0x00000001, 22, 22, 0x00000001, 20, 20, 0x00000000 },
{ "ldr", 4, 0, 28, 31, 0x0000000e, 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000001 },
{ "ldrcond", 3, 0, 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000001 },
{ "str", 3, 0, 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000000 },
{ "cdp", 2, 0, 24, 27, 0x0000000e, 4, 4, 0x00000000 },
{ "stc", 2, 0, 25, 27, 0x00000006, 20, 20, 0x00000000 },
{ "ldc", 2, 0, 25, 27, 0x00000006, 20, 20, 0x00000001 },
{ "swi", 1, 0, 24, 27, 0x0000000f },
{ "bbl", 1, 0, 25, 27, 0x00000005 },
};
const ISEITEM arm_exclusion_code[] = {
#define VFP_DECODE_EXCLUSION
#include "core/arm/skyeye_common/vfp/vfpinstr.cpp"
#undef VFP_DECODE_EXCLUSION
{"srs" , 0 , 6 , 0},
{"rfe" , 0 , 6 , 0},
{"bkpt" , 0 , 3 , 0},
{"blx" , 0 , 3 , 0},
{"cps" , 0 , 6 , 0},
{"pld" , 0 , 4 , 0},
{"setend" , 0 , 6 , 0},
{"clrex" , 0 , 6 , 0},
{"rev16" , 0 , 6 , 0},
{"usad8" , 0 , 6 , 0},
{"sxtb" , 0 , 6 , 0},
{"uxtb" , 0 , 6 , 0},
{"sxth" , 0 , 6 , 0},
{"sxtb16" , 0 , 6 , 0},
{"uxth" , 0 , 6 , 0},
{"uxtb16" , 0 , 6 , 0},
{"cpy" , 0 , 6 , 0},
{"uxtab" , 0 , 6 , 0},
{"ssub8" , 0 , 6 , 0},
{"shsub8" , 0 , 6 , 0},
{"ssubaddx" , 0 , 6 , 0},
{"strex" , 0 , 6 , 0},
{"strexb" , 0 , 7 , 0},
{"swp" , 0 , 0 , 0},
{"swpb" , 0 , 0 , 0},
{"ssub16" , 0 , 6 , 0},
{"ssat16" , 0 , 6 , 0},
{"shsubaddx" , 0 , 6 , 0},
{"qsubaddx" , 0 , 6 , 0},
{"shaddsubx" , 0 , 6 , 0},
{"shadd8" , 0 , 6 , 0},
{"shadd16" , 0 , 6 , 0},
{"sel" , 0 , 6 , 0},
{"saddsubx" , 0 , 6 , 0},
{"sadd8" , 0 , 6 , 0},
{"sadd16" , 0 , 6 , 0},
{"shsub16" , 0 , 6 , 0},
{"umaal" , 0 , 6 , 0},
{"uxtab16" , 0 , 6 , 0},
{"usubaddx" , 0 , 6 , 0},
{"usub8" , 0 , 6 , 0},
{"usub16" , 0 , 6 , 0},
{"usat16" , 0 , 6 , 0},
{"usada8" , 0 , 6 , 0},
{"uqsubaddx" , 0 , 6 , 0},
{"uqsub8" , 0 , 6 , 0},
{"uqsub16" , 0 , 6 , 0},
{"uqaddsubx" , 0 , 6 , 0},
{"uqadd8" , 0 , 6 , 0},
{"uqadd16" , 0 , 6 , 0},
{"sxtab" , 0 , 6 , 0},
{"uhsubaddx" , 0 , 6 , 0},
{"uhsub8" , 0 , 6 , 0},
{"uhsub16" , 0 , 6 , 0},
{"uhaddsubx" , 0 , 6 , 0},
{"uhadd8" , 0 , 6 , 0},
{"uhadd16" , 0 , 6 , 0},
{"uaddsubx" , 0 , 6 , 0},
{"uadd8" , 0 , 6 , 0},
{"uadd16" , 0 , 6 , 0},
{"sxtah" , 0 , 6 , 0},
{"sxtab16" , 0 , 6 , 0},
{"qadd8" , 0 , 6 , 0},
{"bxj" , 0 , 5 , 0},
{"clz" , 0 , 3 , 0},
{"uxtah" , 0 , 6 , 0},
{"bx" , 0 , 2 , 0},
{"rev" , 0 , 6 , 0},
{"blx" , 0 , 3 , 0},
{"revsh" , 0 , 6 , 0},
{"qadd" , 0 , 4 , 0},
{"qadd16" , 0 , 6 , 0},
{"qaddsubx" , 0 , 6 , 0},
{"ldrex" , 0 , 0 , 0},
{"qdadd" , 0 , 4 , 0},
{"qdsub" , 0 , 4 , 0},
{"qsub" , 0 , 4 , 0},
{"ldrexb" , 0 , 7 , 0},
{"qsub8" , 0 , 6 , 0},
{"qsub16" , 0 , 6 , 0},
{"smuad" , 0 , 6 , 0},
{"smmul" , 0 , 6 , 0},
{"smusd" , 0 , 6 , 0},
{"smlsd" , 0 , 6 , 0},
{"smlsld" , 0 , 6 , 0},
{"smmla" , 0 , 6 , 0},
{"smmls" , 0 , 6 , 0},
{"smlald" , 0 , 6 , 0},
{"smlad" , 0 , 6 , 0},
{"smlaw" , 0 , 4 , 0},
{"smulw" , 0 , 4 , 0},
{"pkhtb" , 0 , 6 , 0},
{"pkhbt" , 0 , 6 , 0},
{"smul" , 0 , 4 , 0},
{"smlal" , 0 , 4 , 0},
{"smla" , 0 , 4 , 0},
{"mcrr" , 0 , 6 , 0},
{"mrrc" , 0 , 6 , 0},
{"cmp" , 3 , 0 , 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000},
{"tst" , 3 , 0 , 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000},
{"teq" , 3 , 0 , 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000},
{"cmn" , 3 , 0 , 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000},
{"smull" , 0 , 0 , 0},
{"umull" , 0 , 0 , 0},
{"umlal" , 0 , 0 , 0},
{"smlal" , 0 , 0 , 0},
{"mul" , 0 , 0 , 0},
{"mla" , 0 , 0 , 0},
{"ssat" , 0 , 6 , 0},
{"usat" , 0 , 6 , 0},
{"mrs" , 0 , 0 , 0},
{"msr" , 0 , 0 , 0},
{"and" , 3 , 0 , 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000},
{"bic" , 3 , 0 , 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000},
{"ldm" , 0 , 0 , 0},
{"eor" , 3 , 0 , 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000},
{"add" , 3 , 0 , 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000},
{"rsb" , 3 , 0 , 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000},
{"rsc" , 3 , 0 , 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000},
{"sbc" , 3 , 0 , 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000},
{"adc" , 3 , 0 , 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000},
{"sub" , 3 , 0 , 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000},
{"orr" , 3 , 0 , 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000},
{"mvn" , 3 , 0 , 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000},
{"mov" , 3 , 0 , 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000},
{"stm" , 0 , 0 , 0},
{"ldm" , 0 , 0 , 0},
{"ldrsh" , 0 , 2 , 0},
{"stm" , 0 , 0 , 0},
{"ldm" , 0 , 0 , 0},
{"ldrsb" , 0 , 2 , 0},
{"strd" , 0 , 4 , 0},
{"ldrh" , 0 , 0 , 0},
{"strh" , 0 , 0 , 0},
{"ldrd" , 0 , 4 , 0},
{"strt" , 0 , 0 , 0},
{"strbt" , 0 , 0 , 0},
{"ldrbt" , 0 , 0 , 0},
{"ldrt" , 0 , 0 , 0},
{"mrc" , 0 , 6 , 0},
{"mcr" , 0 , 0 , 0},
{"msr" , 0 , 0 , 0},
{"ldrb" , 0 , 0 , 0},
{"strb" , 0 , 0 , 0},
{"ldr" , 0 , 0 , 0},
{"ldrcond" , 1 , 0 , 28, 31, 0x0000000e},
{"str" , 0 , 0 , 0},
{"cdp" , 0 , 0 , 0},
{"stc" , 0 , 0 , 0},
{"ldc" , 0 , 0 , 0},
{"swi" , 0 , 0 , 0},
{"bbl" , 0 , 0 , 0},
{"bl_1_thumb", 0, INVALID, 0},/* should be table[-4] */
{"bl_2_thumb", 0, INVALID, 0}, /* should be located at the end of the table[-3] */
{"blx_1_thumb", 0, INVALID, 0}, /* should be located at table[-2] */
{"invalid", 0, INVALID, 0}
{ "vmla", 0, ARMVFP2, 0 },
{ "vmls", 0, ARMVFP2, 0 },
{ "vnmla", 0, ARMVFP2, 0 },
{ "vnmla", 0, ARMVFP2, 0 },
{ "vnmls", 0, ARMVFP2, 0 },
{ "vnmul", 0, ARMVFP2, 0 },
{ "vmul", 0, ARMVFP2, 0 },
{ "vadd", 0, ARMVFP2, 0 },
{ "vsub", 0, ARMVFP2, 0 },
{ "vdiv", 0, ARMVFP2, 0 },
{ "vmov(i)", 0, ARMVFP3, 0 },
{ "vmov(r)", 0, ARMVFP3, 0 },
{ "vabs", 0, ARMVFP2, 0 },
{ "vneg", 0, ARMVFP2, 0 },
{ "vsqrt", 0, ARMVFP2, 0 },
{ "vcmp", 0, ARMVFP2, 0 },
{ "vcmp2", 0, ARMVFP2, 0 },
{ "vcvt(bff)", 0, ARMVFP3, 4, 4, 1 },
{ "vcvt(bds)", 0, ARMVFP2, 0 },
{ "vcvt(bfi)", 0, ARMVFP2, 0 },
{ "vmovbrs", 0, ARMVFP2, 0 },
{ "vmsr", 0, ARMVFP2, 0 },
{ "vmovbrc", 0, ARMVFP2, 0 },
{ "vmrs", 0, ARMVFP2, 0 },
{ "vmovbcr", 0, ARMVFP2, 0 },
{ "vmovbrrss", 0, ARMVFP2, 0 },
{ "vmovbrrd", 0, ARMVFP2, 0 },
{ "vstr", 0, ARMVFP2, 0 },
{ "vpush", 0, ARMVFP2, 0 },
{ "vstm", 0, ARMVFP2, 0 },
{ "vpop", 0, ARMVFP2, 0 },
{ "vldr", 0, ARMVFP2, 0 },
{ "vldm", 0, ARMVFP2, 0 },
{ "srs", 0, 6, 0 },
{ "rfe", 0, 6, 0 },
{ "bkpt", 0, 3, 0 },
{ "blx", 0, 3, 0 },
{ "cps", 0, 6, 0 },
{ "pld", 0, 4, 0 },
{ "setend", 0, 6, 0 },
{ "clrex", 0, 6, 0 },
{ "rev16", 0, 6, 0 },
{ "usad8", 0, 6, 0 },
{ "sxtb", 0, 6, 0 },
{ "uxtb", 0, 6, 0 },
{ "sxth", 0, 6, 0 },
{ "sxtb16", 0, 6, 0 },
{ "uxth", 0, 6, 0 },
{ "uxtb16", 0, 6, 0 },
{ "cpy", 0, 6, 0 },
{ "uxtab", 0, 6, 0 },
{ "ssub8", 0, 6, 0 },
{ "shsub8", 0, 6, 0 },
{ "ssubaddx", 0, 6, 0 },
{ "strex", 0, 6, 0 },
{ "strexb", 0, 7, 0 },
{ "swp", 0, 0, 0 },
{ "swpb", 0, 0, 0 },
{ "ssub16", 0, 6, 0 },
{ "ssat16", 0, 6, 0 },
{ "shsubaddx", 0, 6, 0 },
{ "qsubaddx", 0, 6, 0 },
{ "shaddsubx", 0, 6, 0 },
{ "shadd8", 0, 6, 0 },
{ "shadd16", 0, 6, 0 },
{ "sel", 0, 6, 0 },
{ "saddsubx", 0, 6, 0 },
{ "sadd8", 0, 6, 0 },
{ "sadd16", 0, 6, 0 },
{ "shsub16", 0, 6, 0 },
{ "umaal", 0, 6, 0 },
{ "uxtab16", 0, 6, 0 },
{ "usubaddx", 0, 6, 0 },
{ "usub8", 0, 6, 0 },
{ "usub16", 0, 6, 0 },
{ "usat16", 0, 6, 0 },
{ "usada8", 0, 6, 0 },
{ "uqsubaddx", 0, 6, 0 },
{ "uqsub8", 0, 6, 0 },
{ "uqsub16", 0, 6, 0 },
{ "uqaddsubx", 0, 6, 0 },
{ "uqadd8", 0, 6, 0 },
{ "uqadd16", 0, 6, 0 },
{ "sxtab", 0, 6, 0 },
{ "uhsubaddx", 0, 6, 0 },
{ "uhsub8", 0, 6, 0 },
{ "uhsub16", 0, 6, 0 },
{ "uhaddsubx", 0, 6, 0 },
{ "uhadd8", 0, 6, 0 },
{ "uhadd16", 0, 6, 0 },
{ "uaddsubx", 0, 6, 0 },
{ "uadd8", 0, 6, 0 },
{ "uadd16", 0, 6, 0 },
{ "sxtah", 0, 6, 0 },
{ "sxtab16", 0, 6, 0 },
{ "qadd8", 0, 6, 0 },
{ "bxj", 0, 5, 0 },
{ "clz", 0, 3, 0 },
{ "uxtah", 0, 6, 0 },
{ "bx", 0, 2, 0 },
{ "rev", 0, 6, 0 },
{ "blx", 0, 3, 0 },
{ "revsh", 0, 6, 0 },
{ "qadd", 0, 4, 0 },
{ "qadd16", 0, 6, 0 },
{ "qaddsubx", 0, 6, 0 },
{ "ldrex", 0, 0, 0 },
{ "qdadd", 0, 4, 0 },
{ "qdsub", 0, 4, 0 },
{ "qsub", 0, 4, 0 },
{ "ldrexb", 0, 7, 0 },
{ "qsub8", 0, 6, 0 },
{ "qsub16", 0, 6, 0 },
{ "smuad", 0, 6, 0 },
{ "smmul", 0, 6, 0 },
{ "smusd", 0, 6, 0 },
{ "smlsd", 0, 6, 0 },
{ "smlsld", 0, 6, 0 },
{ "smmla", 0, 6, 0 },
{ "smmls", 0, 6, 0 },
{ "smlald", 0, 6, 0 },
{ "smlad", 0, 6, 0 },
{ "smlaw", 0, 4, 0 },
{ "smulw", 0, 4, 0 },
{ "pkhtb", 0, 6, 0 },
{ "pkhbt", 0, 6, 0 },
{ "smul", 0, 4, 0 },
{ "smlal", 0, 4, 0 },
{ "smla", 0, 4, 0 },
{ "mcrr", 0, 6, 0 },
{ "mrrc", 0, 6, 0 },
{ "cmp", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
{ "tst", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
{ "teq", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
{ "cmn", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
{ "smull", 0, 0, 0 },
{ "umull", 0, 0, 0 },
{ "umlal", 0, 0, 0 },
{ "smlal", 0, 0, 0 },
{ "mul", 0, 0, 0 },
{ "mla", 0, 0, 0 },
{ "ssat", 0, 6, 0 },
{ "usat", 0, 6, 0 },
{ "mrs", 0, 0, 0 },
{ "msr", 0, 0, 0 },
{ "and", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
{ "bic", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
{ "ldm", 0, 0, 0 },
{ "eor", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
{ "add", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
{ "rsb", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
{ "rsc", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
{ "sbc", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
{ "adc", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
{ "sub", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
{ "orr", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
{ "mvn", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
{ "mov", 3, 0, 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 },
{ "stm", 0, 0, 0 },
{ "ldm", 0, 0, 0 },
{ "ldrsh", 0, 2, 0 },
{ "stm", 0, 0, 0 },
{ "ldm", 0, 0, 0 },
{ "ldrsb", 0, 2, 0 },
{ "strd", 0, 4, 0 },
{ "ldrh", 0, 0, 0 },
{ "strh", 0, 0, 0 },
{ "ldrd", 0, 4, 0 },
{ "strt", 0, 0, 0 },
{ "strbt", 0, 0, 0 },
{ "ldrbt", 0, 0, 0 },
{ "ldrt", 0, 0, 0 },
{ "mrc", 0, 6, 0 },
{ "mcr", 0, 0, 0 },
{ "msr", 0, 0, 0 },
{ "ldrb", 0, 0, 0 },
{ "strb", 0, 0, 0 },
{ "ldr", 0, 0, 0 },
{ "ldrcond", 1, 0, 28, 31, 0x0000000e },
{ "str", 0, 0, 0 },
{ "cdp", 0, 0, 0 },
{ "stc", 0, 0, 0 },
{ "ldc", 0, 0, 0 },
{ "swi", 0, 0, 0 },
{ "bbl", 0, 0, 0 },
{ "bl_1_thumb", 0, INVALID, 0 }, // Should be table[-4]
{ "bl_2_thumb", 0, INVALID, 0 }, // Should be located at the end of the table[-3]
{ "blx_1_thumb", 0, INVALID, 0 }, // Should be located at table[-2]
{ "invalid", 0, INVALID, 0 }
};
int decode_arm_instr(uint32_t instr, int32_t *idx)
{
int n = 0;
int base = 0;
int ret = DECODE_FAILURE;
int i = 0;
int instr_slots = sizeof(arm_instruction)/sizeof(ISEITEM);
for (i = 0; i < instr_slots; i++)
{
// ret = DECODE_SUCCESS;
n = arm_instruction[i].attribute_value;
base = 0;
while (n) {
if (arm_instruction[i].content[base + 1] == 31 && arm_instruction[i].content[base] == 0) {
/* clrex */
if (instr != arm_instruction[i].content[base + 2]) {
break;
}
} else if (BITS(arm_instruction[i].content[base], arm_instruction[i].content[base + 1]) != arm_instruction[i].content[base + 2]) {
break;
}
base += 3;
n --;
}
//All conditions is satisfied.
if (n == 0)
ret = DECODE_SUCCESS;
int decode_arm_instr(uint32_t instr, int32_t *idx) {
int n = 0;
int base = 0;
int ret = DECODE_FAILURE;
int i = 0;
int instr_slots = sizeof(arm_instruction) / sizeof(ISEITEM);
for (i = 0; i < instr_slots; i++) {
n = arm_instruction[i].attribute_value;
base = 0;
if (ret == DECODE_SUCCESS) {
n = arm_exclusion_code[i].attribute_value;
if (n != 0) {
base = 0;
while (n) {
if (BITS(arm_exclusion_code[i].content[base], arm_exclusion_code[i].content[base + 1]) != arm_exclusion_code[i].content[base + 2]) {
break; }
base += 3;
n --;
}
//All conditions is satisfied.
if (n == 0)
ret = DECODE_FAILURE;
}
}
while (n) {
if (arm_instruction[i].content[base + 1] == 31 && arm_instruction[i].content[base] == 0) {
// clrex
if (instr != arm_instruction[i].content[base + 2]) {
break;
}
} else if (BITS(arm_instruction[i].content[base], arm_instruction[i].content[base + 1]) != arm_instruction[i].content[base + 2]) {
break;
}
base += 3;
n--;
}
if (ret == DECODE_SUCCESS) {
*idx = i;
return ret;
}
}
return ret;
// All conditions is satisfied.
if (n == 0)
ret = DECODE_SUCCESS;
if (ret == DECODE_SUCCESS) {
n = arm_exclusion_code[i].attribute_value;
if (n != 0) {
base = 0;
while (n) {
if (BITS(arm_exclusion_code[i].content[base], arm_exclusion_code[i].content[base + 1]) != arm_exclusion_code[i].content[base + 2]) {
break;
}
base += 3;
n--;
}
// All conditions is satisfied.
if (n == 0)
ret = DECODE_FAILURE;
}
}
if (ret == DECODE_SUCCESS) {
*idx = i;
return ret;
}
}
return ret;
}

View file

@ -56,8 +56,6 @@
#define RN ((instr >> 16) & 0xF)
/*xxxx xxxx xxxx xxxx xxxx xxxx xxxx 1111 */
#define RM (instr & 0xF)
#define BIT(n) ((instr >> (n)) & 1)
#define BITS(a,b) ((instr >> (a)) & ((1 << (1+(b)-(a)))-1))
/* CP15 registers */
#define OPCODE_1 BITS(21, 23)

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once

View file

@ -1,42 +1,15 @@
/* Copyright (C)
* 2011 - Michael.Kang blackfin.kang@gmail.com
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
/**
* @file arm_dyncom_run.cpp
* @brief The dyncom run implementation for arm
* @author Michael.Kang blackfin.kang@gmail.com
* @version 78.77
* @date 2011-11-20
*/
// Copyright 2012 Michael Kang, 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <assert.h>
#include "core/arm/skyeye_common/armdefs.h"
void switch_mode(arm_core_t *core, uint32_t mode)
{
uint32_t tmp1, tmp2;
if (core->Mode == mode) {
//Mode not changed.
//printf("mode not changed\n");
void switch_mode(arm_core_t *core, uint32_t mode) {
if (core->Mode == mode)
return;
}
//printf("%d --->>> %d\n", core->Mode, mode);
//printf("In %s, Cpsr=0x%x, R15=0x%x, last_pc=0x%x, cpsr=0x%x, spsr_copy=0x%x, icounter=%lld\n", __FUNCTION__, core->Cpsr, core->Reg[15], core->last_pc, core->Cpsr, core->Spsr_copy, core->icounter);
if (mode != USERBANK) {
switch (core->Mode) {
case USER32MODE:
@ -110,11 +83,8 @@ void switch_mode(arm_core_t *core, uint32_t mode)
}
core->Mode = mode;
//printf("In %si end, Cpsr=0x%x, R15=0x%x, last_pc=0x%x, cpsr=0x%x, spsr_copy=0x%x, icounter=%lld\n", __FUNCTION__, core->Cpsr, core->Reg[15], core->last_pc, core->Cpsr, core->Spsr_copy, core->icounter);
//printf("\n--------------------------------------\n");
}
else {
printf("user mode\n");
} else {
LOG_CRITICAL(Core_ARM11, "user mode");
exit(-2);
}
}

View file

@ -1,35 +1,13 @@
/* Copyright (C)
* 2011 - Michael.Kang blackfin.kang@gmail.com
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
/**
* @file arm_dyncom_thumb.c
* @brief The thumb dynamic interpreter
* @author Michael.Kang blackfin.kang@gmail.com
* @version 78.77
* @date 2011-11-07
*/
// Copyright 2012 Michael Kang, 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
/* We can provide simple Thumb simulation by decoding the Thumb
instruction into its corresponding ARM instruction, and using the
existing ARM simulator. */
// We can provide simple Thumb simulation by decoding the Thumb instruction into its corresponding
// ARM instruction, and using the existing ARM simulator.
#include "core/arm/skyeye_common/skyeye_defs.h"
#ifndef MODET /* required for the Thumb instruction support */
#ifndef MODET // Required for the Thumb instruction support
#if 1
#error "MODET needs to be defined for the Thumb world to work"
#else
@ -40,482 +18,359 @@ existing ARM simulator. */
#include "core/arm/skyeye_common/armos.h"
#include "core/arm/dyncom/arm_dyncom_thumb.h"
/* Decode a 16bit Thumb instruction. The instruction is in the low
16-bits of the tinstr field, with the following Thumb instruction
held in the high 16-bits. Passing in two Thumb instructions allows
easier simulation of the special dual BL instruction. */
// Decode a 16bit Thumb instruction. The instruction is in the low 16-bits of the tinstr field,
// with the following Thumb instruction held in the high 16-bits. Passing in two Thumb instructions
// allows easier simulation of the special dual BL instruction.
tdstate thumb_translate (addr_t addr, uint32_t instr, uint32_t* ainstr, uint32_t* inst_size)
{
tdstate thumb_translate (addr_t addr, uint32_t instr, uint32_t* ainstr, uint32_t* inst_size) {
tdstate valid = t_uninitialized;
ARMword next_instr;
ARMword tinstr;
tinstr = instr;
/* The endian should be judge here */
#if 0
if (state->bigendSig) {
next_instr = tinstr & 0xFFFF;
tinstr >>= 16;
}
else {
next_instr = tinstr >> 16;
tinstr &= 0xFFFF;
}
#endif
if((addr & 0x3) != 0)
tinstr = instr >> 16;
else
tinstr &= 0xFFFF;
ARMword tinstr;
tinstr = instr;
//printf("In %s, instr=0x%x, tinstr=0x%x, r15=0x%x\n", __FUNCTION__, instr, tinstr, cpu->translate_pc);
#if 1 /* debugging to catch non updates */
*ainstr = 0xDEADC0DE;
#endif
// The endian should be judge here
if((addr & 0x3) != 0)
tinstr = instr >> 16;
else
tinstr &= 0xFFFF;
switch ((tinstr & 0xF800) >> 11) {
case 0: /* LSL */
case 1: /* LSR */
case 2: /* ASR */
/* Format 1 */
*ainstr = 0xE1B00000 /* base opcode */
| ((tinstr & 0x1800) >> (11 - 5)) /* shift type */
|((tinstr & 0x07C0) << (7 - 6)) /* imm5 */
|((tinstr & 0x0038) >> 3) /* Rs */
|((tinstr & 0x0007) << 12); /* Rd */
break;
case 3: /* ADD/SUB */
/* Format 2 */
{
ARMword subset[4] = {
0xE0900000, /* ADDS Rd,Rs,Rn */
0xE0500000, /* SUBS Rd,Rs,Rn */
0xE2900000, /* ADDS Rd,Rs,#imm3 */
0xE2500000 /* SUBS Rd,Rs,#imm3 */
};
/* It is quicker indexing into a table, than performing switch
or conditionals: */
*ainstr = subset[(tinstr & 0x0600) >> 9] /* base opcode */
|((tinstr & 0x01C0) >> 6) /* Rn or imm3 */
|((tinstr & 0x0038) << (16 - 3)) /* Rs */
|((tinstr & 0x0007) << (12 - 0)); /* Rd */
}
break;
case 4: /* MOV */
case 5: /* CMP */
case 6: /* ADD */
case 7: /* SUB */
/* Format 3 */
{
ARMword subset[4] = {
0xE3B00000, /* MOVS Rd,#imm8 */
0xE3500000, /* CMP Rd,#imm8 */
0xE2900000, /* ADDS Rd,Rd,#imm8 */
0xE2500000, /* SUBS Rd,Rd,#imm8 */
};
*ainstr = subset[(tinstr & 0x1800) >> 11] /* base opcode */
|((tinstr & 0x00FF) >> 0) /* imm8 */
|((tinstr & 0x0700) << (16 - 8)) /* Rn */
|((tinstr & 0x0700) << (12 - 8)); /* Rd */
}
break;
case 8: /* Arithmetic and high register transfers */
/* TODO: Since the subsets for both Format 4 and Format 5
instructions are made up of different ARM encodings, we could
save the following conditional, and just have one large
subset. */
if ((tinstr & (1 << 10)) == 0) {
typedef enum
{ t_norm, t_shift, t_neg, t_mul }otype_t;
*ainstr = 0xDEADC0DE; // Debugging to catch non updates
/* Format 4 */
struct
{
ARMword opcode;
otype_t otype;
}
subset[16] = {
{
0xE0100000, t_norm}, /* ANDS Rd,Rd,Rs */
{
0xE0300000, t_norm}, /* EORS Rd,Rd,Rs */
{
0xE1B00010, t_shift}, /* MOVS Rd,Rd,LSL Rs */
{
0xE1B00030, t_shift}, /* MOVS Rd,Rd,LSR Rs */
{
0xE1B00050, t_shift}, /* MOVS Rd,Rd,ASR Rs */
{
0xE0B00000, t_norm}, /* ADCS Rd,Rd,Rs */
{
0xE0D00000, t_norm}, /* SBCS Rd,Rd,Rs */
{
0xE1B00070, t_shift}, /* MOVS Rd,Rd,ROR Rs */
{
0xE1100000, t_norm}, /* TST Rd,Rs */
{
0xE2700000, t_neg}, /* RSBS Rd,Rs,#0 */
{
0xE1500000, t_norm}, /* CMP Rd,Rs */
{
0xE1700000, t_norm}, /* CMN Rd,Rs */
{
0xE1900000, t_norm}, /* ORRS Rd,Rd,Rs */
{
0xE0100090, t_mul}, /* MULS Rd,Rd,Rs */
{
0xE1D00000, t_norm}, /* BICS Rd,Rd,Rs */
{
0xE1F00000, t_norm} /* MVNS Rd,Rs */
};
*ainstr = subset[(tinstr & 0x03C0) >> 6].opcode; /* base */
switch (subset[(tinstr & 0x03C0) >> 6].otype) {
case t_norm:
*ainstr |= ((tinstr & 0x0007) << 16) /* Rn */
|((tinstr & 0x0007) << 12) /* Rd */
|((tinstr & 0x0038) >> 3); /* Rs */
break;
case t_shift:
*ainstr |= ((tinstr & 0x0007) << 12) /* Rd */
|((tinstr & 0x0007) >> 0) /* Rm */
|((tinstr & 0x0038) << (8 - 3)); /* Rs */
break;
case t_neg:
*ainstr |= ((tinstr & 0x0007) << 12) /* Rd */
|((tinstr & 0x0038) << (16 - 3)); /* Rn */
break;
case t_mul:
*ainstr |= ((tinstr & 0x0007) << 16) /* Rd */
|((tinstr & 0x0007) << 8) /* Rs */
|((tinstr & 0x0038) >> 3); /* Rm */
break;
}
}
else {
/* Format 5 */
ARMword Rd = ((tinstr & 0x0007) >> 0);
ARMword Rs = ((tinstr & 0x0038) >> 3);
if (tinstr & (1 << 7))
Rd += 8;
if (tinstr & (1 << 6))
Rs += 8;
switch ((tinstr & 0x03C0) >> 6) {
case 0x1: /* ADD Rd,Rd,Hs */
case 0x2: /* ADD Hd,Hd,Rs */
case 0x3: /* ADD Hd,Hd,Hs */
*ainstr = 0xE0800000 /* base */
| (Rd << 16) /* Rn */
|(Rd << 12) /* Rd */
|(Rs << 0); /* Rm */
break;
case 0x5: /* CMP Rd,Hs */
case 0x6: /* CMP Hd,Rs */
case 0x7: /* CMP Hd,Hs */
*ainstr = 0xE1500000 /* base */
| (Rd << 16) /* Rn */
|(Rd << 12) /* Rd */
|(Rs << 0); /* Rm */
break;
case 0x9: /* MOV Rd,Hs */
case 0xA: /* MOV Hd,Rs */
case 0xB: /* MOV Hd,Hs */
*ainstr = 0xE1A00000 /* base */
| (Rd << 16) /* Rn */
|(Rd << 12) /* Rd */
|(Rs << 0); /* Rm */
break;
case 0xC: /* BX Rs */
case 0xD: /* BX Hs */
*ainstr = 0xE12FFF10 /* base */
| ((tinstr & 0x0078) >> 3); /* Rd */
break;
case 0x0: /* UNDEFINED */
case 0x4: /* UNDEFINED */
case 0x8: /* UNDEFINED */
valid = t_undefined;
break;
case 0xE: /* BLX */
case 0xF: /* BLX */
//if (state->is_v5) {
if(1){
//valid = t_branch;
#if 1
*ainstr = 0xE1200030 /* base */
|(Rs << 0); /* Rm */
#endif
} else {
valid = t_undefined;
}
break;
}
}
break;
case 9: /* LDR Rd,[PC,#imm8] */
/* Format 6 */
*ainstr = 0xE59F0000 /* base */
| ((tinstr & 0x0700) << (12 - 8)) /* Rd */
|((tinstr & 0x00FF) << (2 - 0)); /* off8 */
break;
case 10:
case 11:
/* TODO: Format 7 and Format 8 perform the same ARM encoding, so
the following could be merged into a single subset, saving on
the following boolean: */
if ((tinstr & (1 << 9)) == 0) {
/* Format 7 */
ARMword subset[4] = {
0xE7800000, /* STR Rd,[Rb,Ro] */
0xE7C00000, /* STRB Rd,[Rb,Ro] */
0xE7900000, /* LDR Rd,[Rb,Ro] */
0xE7D00000 /* LDRB Rd,[Rb,Ro] */
};
*ainstr = subset[(tinstr & 0x0C00) >> 10] /* base */
|((tinstr & 0x0007) << (12 - 0)) /* Rd */
|((tinstr & 0x0038) << (16 - 3)) /* Rb */
|((tinstr & 0x01C0) >> 6); /* Ro */
}
else {
/* Format 8 */
ARMword subset[4] = {
0xE18000B0, /* STRH Rd,[Rb,Ro] */
0xE19000D0, /* LDRSB Rd,[Rb,Ro] */
0xE19000B0, /* LDRH Rd,[Rb,Ro] */
0xE19000F0 /* LDRSH Rd,[Rb,Ro] */
};
*ainstr = subset[(tinstr & 0x0C00) >> 10] /* base */
|((tinstr & 0x0007) << (12 - 0)) /* Rd */
|((tinstr & 0x0038) << (16 - 3)) /* Rb */
|((tinstr & 0x01C0) >> 6); /* Ro */
}
break;
case 12: /* STR Rd,[Rb,#imm5] */
case 13: /* LDR Rd,[Rb,#imm5] */
case 14: /* STRB Rd,[Rb,#imm5] */
case 15: /* LDRB Rd,[Rb,#imm5] */
/* Format 9 */
{
ARMword subset[4] = {
0xE5800000, /* STR Rd,[Rb,#imm5] */
0xE5900000, /* LDR Rd,[Rb,#imm5] */
0xE5C00000, /* STRB Rd,[Rb,#imm5] */
0xE5D00000 /* LDRB Rd,[Rb,#imm5] */
};
/* The offset range defends on whether we are transferring a
byte or word value: */
*ainstr = subset[(tinstr & 0x1800) >> 11] /* base */
|((tinstr & 0x0007) << (12 - 0)) /* Rd */
|((tinstr & 0x0038) << (16 - 3)) /* Rb */
|((tinstr & 0x07C0) >> (6 - ((tinstr & (1 << 12)) ? 0 : 2))); /* off5 */
}
break;
case 16: /* STRH Rd,[Rb,#imm5] */
case 17: /* LDRH Rd,[Rb,#imm5] */
/* Format 10 */
*ainstr = ((tinstr & (1 << 11)) /* base */
? 0xE1D000B0 /* LDRH */
: 0xE1C000B0) /* STRH */
|((tinstr & 0x0007) << (12 - 0)) /* Rd */
|((tinstr & 0x0038) << (16 - 3)) /* Rb */
|((tinstr & 0x01C0) >> (6 - 1)) /* off5, low nibble */
|((tinstr & 0x0600) >> (9 - 8)); /* off5, high nibble */
break;
case 18: /* STR Rd,[SP,#imm8] */
case 19: /* LDR Rd,[SP,#imm8] */
/* Format 11 */
*ainstr = ((tinstr & (1 << 11)) /* base */
? 0xE59D0000 /* LDR */
: 0xE58D0000) /* STR */
|((tinstr & 0x0700) << (12 - 8)) /* Rd */
|((tinstr & 0x00FF) << 2); /* off8 */
break;
case 20: /* ADD Rd,PC,#imm8 */
case 21: /* ADD Rd,SP,#imm8 */
/* Format 12 */
if ((tinstr & (1 << 11)) == 0) {
/* NOTE: The PC value used here should by word aligned */
/* We encode shift-left-by-2 in the rotate immediate field,
so no shift of off8 is needed. */
*ainstr = 0xE28F0F00 /* base */
| ((tinstr & 0x0700) << (12 - 8)) /* Rd */
|(tinstr & 0x00FF); /* off8 */
}
else {
/* We encode shift-left-by-2 in the rotate immediate field,
so no shift of off8 is needed. */
*ainstr = 0xE28D0F00 /* base */
| ((tinstr & 0x0700) << (12 - 8)) /* Rd */
|(tinstr & 0x00FF); /* off8 */
}
break;
case 22:
case 23:
if ((tinstr & 0x0F00) == 0x0000) {
/* Format 13 */
/* NOTE: The instruction contains a shift left of 2
equivalent (implemented as ROR #30): */
*ainstr = ((tinstr & (1 << 7)) /* base */
? 0xE24DDF00 /* SUB */
: 0xE28DDF00) /* ADD */
|(tinstr & 0x007F); /* off7 */
}
else if ((tinstr & 0x0F00) == 0x0e00)
*ainstr = 0xEF000000 | SWI_Breakpoint;
else {
/* Format 14 */
ARMword subset[4] = {
0xE92D0000, /* STMDB sp!,{rlist} */
0xE92D4000, /* STMDB sp!,{rlist,lr} */
0xE8BD0000, /* LDMIA sp!,{rlist} */
0xE8BD8000 /* LDMIA sp!,{rlist,pc} */
};
*ainstr = subset[((tinstr & (1 << 11)) >> 10) | ((tinstr & (1 << 8)) >> 8)] /* base */
|(tinstr & 0x00FF); /* mask8 */
}
break;
case 24: /* STMIA */
case 25: /* LDMIA */
/* Format 15 */
*ainstr = ((tinstr & (1 << 11)) /* base */
? 0xE8B00000 /* LDMIA */
: 0xE8A00000) /* STMIA */
|((tinstr & 0x0700) << (16 - 8)) /* Rb */
|(tinstr & 0x00FF); /* mask8 */
break;
case 26: /* Bcc */
case 27: /* Bcc/SWI */
if ((tinstr & 0x0F00) == 0x0F00) {
#if 0
if (tinstr == (ARMul_ABORTWORD & 0xffff) &&
state->AbortAddr == pc) {
*ainstr = ARMul_ABORTWORD;
break;
}
#endif
/* Format 17 : SWI */
*ainstr = 0xEF000000;
/* Breakpoint must be handled specially. */
if ((tinstr & 0x00FF) == 0x18)
*ainstr |= ((tinstr & 0x00FF) << 16);
/* New breakpoint value. See gdb/arm-tdep.c */
else if ((tinstr & 0x00FF) == 0xFE)
*ainstr |= SWI_Breakpoint;
else
*ainstr |= (tinstr & 0x00FF);
}
else if ((tinstr & 0x0F00) != 0x0E00) {
/* Format 16 */
#if 0
int doit = FALSE;
/* TODO: Since we are doing a switch here, we could just add
the SWI and undefined instruction checks into this
switch to same on a couple of conditionals: */
switch ((tinstr & 0x0F00) >> 8) {
case EQ:
doit = ZFLAG;
break;
case NE:
doit = !ZFLAG;
break;
case VS:
doit = VFLAG;
break;
case VC:
doit = !VFLAG;
break;
case MI:
doit = NFLAG;
break;
case PL:
doit = !NFLAG;
break;
case CS:
doit = CFLAG;
break;
case CC:
doit = !CFLAG;
break;
case HI:
doit = (CFLAG && !ZFLAG);
break;
case LS:
doit = (!CFLAG || ZFLAG);
break;
case GE:
doit = ((!NFLAG && !VFLAG)
|| (NFLAG && VFLAG));
break;
case LT:
doit = ((NFLAG && !VFLAG)
|| (!NFLAG && VFLAG));
break;
case GT:
doit = ((!NFLAG && !VFLAG && !ZFLAG)
|| (NFLAG && VFLAG && !ZFLAG));
break;
case LE:
doit = ((NFLAG && !VFLAG)
|| (!NFLAG && VFLAG)) || ZFLAG;
break;
}
if (doit) {
state->Reg[15] = (pc + 4
+ (((tinstr & 0x7F) << 1)
| ((tinstr & (1 << 7)) ?
0xFFFFFF00 : 0)));
FLUSHPIPE;
}
#endif
valid = t_branch;
}
else /* UNDEFINED : cc=1110(AL) uses different format */
valid = t_undefined;
break;
case 28: /* B */
/* Format 18 */
#if 0
state->Reg[15] = (pc + 4 + (((tinstr & 0x3FF) << 1)
| ((tinstr & (1 << 10)) ?
0xFFFFF800 : 0)));
#endif
//FLUSHPIPE;
valid = t_branch;
break;
case 29:
if(tinstr & 0x1)
valid = t_undefined;
else{
/* BLX 1 for armv5t and above */
//printf("In %s, After BLX(1),LR=0x%x,PC=0x%x, offset=0x%x\n", __FUNCTION__, state->Reg[14], state->Reg[15], (tinstr &0x7FF) << 1);
valid = t_branch;
}
break;
case 30: /* BL instruction 1 */
/* Format 19 */
/* There is no single ARM instruction equivalent for this Thumb
instruction. To keep the simulation simple (from the user
perspective) we check if the following instruction is the
second half of this BL, and if it is we simulate it
immediately. */
valid = t_branch;
break;
case 31: /* BL instruction 2 */
/* Format 19 */
/* There is no single ARM instruction equivalent for this
instruction. Also, it should only ever be matched with the
fmt19 "BL instruction 1" instruction. However, we do allow
the simulation of it on its own, with undefined results if
r14 is not suitably initialised. */
{
#if 0
ARMword tmp = (pc + 2);
state->Reg[15] =
(state->Reg[14] + ((tinstr & 0x07FF) << 1));
state->Reg[14] = (tmp | 1);
#endif
valid = t_branch;
}
break;
}
*inst_size = 2;
return valid;
switch ((tinstr & 0xF800) >> 11) {
case 0: // LSL
case 1: // LSR
case 2: // ASR
*ainstr = 0xE1B00000 // base opcode
| ((tinstr & 0x1800) >> (11 - 5)) // shift type
|((tinstr & 0x07C0) << (7 - 6)) // imm5
|((tinstr & 0x0038) >> 3) // Rs
|((tinstr & 0x0007) << 12); // Rd
break;
case 3: // ADD/SUB
{
ARMword subset[4] = {
0xE0900000, // ADDS Rd,Rs,Rn
0xE0500000, // SUBS Rd,Rs,Rn
0xE2900000, // ADDS Rd,Rs,#imm3
0xE2500000 // SUBS Rd,Rs,#imm3
};
// It is quicker indexing into a table, than performing switch or conditionals:
*ainstr = subset[(tinstr & 0x0600) >> 9] // base opcode
|((tinstr & 0x01C0) >> 6) // Rn or imm3
|((tinstr & 0x0038) << (16 - 3)) // Rs
|((tinstr & 0x0007) << (12 - 0)); // Rd
}
break;
case 4: // MOV
case 5: // CMP
case 6: // ADD
case 7: // SUB
{
ARMword subset[4] = {
0xE3B00000, // MOVS Rd,#imm8
0xE3500000, // CMP Rd,#imm8
0xE2900000, // ADDS Rd,Rd,#imm8
0xE2500000, // SUBS Rd,Rd,#imm8
};
*ainstr = subset[(tinstr & 0x1800) >> 11] // base opcode
|((tinstr & 0x00FF) >> 0) // imm8
|((tinstr & 0x0700) << (16 - 8)) // Rn
|((tinstr & 0x0700) << (12 - 8)); // Rd
}
break;
case 8: // Arithmetic and high register transfers
// TODO: Since the subsets for both Format 4 and Format 5 instructions are made up of
// different ARM encodings, we could save the following conditional, and just have one
// large subset
if ((tinstr & (1 << 10)) == 0) {
enum otype {
t_norm,
t_shift,
t_neg,
t_mul
};
struct {
ARMword opcode;
otype type;
} subset[16] = {
{ 0xE0100000, t_norm }, // ANDS Rd,Rd,Rs
{ 0xE0300000, t_norm }, // EORS Rd,Rd,Rs
{ 0xE1B00010, t_shift }, // MOVS Rd,Rd,LSL Rs
{ 0xE1B00030, t_shift }, // MOVS Rd,Rd,LSR Rs
{ 0xE1B00050, t_shift }, // MOVS Rd,Rd,ASR Rs
{ 0xE0B00000, t_norm }, // ADCS Rd,Rd,Rs
{ 0xE0D00000, t_norm }, // SBCS Rd,Rd,Rs
{ 0xE1B00070, t_shift }, // MOVS Rd,Rd,ROR Rs
{ 0xE1100000, t_norm }, // TST Rd,Rs
{ 0xE2700000, t_neg }, // RSBS Rd,Rs,#0
{ 0xE1500000, t_norm }, // CMP Rd,Rs
{ 0xE1700000, t_norm }, // CMN Rd,Rs
{ 0xE1900000, t_norm }, // ORRS Rd,Rd,Rs
{ 0xE0100090, t_mul }, // MULS Rd,Rd,Rs
{ 0xE1D00000, t_norm }, // BICS Rd,Rd,Rs
{ 0xE1F00000, t_norm } // MVNS Rd,Rs
};
*ainstr = subset[(tinstr & 0x03C0) >> 6].opcode; // base
switch (subset[(tinstr & 0x03C0) >> 6].type) {
case t_norm:
*ainstr |= ((tinstr & 0x0007) << 16) // Rn
|((tinstr & 0x0007) << 12) // Rd
|((tinstr & 0x0038) >> 3); // Rs
break;
case t_shift:
*ainstr |= ((tinstr & 0x0007) << 12) // Rd
|((tinstr & 0x0007) >> 0) // Rm
|((tinstr & 0x0038) << (8 - 3)); // Rs
break;
case t_neg:
*ainstr |= ((tinstr & 0x0007) << 12) // Rd
|((tinstr & 0x0038) << (16 - 3)); // Rn
break;
case t_mul:
*ainstr |= ((tinstr & 0x0007) << 16) // Rd
|((tinstr & 0x0007) << 8) // Rs
|((tinstr & 0x0038) >> 3); // Rm
break;
}
} else {
ARMword Rd = ((tinstr & 0x0007) >> 0);
ARMword Rs = ((tinstr & 0x0038) >> 3);
if (tinstr & (1 << 7))
Rd += 8;
if (tinstr & (1 << 6))
Rs += 8;
switch ((tinstr & 0x03C0) >> 6) {
case 0x1: // ADD Rd,Rd,Hs
case 0x2: // ADD Hd,Hd,Rs
case 0x3: // ADD Hd,Hd,Hs
*ainstr = 0xE0800000 // base
| (Rd << 16) // Rn
|(Rd << 12) // Rd
|(Rs << 0); // Rm
break;
case 0x5: // CMP Rd,Hs
case 0x6: // CMP Hd,Rs
case 0x7: // CMP Hd,Hs
*ainstr = 0xE1500000 // base
| (Rd << 16) // Rn
|(Rd << 12) // Rd
|(Rs << 0); // Rm
break;
case 0x9: // MOV Rd,Hs
case 0xA: // MOV Hd,Rs
case 0xB: // MOV Hd,Hs
*ainstr = 0xE1A00000 // base
| (Rd << 16) // Rn
|(Rd << 12) // Rd
|(Rs << 0); // Rm
break;
case 0xC: // BX Rs
case 0xD: // BX Hs
*ainstr = 0xE12FFF10 // base
| ((tinstr & 0x0078) >> 3); // Rd
break;
case 0x0: // UNDEFINED
case 0x4: // UNDEFINED
case 0x8: // UNDEFINED
valid = t_undefined;
break;
case 0xE: // BLX
case 0xF: // BLX
*ainstr = 0xE1200030 // base
| (Rs << 0); // Rm
break;
}
}
break;
case 9: // LDR Rd,[PC,#imm8]
*ainstr = 0xE59F0000 // base
| ((tinstr & 0x0700) << (12 - 8)) // Rd
|((tinstr & 0x00FF) << (2 - 0)); // off8
break;
case 10:
case 11:
// TODO: Format 7 and Format 8 perform the same ARM encoding, so the following could be
// merged into a single subset, saving on the following boolean:
if ((tinstr & (1 << 9)) == 0) {
ARMword subset[4] = {
0xE7800000, // STR Rd,[Rb,Ro]
0xE7C00000, // STRB Rd,[Rb,Ro]
0xE7900000, // LDR Rd,[Rb,Ro]
0xE7D00000 // LDRB Rd,[Rb,Ro]
};
*ainstr = subset[(tinstr & 0x0C00) >> 10] // base
|((tinstr & 0x0007) << (12 - 0)) // Rd
|((tinstr & 0x0038) << (16 - 3)) // Rb
|((tinstr & 0x01C0) >> 6); // Ro
} else {
ARMword subset[4] = {
0xE18000B0, // STRH Rd,[Rb,Ro]
0xE19000D0, // LDRSB Rd,[Rb,Ro]
0xE19000B0, // LDRH Rd,[Rb,Ro]
0xE19000F0 // LDRSH Rd,[Rb,Ro]
};
*ainstr = subset[(tinstr & 0x0C00) >> 10] // base
|((tinstr & 0x0007) << (12 - 0)) // Rd
|((tinstr & 0x0038) << (16 - 3)) // Rb
|((tinstr & 0x01C0) >> 6); // Ro
}
break;
case 12: // STR Rd,[Rb,#imm5]
case 13: // LDR Rd,[Rb,#imm5]
case 14: // STRB Rd,[Rb,#imm5]
case 15: // LDRB Rd,[Rb,#imm5]
{
ARMword subset[4] = {
0xE5800000, // STR Rd,[Rb,#imm5]
0xE5900000, // LDR Rd,[Rb,#imm5]
0xE5C00000, // STRB Rd,[Rb,#imm5]
0xE5D00000 // LDRB Rd,[Rb,#imm5]
};
// The offset range defends on whether we are transferring a byte or word value:
*ainstr = subset[(tinstr & 0x1800) >> 11] // base
|((tinstr & 0x0007) << (12 - 0)) // Rd
|((tinstr & 0x0038) << (16 - 3)) // Rb
|((tinstr & 0x07C0) >> (6 - ((tinstr & (1 << 12)) ? 0 : 2))); // off5
}
break;
case 16: // STRH Rd,[Rb,#imm5]
case 17: // LDRH Rd,[Rb,#imm5]
*ainstr = ((tinstr & (1 << 11)) // base
? 0xE1D000B0 // LDRH
: 0xE1C000B0) // STRH
|((tinstr & 0x0007) << (12 - 0)) // Rd
|((tinstr & 0x0038) << (16 - 3)) // Rb
|((tinstr & 0x01C0) >> (6 - 1)) // off5, low nibble
|((tinstr & 0x0600) >> (9 - 8)); // off5, high nibble
break;
case 18: // STR Rd,[SP,#imm8]
case 19: // LDR Rd,[SP,#imm8]
*ainstr = ((tinstr & (1 << 11)) // base
? 0xE59D0000 // LDR
: 0xE58D0000) // STR
|((tinstr & 0x0700) << (12 - 8)) // Rd
|((tinstr & 0x00FF) << 2); // off8
break;
case 20: // ADD Rd,PC,#imm8
case 21: // ADD Rd,SP,#imm8
if ((tinstr & (1 << 11)) == 0) {
// NOTE: The PC value used here should by word aligned. We encode shift-left-by-2 in the
// rotate immediate field, so no shift of off8 is needed.
*ainstr = 0xE28F0F00 // base
| ((tinstr & 0x0700) << (12 - 8)) // Rd
|(tinstr & 0x00FF); // off8
} else {
// We encode shift-left-by-2 in the rotate immediate field, so no shift of off8 is needed.
*ainstr = 0xE28D0F00 // base
| ((tinstr & 0x0700) << (12 - 8)) // Rd
|(tinstr & 0x00FF); // off8
}
break;
case 22:
case 23:
if ((tinstr & 0x0F00) == 0x0000) {
// NOTE: The instruction contains a shift left of 2 equivalent (implemented as ROR #30):
*ainstr = ((tinstr & (1 << 7)) // base
? 0xE24DDF00 // SUB
: 0xE28DDF00) // ADD
|(tinstr & 0x007F); // off7
} else if ((tinstr & 0x0F00) == 0x0e00)
*ainstr = 0xEF000000 | SWI_Breakpoint;
else {
ARMword subset[4] = {
0xE92D0000, // STMDB sp!,{rlist}
0xE92D4000, // STMDB sp!,{rlist,lr}
0xE8BD0000, // LDMIA sp!,{rlist}
0xE8BD8000 // LDMIA sp!,{rlist,pc}
};
*ainstr = subset[((tinstr & (1 << 11)) >> 10) | ((tinstr & (1 << 8)) >> 8)] // base
|(tinstr & 0x00FF); // mask8
}
break;
case 24: // STMIA
case 25: // LDMIA
*ainstr = ((tinstr & (1 << 11)) // base
? 0xE8B00000 // LDMIA
: 0xE8A00000) // STMIA
|((tinstr & 0x0700) << (16 - 8)) // Rb
|(tinstr & 0x00FF); // mask8
break;
case 26: // Bcc
case 27: // Bcc/SWI
if ((tinstr & 0x0F00) == 0x0F00) {
// Format 17 : SWI
*ainstr = 0xEF000000;
// Breakpoint must be handled specially.
if ((tinstr & 0x00FF) == 0x18)
*ainstr |= ((tinstr & 0x00FF) << 16);
// New breakpoint value. See gdb/arm-tdep.c
else if ((tinstr & 0x00FF) == 0xFE)
*ainstr |= SWI_Breakpoint;
else
*ainstr |= (tinstr & 0x00FF);
} else if ((tinstr & 0x0F00) != 0x0E00)
valid = t_branch;
else // UNDEFINED : cc=1110(AL) uses different format
valid = t_undefined;
break;
case 28: // B
valid = t_branch;
break;
case 29:
if(tinstr & 0x1)
valid = t_undefined;
else
valid = t_branch;
break;
case 30: // BL instruction 1
// There is no single ARM instruction equivalent for this Thumb instruction. To keep the
// simulation simple (from the user perspective) we check if the following instruction is
// the second half of this BL, and if it is we simulate it immediately
valid = t_branch;
break;
case 31: // BL instruction 2
// There is no single ARM instruction equivalent for this instruction. Also, it should only
// ever be matched with the fmt19 "BL instruction 1" instruction. However, we do allow the
// simulation of it on its own, with undefined results if r14 is not suitably initialised.
valid = t_branch;
break;
}
*inst_size = 2;
return valid;
}

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/arm/interpreter/arm_interpreter.h"
@ -38,78 +38,43 @@ ARM_Interpreter::~ARM_Interpreter() {
delete state;
}
/**
* Set the Program Counter to an address
* @param addr Address to set PC to
*/
void ARM_Interpreter::SetPC(u32 pc) {
state->pc = state->Reg[15] = pc;
}
/*
* Get the current Program Counter
* @return Returns current PC
*/
u32 ARM_Interpreter::GetPC() const {
return state->pc;
}
/**
* Get an ARM register
* @param index Register index (0-15)
* @return Returns the value in the register
*/
u32 ARM_Interpreter::GetReg(int index) const {
return state->Reg[index];
}
/**
* Set an ARM register
* @param index Register index (0-15)
* @param value Value to set register to
*/
void ARM_Interpreter::SetReg(int index, u32 value) {
state->Reg[index] = value;
}
/**
* Get the current CPSR register
* @return Returns the value of the CPSR register
*/
u32 ARM_Interpreter::GetCPSR() const {
return state->Cpsr;
}
/**
* Set the current CPSR register
* @param cpsr Value to set CPSR to
*/
void ARM_Interpreter::SetCPSR(u32 cpsr) {
state->Cpsr = cpsr;
}
/**
* Returns the number of clock ticks since the last reset
* @return Returns number of clock ticks
*/
u64 ARM_Interpreter::GetTicks() const {
return ARMul_Time(state);
return state->NumInstrs;
}
void ARM_Interpreter::AddTicks(u64 ticks) {
state->NumInstrs += ticks;
}
/**
* Executes the given number of instructions
* @param num_instructions Number of instructions to executes
*/
void ARM_Interpreter::ExecuteInstructions(int num_instructions) {
state->NumInstrsToExecute = num_instructions - 1;
ARMul_Emulate32(state);
}
/**
* Saves the current CPU context
* @param ctx Thread context to save
* @todo Do we need to save Reg[15] and NextInstr?
*/
void ARM_Interpreter::SaveContext(ThreadContext& ctx) {
memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers));
memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers));
@ -126,11 +91,6 @@ void ARM_Interpreter::SaveContext(ThreadContext& ctx) {
ctx.mode = state->NextInstr;
}
/**
* Loads a CPU context
* @param ctx Thread context to load
* @param Do we need to load Reg[15] and NextInstr?
*/
void ARM_Interpreter::LoadContext(const ThreadContext& ctx) {
memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers));
memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers));
@ -147,7 +107,6 @@ void ARM_Interpreter::LoadContext(const ThreadContext& ctx) {
state->NextInstr = ctx.mode;
}
/// Prepare core for thread reschedule (if needed to correctly handle state)
void ARM_Interpreter::PrepareReschedule() {
state->NumInstrsToExecute = 0;
}

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -60,6 +60,12 @@ public:
*/
u64 GetTicks() const override;
/**
* Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time)
* @param ticks Number of ticks to advance the CPU core
*/
void AddTicks(u64 ticks) override;
/**
* Saves the current CPU context
* @param ctx Thread context to save

File diff suppressed because it is too large Load diff

View file

@ -227,8 +227,9 @@ ARMul_CPSRAltered (ARMul_State * state)
//state->Cpsr &= ~CBIT;
ASSIGNV ((state->Cpsr & VBIT) != 0);
//state->Cpsr &= ~VBIT;
ASSIGNS ((state->Cpsr & SBIT) != 0);
//state->Cpsr &= ~SBIT;
ASSIGNQ ((state->Cpsr & QBIT) != 0);
//state->Cpsr &= ~QBIT;
state->GEFlag = (state->Cpsr & 0x000F0000);
#ifdef MODET
ASSIGNT ((state->Cpsr & TBIT) != 0);
//state->Cpsr &= ~TBIT;
@ -391,6 +392,15 @@ ARMul_NthReg (ARMword instr, unsigned number)
return (bit - 1);
}
/* Unsigned sum of absolute difference */
u8 ARMul_UnsignedAbsoluteDifference(u8 left, u8 right)
{
if (left > right)
return left - right;
return right - left;
}
/* Assigns the N and Z flags depending on the value of result. */
void
@ -443,6 +453,14 @@ ARMul_AddOverflow (ARMul_State * state, ARMword a, ARMword b, ARMword result)
ASSIGNV (AddOverflow (a, b, result));
}
/* Assigns the Q flag if the given result is considered an overflow from the addition of a and b */
void ARMul_AddOverflowQ(ARMul_State* state, ARMword a, ARMword b)
{
u32 result = a + b;
if (((result ^ a) & (u32)0x80000000) && ((a ^ b) & (u32)0x80000000) == 0)
SETQ;
}
/* Assigns the C flag after an subtraction of a and b to give result. */
void
@ -460,6 +478,142 @@ ARMul_SubOverflow (ARMul_State * state, ARMword a, ARMword b, ARMword result)
ASSIGNV (SubOverflow (a, b, result));
}
/* 8-bit signed saturated addition */
u8 ARMul_SignedSaturatedAdd8(u8 left, u8 right)
{
u8 result = left + right;
if (((result ^ left) & 0x80) && ((left ^ right) & 0x80) == 0) {
if (left & 0x80)
result = 0x80;
else
result = 0x7F;
}
return result;
}
/* 8-bit signed saturated subtraction */
u8 ARMul_SignedSaturatedSub8(u8 left, u8 right)
{
u8 result = left - right;
if (((result ^ left) & 0x80) && ((left ^ right) & 0x80) != 0) {
if (left & 0x80)
result = 0x80;
else
result = 0x7F;
}
return result;
}
/* 16-bit signed saturated addition */
u16 ARMul_SignedSaturatedAdd16(u16 left, u16 right)
{
u16 result = left + right;
if (((result ^ left) & 0x8000) && ((left ^ right) & 0x8000) == 0) {
if (left & 0x8000)
result = 0x8000;
else
result = 0x7FFF;
}
return result;
}
/* 16-bit signed saturated subtraction */
u16 ARMul_SignedSaturatedSub16(u16 left, u16 right)
{
u16 result = left - right;
if (((result ^ left) & 0x8000) && ((left ^ right) & 0x8000) != 0) {
if (left & 0x8000)
result = 0x8000;
else
result = 0x7FFF;
}
return result;
}
/* 8-bit unsigned saturated addition */
u8 ARMul_UnsignedSaturatedAdd8(u8 left, u8 right)
{
u8 result = left + right;
if (result < left)
result = 0xFF;
return result;
}
/* 16-bit unsigned saturated addition */
u16 ARMul_UnsignedSaturatedAdd16(u16 left, u16 right)
{
u16 result = left + right;
if (result < left)
result = 0xFFFF;
return result;
}
/* 8-bit unsigned saturated subtraction */
u8 ARMul_UnsignedSaturatedSub8(u8 left, u8 right)
{
if (left <= right)
return 0;
return left - right;
}
/* 16-bit unsigned saturated subtraction */
u16 ARMul_UnsignedSaturatedSub16(u16 left, u16 right)
{
if (left <= right)
return 0;
return left - right;
}
// Signed saturation.
u32 ARMul_SignedSatQ(s32 value, u8 shift, bool* saturation_occurred)
{
const u32 max = (1 << shift) - 1;
const s32 top = (value >> shift);
if (top > 0) {
*saturation_occurred = true;
return max;
}
else if (top < -1) {
*saturation_occurred = true;
return ~max;
}
*saturation_occurred = false;
return (u32)value;
}
// Unsigned saturation
u32 ARMul_UnsignedSatQ(s32 value, u8 shift, bool* saturation_occurred)
{
const u32 max = (1 << shift) - 1;
if (value < 0) {
*saturation_occurred = true;
return 0;
} else if ((u32)value > max) {
*saturation_occurred = true;
return max;
}
*saturation_occurred = false;
return (u32)value;
}
/* This function does the work of generating the addresses used in an
LDC instruction. The code here is always post-indexed, it's up to the
caller to get the input address correct and to handle base register
@ -665,7 +819,7 @@ ARMul_MCR (ARMul_State * state, ARMword instr, ARMword source)
//if (!CP_ACCESS_ALLOWED (state, CPNum)) {
if (!state->MCR[CPNum]) {
//chy 2004-07-19 should fix in the future ????!!!!
DEBUG("SKYEYE ARMul_MCR, ACCESS_not ALLOWed, UndefinedInstr CPnum is %x, source %x\n",CPNum, source);
LOG_ERROR(Core_ARM11, "SKYEYE ARMul_MCR, ACCESS_not ALLOWed, UndefinedInstr CPnum is %x, source %x",CPNum, source);
ARMul_UndefInstr (state, instr);
return;
}
@ -690,7 +844,7 @@ ARMul_MCR (ARMul_State * state, ARMword instr, ARMword source)
}
if (cpab == ARMul_CANT) {
DEBUG("SKYEYE ARMul_MCR, CANT, UndefinedInstr %x CPnum is %x, source %x\n", instr, CPNum, source); //ichfly todo
LOG_ERROR(Core_ARM11, "SKYEYE ARMul_MCR, CANT, UndefinedInstr %x CPnum is %x, source %x", instr, CPNum, source); //ichfly todo
//ARMul_Abort (state, ARMul_UndefinedInstrV);
} else {
BUSUSEDINCPCN;
@ -762,7 +916,7 @@ ARMword ARMul_MRC (ARMul_State * state, ARMword instr)
//if (!CP_ACCESS_ALLOWED (state, CPNum)) {
if (!state->MRC[CPNum]) {
//chy 2004-07-19 should fix in the future????!!!!
DEBUG("SKYEYE ARMul_MRC,NOT ALLOWed UndefInstr CPnum is %x, instr %x\n", CPNum, instr);
LOG_ERROR(Core_ARM11, "SKYEYE ARMul_MRC,NOT ALLOWed UndefInstr CPnum is %x, instr %x", CPNum, instr);
ARMul_UndefInstr (state, instr);
return -1;
}
@ -865,7 +1019,7 @@ void
ARMul_UndefInstr (ARMul_State * state, ARMword instr)
{
std::string disasm = ARM_Disasm::Disassemble(state->pc, instr);
ERROR_LOG(ARM11, "Undefined instruction!! Disasm: %s Opcode: 0x%x", disasm.c_str(), instr);
LOG_ERROR(Core_ARM11, "Undefined instruction!! Disasm: %s Opcode: 0x%x", disasm.c_str(), instr);
ARMul_Abort (state, ARMul_UndefinedInstrV);
}

View file

@ -467,7 +467,7 @@ ARMul_ThumbDecode (
(state->Reg[14] + ((tinstr & 0x07FF) << 1)) & 0xFFFFFFFC;
state->Reg[14] = (tmp | 1);
CLEART;
DEBUG_LOG(ARM11, "In %s, After BLX(1),LR=0x%x,PC=0x%x, offset=0x%x\n", __FUNCTION__, state->Reg[14], state->Reg[15], (tinstr &0x7FF) << 1);
LOG_DEBUG(Core_ARM11, "After BLX(1),LR=0x%x,PC=0x%x, offset=0x%x", state->Reg[14], state->Reg[15], (tinstr &0x7FF) << 1);
valid = t_branch;
FLUSHPIPE;
}

View file

@ -18,38 +18,26 @@
#ifndef _ARMDEFS_H_
#define _ARMDEFS_H_
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "common/platform.h"
//teawater add for arm2x86 2005.02.14-------------------------------------------
// koodailar remove it for mingw 2005.12.18----------------
//anthonylee modify it for portable 2007.01.30
//#include "portable/mman.h"
#include <cerrno>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "arm_regformat.h"
#include "common/common_types.h"
#include "common/platform.h"
#include "core/arm/skyeye_common/armmmu.h"
#include "core/arm/skyeye_common/skyeye_defs.h"
//AJ2D--------------------------------------------------------------------------
//teawater add for arm2x86 2005.07.03-------------------------------------------
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if EMU_PLATFORM == PLATFORM_LINUX
#include <sys/time.h>
#include <unistd.h>
#endif
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
//#include <memory_space.h>
//AJ2D--------------------------------------------------------------------------
#if 0
#if 0
#define DIFF_STATE 1
@ -70,25 +58,8 @@
#define LOWHIGH 1
#define HIGHLOW 2
//teawater add DBCT_TEST_SPEED 2005.10.04---------------------------------------
#include <signal.h>
#include "common/platform.h"
#if EMU_PLATFORM == PLATFORM_LINUX
#include <sys/time.h>
#endif
//#define DBCT_TEST_SPEED
#define DBCT_TEST_SPEED_SEC 10
//AJ2D--------------------------------------------------------------------------
//teawater add compile switch for DBCT GDB RSP function 2005.10.21--------------
//#define DBCT_GDBRSP
//AJ2D--------------------------------------------------------------------------
//#include <skyeye_defs.h>
//#include <skyeye_types.h>
#define ARM_BYTE_TYPE 0
#define ARM_HALFWORD_TYPE 1
@ -103,71 +74,34 @@
typedef char *VoidStar;
#endif
typedef unsigned long long ARMdword; /* must be 64 bits wide */
typedef unsigned int ARMword; /* must be 32 bits wide */
typedef unsigned char ARMbyte; /* must be 8 bits wide */
typedef unsigned short ARMhword; /* must be 16 bits wide */
typedef u64 ARMdword; // must be 64 bits wide
typedef u32 ARMword; // must be 32 bits wide
typedef u16 ARMhword; // must be 16 bits wide
typedef u8 ARMbyte; // must be 8 bits wide
typedef struct ARMul_State ARMul_State;
typedef struct ARMul_io ARMul_io;
typedef struct ARMul_Energy ARMul_Energy;
//teawater add for arm2x86 2005.06.24-------------------------------------------
#include <stdint.h>
//AJ2D--------------------------------------------------------------------------
/*
//chy 2005-05-11
#ifndef __CYGWIN__
//teawater add for arm2x86 2005.02.14-------------------------------------------
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int u32;
#if defined (__x86_64__)
typedef unsigned long uint64_t;
#else
typedef unsigned long long uint64_t;
#endif
////AJ2D--------------------------------------------------------------------------
#endif
*/
#include "core/arm/skyeye_common/armmmu.h"
//#include "lcd/skyeye_lcd.h"
//#include "skyeye.h"
//#include "skyeye_device.h"
//#include "net/skyeye_net.h"
//#include "skyeye_config.h"
typedef unsigned ARMul_CPInits (ARMul_State * state);
typedef unsigned ARMul_CPExits (ARMul_State * state);
typedef unsigned ARMul_LDCs (ARMul_State * state, unsigned type,
ARMword instr, ARMword value);
typedef unsigned ARMul_STCs (ARMul_State * state, unsigned type,
ARMword instr, ARMword * value);
typedef unsigned ARMul_MRCs (ARMul_State * state, unsigned type,
ARMword instr, ARMword * value);
typedef unsigned ARMul_MCRs (ARMul_State * state, unsigned type,
ARMword instr, ARMword value);
typedef unsigned ARMul_MRRCs (ARMul_State * state, unsigned type,
ARMword instr, ARMword * value1, ARMword * value2);
typedef unsigned ARMul_MCRRs (ARMul_State * state, unsigned type,
ARMword instr, ARMword value1, ARMword value2);
typedef unsigned ARMul_CDPs (ARMul_State * state, unsigned type,
ARMword instr);
typedef unsigned ARMul_CPReads (ARMul_State * state, unsigned reg,
ARMword * value);
typedef unsigned ARMul_CPWrites (ARMul_State * state, unsigned reg,
ARMword value);
typedef unsigned ARMul_CPInits(ARMul_State* state);
typedef unsigned ARMul_CPExits(ARMul_State* state);
typedef unsigned ARMul_LDCs(ARMul_State* state, unsigned type, ARMword instr, ARMword value);
typedef unsigned ARMul_STCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value);
typedef unsigned ARMul_MRCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value);
typedef unsigned ARMul_MCRs(ARMul_State* state, unsigned type, ARMword instr, ARMword value);
typedef unsigned ARMul_MRRCs(ARMul_State* state, unsigned type, ARMword instr, ARMword* value1, ARMword* value2);
typedef unsigned ARMul_MCRRs(ARMul_State* state, unsigned type, ARMword instr, ARMword value1, ARMword value2);
typedef unsigned ARMul_CDPs(ARMul_State* state, unsigned type, ARMword instr);
typedef unsigned ARMul_CPReads(ARMul_State* state, unsigned reg, ARMword* value);
typedef unsigned ARMul_CPWrites(ARMul_State* state, unsigned reg, ARMword value);
//added by ksh,2004-3-5
struct ARMul_io
{
ARMword *instr; //to display the current interrupt state
ARMword *net_flag; //to judge if network is enabled
ARMword *net_int; //netcard interrupt
ARMword *instr; // to display the current interrupt state
ARMword *net_flag; // to judge if network is enabled
ARMword *net_int; // netcard interrupt
//ywc,2004-04-01
ARMword *ts_int;
@ -180,17 +114,17 @@ struct ARMul_io
/* added by ksh,2004-11-26,some energy profiling */
struct ARMul_Energy
{
int energy_prof; /* <tktan> BUG200103282109 : for energy profiling */
int enable_func_energy; /* <tktan> BUG200105181702 */
int energy_prof; /* <tktan> BUG200103282109 : for energy profiling */
int enable_func_energy; /* <tktan> BUG200105181702 */
char *func_energy;
int func_display; /* <tktan> BUG200103311509 : for function call display */
int func_display; /* <tktan> BUG200103311509 : for function call display */
int func_disp_start; /* <tktan> BUG200104191428 : to start func profiling */
char *start_func; /* <tktan> BUG200104191428 */
char *start_func; /* <tktan> BUG200104191428 */
FILE *outfile; /* <tktan> BUG200105201531 : direct console to file */
FILE *outfile; /* <tktan> BUG200105201531 : direct console to file */
long long tcycle, pcycle;
float t_energy;
void *cur_task; /* <tktan> BUG200103291737 */
void *cur_task; /* <tktan> BUG200103291737 */
long long t_mem_cycle, t_idle_cycle, t_uart_cycle;
long long p_mem_cycle, p_idle_cycle, p_uart_cycle;
long long p_io_update_tcycle;
@ -203,13 +137,12 @@ struct ARMul_Energy
typedef struct mem_bank
{
ARMword (*read_byte) (ARMul_State * state, ARMword addr);
void (*write_byte) (ARMul_State * state, ARMword addr, ARMword data);
ARMword (*read_halfword) (ARMul_State * state, ARMword addr);
void (*write_halfword) (ARMul_State * state, ARMword addr,
ARMword data);
ARMword (*read_word) (ARMul_State * state, ARMword addr);
void (*write_word) (ARMul_State * state, ARMword addr, ARMword data);
ARMword (*read_byte) (ARMul_State* state, ARMword addr);
void (*write_byte) (ARMul_State* state, ARMword addr, ARMword data);
ARMword (*read_halfword) (ARMul_State* state, ARMword addr);
void (*write_halfword) (ARMul_State* state, ARMword addr, ARMword data);
ARMword (*read_word) (ARMul_State* state, ARMword addr);
void (*write_word) (ARMul_State* state, ARMword addr, ARMword data);
unsigned int addr, len;
char filename[MAX_STR];
unsigned type; //chy 2003-09-21: maybe io,ram,rom
@ -224,24 +157,24 @@ typedef struct
#define VFP_REG_NUM 64
struct ARMul_State
{
ARMword Emulate; /* to start and stop emulation */
unsigned EndCondition; /* reason for stopping */
ARMword Emulate; /* to start and stop emulation */
unsigned EndCondition; /* reason for stopping */
unsigned ErrorCode; /* type of illegal instruction */
/* Order of the following register should not be modified */
ARMword Reg[16]; /* the current register file */
ARMword Cpsr; /* the current psr */
ARMword Reg[16]; /* the current register file */
ARMword Cpsr; /* the current psr */
ARMword Spsr_copy;
ARMword phys_pc;
ARMword Reg_usr[2];
ARMword Reg_svc[2]; /* R13_SVC R14_SVC */
ARMword Reg_svc[2]; /* R13_SVC R14_SVC */
ARMword Reg_abort[2]; /* R13_ABORT R14_ABORT */
ARMword Reg_undef[2]; /* R13 UNDEF R14 UNDEF */
ARMword Reg_irq[2]; /* R13_IRQ R14_IRQ */
ARMword Reg_firq[7]; /* R8---R14 FIRQ */
ARMword Spsr[7]; /* the exception psr's */
ARMword Mode; /* the current mode */
ARMword Bank; /* the current register bank */
ARMword Spsr[7]; /* the exception psr's */
ARMword Mode; /* the current mode */
ARMword Bank; /* the current register bank */
ARMword exclusive_tag;
ARMword exclusive_state;
ARMword exclusive_result;
@ -265,7 +198,7 @@ struct ARMul_State
//ARMword translate_pc;
/* add armv6 flags dyf:2010-08-09 */
ARMword GEFlag, EFlag, AFlag, QFlags;
ARMword GEFlag, EFlag, AFlag, QFlag;
//chy:2003-08-19, used in arm v5e|xscale
ARMword SFlag;
#ifdef MODET
@ -281,38 +214,39 @@ struct ARMul_State
ARMword currentexaddr;
ARMword currentexval;
ARMword currentexvald;
ARMword servaddr;
unsigned NextInstr;
unsigned VectorCatch; /* caught exception mask */
unsigned CallDebug; /* set to call the debugger */
unsigned CanWatch; /* set by memory interface if its willing to suffer the
overhead of checking for watchpoints on each memory
access */
unsigned VectorCatch; /* caught exception mask */
unsigned CallDebug; /* set to call the debugger */
unsigned CanWatch; /* set by memory interface if its willing to suffer the
overhead of checking for watchpoints on each memory
access */
unsigned int StopHandle;
char *CommandLine; /* Command Line from ARMsd */
char *CommandLine; /* Command Line from ARMsd */
ARMul_CPInits *CPInit[16]; /* coprocessor initialisers */
ARMul_CPExits *CPExit[16]; /* coprocessor finalisers */
ARMul_LDCs *LDC[16]; /* LDC instruction */
ARMul_STCs *STC[16]; /* STC instruction */
ARMul_MRCs *MRC[16]; /* MRC instruction */
ARMul_MCRs *MCR[16]; /* MCR instruction */
ARMul_MRRCs *MRRC[16]; /* MRRC instruction */
ARMul_MCRRs *MCRR[16]; /* MCRR instruction */
ARMul_CDPs *CDP[16]; /* CDP instruction */
ARMul_CPReads *CPRead[16]; /* Read CP register */
ARMul_CPWrites *CPWrite[16]; /* Write CP register */
unsigned char *CPData[16]; /* Coprocessor data */
ARMul_CPInits *CPInit[16]; /* coprocessor initialisers */
ARMul_CPExits *CPExit[16]; /* coprocessor finalisers */
ARMul_LDCs *LDC[16]; /* LDC instruction */
ARMul_STCs *STC[16]; /* STC instruction */
ARMul_MRCs *MRC[16]; /* MRC instruction */
ARMul_MCRs *MCR[16]; /* MCR instruction */
ARMul_MRRCs *MRRC[16]; /* MRRC instruction */
ARMul_MCRRs *MCRR[16]; /* MCRR instruction */
ARMul_CDPs *CDP[16]; /* CDP instruction */
ARMul_CPReads *CPRead[16]; /* Read CP register */
ARMul_CPWrites *CPWrite[16]; /* Write CP register */
unsigned char *CPData[16]; /* Coprocessor data */
unsigned char const *CPRegWords[16]; /* map of coprocessor register sizes */
unsigned EventSet; /* the number of events in the queue */
unsigned int Now; /* time to the nearest cycle */
struct EventNode **EventPtr; /* the event list */
unsigned EventSet; /* the number of events in the queue */
unsigned int Now; /* time to the nearest cycle */
struct EventNode **EventPtr; /* the event list */
unsigned Debug; /* show instructions as they are executed */
unsigned NresetSig; /* reset the processor */
unsigned Debug; /* show instructions as they are executed */
unsigned NresetSig; /* reset the processor */
unsigned NfiqSig;
unsigned NirqSig;
@ -356,12 +290,12 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model)
*/
unsigned lateabtSig;
ARMword Vector; /* synthesize aborts in cycle modes */
ARMword Aborted; /* sticky flag for aborts */
ARMword Reseted; /* sticky flag for Reset */
ARMword Vector; /* synthesize aborts in cycle modes */
ARMword Aborted; /* sticky flag for aborts */
ARMword Reseted; /* sticky flag for Reset */
ARMword Inted, LastInted; /* sticky flags for interrupts */
ARMword Base; /* extra hand for base writeback */
ARMword AbortAddr; /* to keep track of Prefetch aborts */
ARMword Base; /* extra hand for base writeback */
ARMword AbortAddr; /* to keep track of Prefetch aborts */
const struct Dbg_HostosInterface *hostif;
@ -378,7 +312,7 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model)
//chy: 2003-08-11, for different arm core type
unsigned is_v4; /* Are we emulating a v4 architecture (or higher) ? */
unsigned is_v5; /* Are we emulating a v5 architecture ? */
unsigned is_v5e; /* Are we emulating a v5e architecture ? */
unsigned is_v5e; /* Are we emulating a v5e architecture ? */
unsigned is_v6; /* Are we emulating a v6 architecture ? */
unsigned is_v7; /* Are we emulating a v7 architecture ? */
unsigned is_XScale; /* Are we emulating an XScale architecture ? */
@ -387,51 +321,43 @@ So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model)
//chy 2005-09-19
unsigned is_pxa27x; /* Are we emulating a Intel PXA27x co-processor ? */
//chy: seems only used in xscale's CP14
unsigned int LastTime; /* Value of last call to ARMul_Time() */
unsigned int LastTime; /* Value of last call to ARMul_Time() */
ARMword CP14R0_CCD; /* used to count 64 clock cycles with CP14 R0 bit 3 set */
//added by ksh:for handle different machs io 2004-3-5
//added by ksh:for handle different machs io 2004-3-5
ARMul_io mach_io;
/*added by ksh,2004-11-26,some energy profiling*/
/*added by ksh,2004-11-26,some energy profiling*/
ARMul_Energy energy;
//teawater add for next_dis 2004.10.27-----------------------
//teawater add for next_dis 2004.10.27-----------------------
int disassemble;
//AJ2D------------------------------------------
//teawater add for arm2x86 2005.02.15-------------------------------------------
//teawater add for arm2x86 2005.02.15-------------------------------------------
u32 trap;
u32 tea_break_addr;
u32 tea_break_ok;
int tea_pc;
//AJ2D--------------------------------------------------------------------------
//teawater add for arm2x86 2005.07.03-------------------------------------------
/*
* 2007-01-24 removed the term-io functions by Anthony Lee,
* moved to "device/uart/skyeye_uart_stdio.c".
*/
//AJ2D--------------------------------------------------------------------------
//teawater add for arm2x86 2005.07.05-------------------------------------------
//teawater add for arm2x86 2005.07.05-------------------------------------------
//arm_arm A2-18
int abort_model; //0 Base Restored Abort Model, 1 the Early Abort Model, 2 Base Updated Abort Model
//AJ2D--------------------------------------------------------------------------
//teawater change for return if running tb dirty 2005.07.09---------------------
//teawater change for return if running tb dirty 2005.07.09---------------------
void *tb_now;
//AJ2D--------------------------------------------------------------------------
//teawater add for record reg value to ./reg.txt 2005.07.10---------------------
//teawater add for record reg value to ./reg.txt 2005.07.10---------------------
FILE *tea_reg_fd;
//AJ2D--------------------------------------------------------------------------
/*added by ksh in 2005-10-1*/
/*added by ksh in 2005-10-1*/
cpu_config_t *cpu;
//mem_config_t *mem_bank;
/* added LPC remap function */
/* added LPC remap function */
int vector_remap_flag;
u32 vector_remap_addr;
u32 vector_remap_size;
@ -486,17 +412,14 @@ typedef ARMul_State arm_core_t;
#define ARM_Debug_Prop 0x10
#define ARM_Isync_Prop ARM_Debug_Prop
#define ARM_Lock_Prop 0x20
//chy 2003-08-11
#define ARM_v4_Prop 0x40
#define ARM_v5_Prop 0x80
/*jeff.du 2010-08-05 */
#define ARM_v6_Prop 0xc0
#define ARM_v5e_Prop 0x100
#define ARM_XScale_Prop 0x200
#define ARM_ep9312_Prop 0x400
#define ARM_iWMMXt_Prop 0x800
//chy 2005-09-19
#define ARM_PXA27X_Prop 0x1000
#define ARM_v7_Prop 0x2000
@ -591,47 +514,44 @@ typedef ARMul_State arm_core_t;
#ifdef __cplusplus
extern "C" {
#endif
extern void ARMul_EmulateInit (void);
extern void ARMul_Reset (ARMul_State * state);
extern void ARMul_EmulateInit();
extern void ARMul_Reset(ARMul_State* state);
#ifdef __cplusplus
}
#endif
extern ARMul_State *ARMul_NewState (ARMul_State * state);
extern ARMword ARMul_DoProg (ARMul_State * state);
extern ARMword ARMul_DoInstr (ARMul_State * state);
extern ARMul_State *ARMul_NewState(ARMul_State* state);
extern ARMword ARMul_DoProg(ARMul_State* state);
extern ARMword ARMul_DoInstr(ARMul_State* state);
/***************************************************************************\
* Definitons of things for event handling *
\***************************************************************************/
extern void ARMul_ScheduleEvent (ARMul_State * state, unsigned int delay,
unsigned (*func) ());
extern void ARMul_EnvokeEvent (ARMul_State * state);
extern unsigned int ARMul_Time (ARMul_State * state);
extern void ARMul_ScheduleEvent(ARMul_State* state, unsigned int delay, unsigned(*func) ());
extern void ARMul_EnvokeEvent(ARMul_State* state);
extern unsigned int ARMul_Time(ARMul_State* state);
/***************************************************************************\
* Useful support routines *
\***************************************************************************/
extern ARMword ARMul_GetReg (ARMul_State * state, unsigned mode,
unsigned reg);
extern void ARMul_SetReg (ARMul_State * state, unsigned mode, unsigned reg,
ARMword value);
extern ARMword ARMul_GetPC (ARMul_State * state);
extern ARMword ARMul_GetNextPC (ARMul_State * state);
extern void ARMul_SetPC (ARMul_State * state, ARMword value);
extern ARMword ARMul_GetR15 (ARMul_State * state);
extern void ARMul_SetR15 (ARMul_State * state, ARMword value);
extern ARMword ARMul_GetReg (ARMul_State* state, unsigned mode, unsigned reg);
extern void ARMul_SetReg (ARMul_State* state, unsigned mode, unsigned reg, ARMword value);
extern ARMword ARMul_GetPC(ARMul_State* state);
extern ARMword ARMul_GetNextPC(ARMul_State* state);
extern void ARMul_SetPC(ARMul_State* state, ARMword value);
extern ARMword ARMul_GetR15(ARMul_State* state);
extern void ARMul_SetR15(ARMul_State* state, ARMword value);
extern ARMword ARMul_GetCPSR (ARMul_State * state);
extern void ARMul_SetCPSR (ARMul_State * state, ARMword value);
extern ARMword ARMul_GetSPSR (ARMul_State * state, ARMword mode);
extern void ARMul_SetSPSR (ARMul_State * state, ARMword mode, ARMword value);
extern ARMword ARMul_GetCPSR(ARMul_State* state);
extern void ARMul_SetCPSR(ARMul_State* state, ARMword value);
extern ARMword ARMul_GetSPSR(ARMul_State* state, ARMword mode);
extern void ARMul_SetSPSR(ARMul_State* state, ARMword mode, ARMword value);
/***************************************************************************\
* Definitons of things to handle aborts *
\***************************************************************************/
extern void ARMul_Abort (ARMul_State * state, ARMword address);
extern void ARMul_Abort(ARMul_State* state, ARMword address);
#ifdef MODET
#define ARMul_ABORTWORD (state->TFlag ? 0xefffdfff : 0xefffffff) /* SWI -1 */
#define ARMul_PREFETCHABORT(address) if (state->AbortAddr == 1) \
@ -649,54 +569,40 @@ extern void ARMul_Abort (ARMul_State * state, ARMword address);
* Definitons of things in the memory interface *
\***************************************************************************/
extern unsigned ARMul_MemoryInit (ARMul_State * state,
unsigned int initmemsize);
extern void ARMul_MemoryExit (ARMul_State * state);
extern unsigned ARMul_MemoryInit(ARMul_State* state, unsigned int initmemsize);
extern void ARMul_MemoryExit(ARMul_State* state);
extern ARMword ARMul_LoadInstrS (ARMul_State * state, ARMword address,
ARMword isize);
extern ARMword ARMul_LoadInstrN (ARMul_State * state, ARMword address,
ARMword isize);
extern ARMword ARMul_LoadInstrS(ARMul_State* state, ARMword address, ARMword isize);
extern ARMword ARMul_LoadInstrN(ARMul_State* state, ARMword address, ARMword isize);
#ifdef __cplusplus
extern "C" {
#endif
extern ARMword ARMul_ReLoadInstr (ARMul_State * state, ARMword address,
ARMword isize);
extern ARMword ARMul_ReLoadInstr(ARMul_State* state, ARMword address, ARMword isize);
#ifdef __cplusplus
}
#endif
extern ARMword ARMul_LoadWordS (ARMul_State * state, ARMword address);
extern ARMword ARMul_LoadWordN (ARMul_State * state, ARMword address);
extern ARMword ARMul_LoadHalfWord (ARMul_State * state, ARMword address);
extern ARMword ARMul_LoadByte (ARMul_State * state, ARMword address);
extern ARMword ARMul_LoadWordS(ARMul_State* state, ARMword address);
extern ARMword ARMul_LoadWordN(ARMul_State* state, ARMword address);
extern ARMword ARMul_LoadHalfWord(ARMul_State* state, ARMword address);
extern ARMword ARMul_LoadByte(ARMul_State* state, ARMword address);
extern void ARMul_StoreWordS (ARMul_State * state, ARMword address,
ARMword data);
extern void ARMul_StoreWordN (ARMul_State * state, ARMword address,
ARMword data);
extern void ARMul_StoreHalfWord (ARMul_State * state, ARMword address,
ARMword data);
extern void ARMul_StoreByte (ARMul_State * state, ARMword address,
ARMword data);
extern void ARMul_StoreWordS(ARMul_State* state, ARMword address, ARMword data);
extern void ARMul_StoreWordN(ARMul_State* state, ARMword address, ARMword data);
extern void ARMul_StoreHalfWord(ARMul_State* state, ARMword address, ARMword data);
extern void ARMul_StoreByte(ARMul_State* state, ARMword address, ARMword data);
extern ARMword ARMul_SwapWord (ARMul_State * state, ARMword address,
ARMword data);
extern ARMword ARMul_SwapByte (ARMul_State * state, ARMword address,
ARMword data);
extern ARMword ARMul_SwapWord(ARMul_State* state, ARMword address, ARMword data);
extern ARMword ARMul_SwapByte(ARMul_State* state, ARMword address, ARMword data);
extern void ARMul_Icycles (ARMul_State * state, unsigned number,
ARMword address);
extern void ARMul_Ccycles (ARMul_State * state, unsigned number,
ARMword address);
extern void ARMul_Icycles(ARMul_State* state, unsigned number, ARMword address);
extern void ARMul_Ccycles(ARMul_State* state, unsigned number, ARMword address);
extern ARMword ARMul_ReadWord (ARMul_State * state, ARMword address);
extern ARMword ARMul_ReadByte (ARMul_State * state, ARMword address);
extern void ARMul_WriteWord (ARMul_State * state, ARMword address,
ARMword data);
extern void ARMul_WriteByte (ARMul_State * state, ARMword address,
ARMword data);
extern ARMword ARMul_ReadWord(ARMul_State* state, ARMword address);
extern ARMword ARMul_ReadByte(ARMul_State* state, ARMword address);
extern void ARMul_WriteWord(ARMul_State* state, ARMword address, ARMword data);
extern void ARMul_WriteByte(ARMul_State* state, ARMword address, ARMword data);
extern ARMword ARMul_MemAccess (ARMul_State * state, ARMword, ARMword,
extern ARMword ARMul_MemAccess(ARMul_State* state, ARMword, ARMword,
ARMword, ARMword, ARMword, ARMword, ARMword,
ARMword, ARMword, ARMword);
@ -739,66 +645,40 @@ extern ARMword ARMul_MemAccess (ARMul_State * state, ARMword, ARMword,
#define ARMul_CP15_DBCON_E1 0x000c
#define ARMul_CP15_DBCON_E0 0x0003
extern unsigned ARMul_CoProInit (ARMul_State * state);
extern void ARMul_CoProExit (ARMul_State * state);
extern void ARMul_CoProAttach (ARMul_State * state, unsigned number,
ARMul_CPInits * init, ARMul_CPExits * exit,
ARMul_LDCs * ldc, ARMul_STCs * stc,
ARMul_MRCs * mrc, ARMul_MCRs * mcr,
ARMul_MRRCs * mrrc, ARMul_MCRRs * mcrr,
ARMul_CDPs * cdp,
ARMul_CPReads * read, ARMul_CPWrites * write);
extern void ARMul_CoProDetach (ARMul_State * state, unsigned number);
extern unsigned ARMul_CoProInit(ARMul_State* state);
extern void ARMul_CoProExit(ARMul_State* state);
extern void ARMul_CoProAttach (ARMul_State* state, unsigned number,
ARMul_CPInits* init, ARMul_CPExits* exit,
ARMul_LDCs* ldc, ARMul_STCs* stc,
ARMul_MRCs* mrc, ARMul_MCRs* mcr,
ARMul_MRRCs* mrrc, ARMul_MCRRs* mcrr,
ARMul_CDPs* cdp,
ARMul_CPReads* read, ARMul_CPWrites* write);
extern void ARMul_CoProDetach(ARMul_State* state, unsigned number);
/***************************************************************************\
* Definitons of things in the host environment *
\***************************************************************************/
extern unsigned ARMul_OSInit (ARMul_State * state);
extern void ARMul_OSExit (ARMul_State * state);
extern unsigned ARMul_OSInit(ARMul_State* state);
extern void ARMul_OSExit(ARMul_State* state);
#ifdef __cplusplus
extern "C" {
#endif
extern unsigned ARMul_OSHandleSWI (ARMul_State * state, ARMword number);
extern unsigned ARMul_OSHandleSWI(ARMul_State* state, ARMword number);
#ifdef __cplusplus
}
#endif
extern ARMword ARMul_OSLastErrorP (ARMul_State * state);
extern ARMword ARMul_OSLastErrorP(ARMul_State* state);
extern ARMword ARMul_Debug (ARMul_State * state, ARMword pc, ARMword instr);
extern unsigned ARMul_OSException (ARMul_State * state, ARMword vector,
ARMword pc);
extern ARMword ARMul_Debug(ARMul_State* state, ARMword pc, ARMword instr);
extern unsigned ARMul_OSException(ARMul_State* state, ARMword vector, ARMword pc);
extern int rdi_log;
/***************************************************************************\
* Host-dependent stuff *
\***************************************************************************/
#ifdef macintosh
pascal void SpinCursor (short increment); /* copied from CursorCtl.h */
# define HOURGLASS SpinCursor( 1 )
# define HOURGLASS_RATE 1023 /* 2^n - 1 */
#endif
//teawater add for arm2x86 2005.02.14-------------------------------------------
/*ywc 2005-03-31*/
/*
#include "arm2x86.h"
#include "arm2x86_dp.h"
#include "arm2x86_movl.h"
#include "arm2x86_psr.h"
#include "arm2x86_shift.h"
#include "arm2x86_mem.h"
#include "arm2x86_mul.h"
#include "arm2x86_test.h"
#include "arm2x86_other.h"
#include "list.h"
#include "tb.h"
*/
enum ConditionCode {
EQ = 0,
NE = 1,
@ -851,32 +731,16 @@ enum ConditionCode {
#define ZBIT_SHIFT 30
#define CBIT_SHIFT 29
#define VBIT_SHIFT 28
#ifdef DBCT
//teawater change for local tb branch directly jump 2005.10.18------------------
#include "dbct/list.h"
#include "dbct/arm2x86.h"
#include "dbct/arm2x86_dp.h"
#include "dbct/arm2x86_movl.h"
#include "dbct/arm2x86_psr.h"
#include "dbct/arm2x86_shift.h"
#include "dbct/arm2x86_mem.h"
#include "dbct/arm2x86_mul.h"
#include "dbct/arm2x86_test.h"
#include "dbct/arm2x86_other.h"
#include "dbct/arm2x86_coproc.h"
#include "dbct/tb.h"
#endif
//AJ2D--------------------------------------------------------------------------
//AJ2D--------------------------------------------------------------------------
#define SKYEYE_OUTREGS(fd) { fprintf ((fd), "R %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,C %x,S %x,%x,%x,%x,%x,%x,%x,M %x,B %x,E %x,I %x,P %x,T %x,L %x,D %x,",\
state->Reg[0],state->Reg[1],state->Reg[2],state->Reg[3], \
state->Reg[4],state->Reg[5],state->Reg[6],state->Reg[7], \
state->Reg[8],state->Reg[9],state->Reg[10],state->Reg[11], \
state->Reg[12],state->Reg[13],state->Reg[14],state->Reg[15], \
state->Cpsr, state->Spsr[0], state->Spsr[1], state->Spsr[2],\
state->Cpsr, state->Spsr[0], state->Spsr[1], state->Spsr[2],\
state->Spsr[3],state->Spsr[4], state->Spsr[5], state->Spsr[6],\
state->Mode,state->Bank,state->ErrorCode,state->instr,state->pc,\
state->temp,state->loaded,state->decoded);}
state->Mode,state->Bank,state->ErrorCode,state->instr,state->pc,\
state->temp,state->loaded,state->decoded);}
#define SKYEYE_OUTMOREREGS(fd) { fprintf ((fd),"\
RUs %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,\
@ -914,17 +778,30 @@ RUn %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n",\
#define SA1110 0x6901b110
#define SA1100 0x4401a100
#define PXA250 0x69052100
#define PXA270 0x69054110
//#define PXA250 0x69052903
#define PXA250 0x69052100
#define PXA270 0x69054110
//#define PXA250 0x69052903
// 0x69052903; //PXA250 B1 from intel 278522-001.pdf
extern void ARMul_UndefInstr (ARMul_State *, ARMword);
extern void ARMul_FixCPSR (ARMul_State *, ARMword, ARMword);
extern void ARMul_FixSPSR (ARMul_State *, ARMword, ARMword);
extern void ARMul_ConsolePrint (ARMul_State *, const char *, ...);
extern void ARMul_SelectProcessor (ARMul_State *, unsigned);
extern void ARMul_UndefInstr(ARMul_State*, ARMword);
extern void ARMul_FixCPSR(ARMul_State*, ARMword, ARMword);
extern void ARMul_FixSPSR(ARMul_State*, ARMword, ARMword);
extern void ARMul_ConsolePrint(ARMul_State*, const char*, ...);
extern void ARMul_SelectProcessor(ARMul_State*, unsigned);
extern u8 ARMul_SignedSaturatedAdd8(u8, u8);
extern u8 ARMul_SignedSaturatedSub8(u8, u8);
extern u16 ARMul_SignedSaturatedAdd16(u16, u16);
extern u16 ARMul_SignedSaturatedSub16(u16, u16);
extern u8 ARMul_UnsignedSaturatedAdd8(u8, u8);
extern u16 ARMul_UnsignedSaturatedAdd16(u16, u16);
extern u8 ARMul_UnsignedSaturatedSub8(u8, u8);
extern u16 ARMul_UnsignedSaturatedSub16(u16, u16);
extern u8 ARMul_UnsignedAbsoluteDifference(u8, u8);
extern u32 ARMul_SignedSatQ(s32, u8, bool*);
extern u32 ARMul_UnsignedSatQ(s32, u8, bool*);
#define DIFF_LOG 0
#define SAVE_LOG 0

View file

@ -23,8 +23,6 @@
//extern ARMword isize;
#define DEBUG(...) DEBUG_LOG(ARM11, __VA_ARGS__)
/* Shift Opcodes. */
#define LSL 0
#define LSR 1
@ -36,7 +34,7 @@
#define ZBIT (1L << 30)
#define CBIT (1L << 29)
#define VBIT (1L << 28)
#define SBIT (1L << 27)
#define QBIT (1L << 27)
#define IBIT (1L << 7)
#define FBIT (1L << 6)
#define IFBITS (3L << 6)
@ -158,13 +156,14 @@
#define R15PCMODE (state->Reg[15] & (R15PCBITS | R15MODEBITS))
#define R15MODE (state->Reg[15] & R15MODEBITS)
#define ECC ((NFLAG << 31) | (ZFLAG << 30) | (CFLAG << 29) | (VFLAG << 28) | (SFLAG << 27))
#define ECC ((NFLAG << 31) | (ZFLAG << 30) | (CFLAG << 29) | (VFLAG << 28) | (QFLAG << 27))
#define EINT (IFFLAGS << 6)
#define ER15INT (IFFLAGS << 26)
#define EMODE (state->Mode)
#define EGEBITS (state->GEFlag & 0x000F0000)
#ifdef MODET
#define CPSR (ECC | EINT | EMODE | (TFLAG << 5))
#define CPSR (ECC | EGEBITS | (EFLAG << 9) | (AFLAG << 8) | EINT | (TFLAG << 5) | EMODE)
#else
#define CPSR (ECC | EINT | EMODE)
#endif
@ -485,7 +484,7 @@ tdstate;
* out-of-updated with the newer ISA.
* -- Michael.Kang
********************************************************************************/
#define UNDEF_WARNING WARN_LOG(ARM11, "undefined or unpredicted behavior for arm instruction.\n");
#define UNDEF_WARNING LOG_WARNING(Core_ARM11, "undefined or unpredicted behavior for arm instruction.");
/* Macros to scrutinize instructions. */
#define UNDEF_Test UNDEF_WARNING
@ -603,6 +602,7 @@ extern ARMword ARMul_SwitchMode (ARMul_State *, ARMword, ARMword);
extern void ARMul_MSRCpsr (ARMul_State *, ARMword, ARMword);
extern void ARMul_SubOverflow (ARMul_State *, ARMword, ARMword, ARMword);
extern void ARMul_AddOverflow (ARMul_State *, ARMword, ARMword, ARMword);
extern void ARMul_AddOverflowQ(ARMul_State*, ARMword, ARMword);
extern void ARMul_SubCarry (ARMul_State *, ARMword, ARMword, ARMword);
extern void ARMul_AddCarry (ARMul_State *, ARMword, ARMword, ARMword);
extern tdstate ARMul_ThumbDecode (ARMul_State *, ARMword, ARMword, ARMword *);

View file

@ -32,8 +32,7 @@
//ARMul_State* persistent_state; /* function calls from SoftFloat lib don't have an access to ARMul_state. */
unsigned
VFPInit (ARMul_State *state)
unsigned VFPInit(ARMul_State* state)
{
state->VFP[VFP_OFFSET(VFP_FPSID)] = VFP_FPSID_IMPLMEN<<24 | VFP_FPSID_SW<<23 | VFP_FPSID_SUBARCH<<16 |
VFP_FPSID_PARTNUM<<8 | VFP_FPSID_VARIANT<<4 | VFP_FPSID_REVISION;
@ -46,8 +45,7 @@ VFPInit (ARMul_State *state)
return 0;
}
unsigned
VFPMRC (ARMul_State * state, unsigned type, u32 instr, u32 * value)
unsigned VFPMRC(ARMul_State* state, unsigned type, u32 instr, u32* value)
{
/* MRC<c> <coproc>,<opc1>,<Rt>,<CRn>,<CRm>{,<opc2>} */
int CoProc = BITS (8, 11); /* 10 or 11 */
@ -61,10 +59,21 @@ VFPMRC (ARMul_State * state, unsigned type, u32 instr, u32 * value)
/* CRn/opc1 CRm/opc2 */
if (CoProc == 10 || CoProc == 11) {
#define VFP_MRC_TRANS
#include "core/arm/skyeye_common/vfp/vfpinstr.cpp"
#undef VFP_MRC_TRANS
if (CoProc == 10 || CoProc == 11)
{
if (OPC_1 == 0x0 && CRm == 0 && (OPC_2 & 0x3) == 0)
{
/* VMOV r to s */
/* Transfering Rt is not mandatory, as the value of interest is pointed by value */
VMOVBRS(state, BIT(20), Rt, BIT(7)|CRn<<1, value);
return ARMul_DONE;
}
if (OPC_1 == 0x7 && CRm == 0 && OPC_2 == 0)
{
VMRS(state, CRn, Rt, value);
return ARMul_DONE;
}
}
DEBUG("Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, CRn %x, CRm %x, OPC_2 %x\n",
instr, CoProc, OPC_1, Rt, CRn, CRm, OPC_2);
@ -72,8 +81,7 @@ VFPMRC (ARMul_State * state, unsigned type, u32 instr, u32 * value)
return ARMul_CANT;
}
unsigned
VFPMCR (ARMul_State * state, unsigned type, u32 instr, u32 value)
unsigned VFPMCR(ARMul_State* state, unsigned type, u32 instr, u32 value)
{
/* MCR<c> <coproc>,<opc1>,<Rt>,<CRn>,<CRm>{,<opc2>} */
int CoProc = BITS (8, 11); /* 10 or 11 */
@ -86,10 +94,33 @@ VFPMCR (ARMul_State * state, unsigned type, u32 instr, u32 value)
/* TODO check access permission */
/* CRn/opc1 CRm/opc2 */
if (CoProc == 10 || CoProc == 11) {
#define VFP_MCR_TRANS
#include "core/arm/skyeye_common/vfp/vfpinstr.cpp"
#undef VFP_MCR_TRANS
if (CoProc == 10 || CoProc == 11)
{
if (OPC_1 == 0x0 && CRm == 0 && (OPC_2 & 0x3) == 0)
{
/* VMOV s to r */
/* Transfering Rt is not mandatory, as the value of interest is pointed by value */
VMOVBRS(state, BIT(20), Rt, BIT(7)|CRn<<1, &value);
return ARMul_DONE;
}
if (OPC_1 == 0x7 && CRm == 0 && OPC_2 == 0)
{
VMSR(state, CRn, Rt);
return ARMul_DONE;
}
if ((OPC_1 & 0x4) == 0 && CoProc == 11 && CRm == 0)
{
VFP_DEBUG_UNIMPLEMENTED(VMOVBRC);
return ARMul_DONE;
}
if (CoProc == 11 && CRm == 0)
{
VFP_DEBUG_UNIMPLEMENTED(VMOVBCR);
return ARMul_DONE;
}
}
DEBUG("Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, CRn %x, CRm %x, OPC_2 %x\n",
instr, CoProc, OPC_1, Rt, CRn, CRm, OPC_2);
@ -97,8 +128,7 @@ VFPMCR (ARMul_State * state, unsigned type, u32 instr, u32 value)
return ARMul_CANT;
}
unsigned
VFPMRRC (ARMul_State * state, unsigned type, u32 instr, u32 * value1, u32 * value2)
unsigned VFPMRRC(ARMul_State* state, unsigned type, u32 instr, u32* value1, u32* value2)
{
/* MCRR<c> <coproc>,<opc1>,<Rt>,<Rt2>,<CRm> */
int CoProc = BITS (8, 11); /* 10 or 11 */
@ -107,10 +137,20 @@ VFPMRRC (ARMul_State * state, unsigned type, u32 instr, u32 * value1, u32 * valu
int Rt2 = BITS (16, 19);
int CRm = BITS (0, 3);
if (CoProc == 10 || CoProc == 11) {
#define VFP_MRRC_TRANS
#include "core/arm/skyeye_common/vfp/vfpinstr.cpp"
#undef VFP_MRRC_TRANS
if (CoProc == 10 || CoProc == 11)
{
if (CoProc == 10 && (OPC_1 & 0xD) == 1)
{
VFP_DEBUG_UNIMPLEMENTED(VMOVBRRSS);
return ARMul_DONE;
}
if (CoProc == 11 && (OPC_1 & 0xD) == 1)
{
/* Transfering Rt and Rt2 is not mandatory, as the value of interest is pointed by value1 and value2 */
VMOVBRRD(state, BIT(20), Rt, Rt2, BIT(5)<<4|CRm, value1, value2);
return ARMul_DONE;
}
}
DEBUG("Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, Rt2 %x, CRm %x\n",
instr, CoProc, OPC_1, Rt, Rt2, CRm);
@ -118,8 +158,7 @@ VFPMRRC (ARMul_State * state, unsigned type, u32 instr, u32 * value1, u32 * valu
return ARMul_CANT;
}
unsigned
VFPMCRR (ARMul_State * state, unsigned type, u32 instr, u32 value1, u32 value2)
unsigned VFPMCRR(ARMul_State* state, unsigned type, u32 instr, u32 value1, u32 value2)
{
/* MCRR<c> <coproc>,<opc1>,<Rt>,<Rt2>,<CRm> */
int CoProc = BITS (8, 11); /* 10 or 11 */
@ -132,10 +171,20 @@ VFPMCRR (ARMul_State * state, unsigned type, u32 instr, u32 value1, u32 value2)
/* CRn/opc1 CRm/opc2 */
if (CoProc == 11 || CoProc == 10) {
#define VFP_MCRR_TRANS
#include "core/arm/skyeye_common/vfp/vfpinstr.cpp"
#undef VFP_MCRR_TRANS
if (CoProc == 11 || CoProc == 10)
{
if (CoProc == 10 && (OPC_1 & 0xD) == 1)
{
VFP_DEBUG_UNIMPLEMENTED(VMOVBRRSS);
return ARMul_DONE;
}
if (CoProc == 11 && (OPC_1 & 0xD) == 1)
{
/* Transfering Rt and Rt2 is not mandatory, as the value of interest is pointed by value1 and value2 */
VMOVBRRD(state, BIT(20), Rt, Rt2, BIT(5)<<4|CRm, &value1, &value2);
return ARMul_DONE;
}
}
DEBUG("Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, Rt2 %x, CRm %x\n",
instr, CoProc, OPC_1, Rt, Rt2, CRm);
@ -143,8 +192,7 @@ VFPMCRR (ARMul_State * state, unsigned type, u32 instr, u32 value1, u32 value2)
return ARMul_CANT;
}
unsigned
VFPSTC (ARMul_State * state, unsigned type, u32 instr, u32 * value)
unsigned VFPSTC(ARMul_State* state, unsigned type, u32 instr, u32 * value)
{
/* STC{L}<c> <coproc>,<CRd>,[<Rn>],<option> */
int CoProc = BITS (8, 11); /* 10 or 11 */
@ -175,9 +223,17 @@ VFPSTC (ARMul_State * state, unsigned type, u32 instr, u32 * value)
}
#endif
#define VFP_STC_TRANS
#include "core/arm/skyeye_common/vfp/vfpinstr.cpp"
#undef VFP_STC_TRANS
if (P == 1 && W == 0)
{
return VSTR(state, type, instr, value);
}
if (P == 1 && U == 0 && W == 1 && Rn == 0xD)
{
return VPUSH(state, type, instr, value);
}
return VSTM(state, type, instr, value);
}
DEBUG("Can't identify %x, CoProc %x, CRd %x, Rn %x, imm8 %x, P %x, U %x, D %x, W %x\n",
instr, CoProc, CRd, Rn, imm8, P, U, D, W);
@ -185,8 +241,7 @@ VFPSTC (ARMul_State * state, unsigned type, u32 instr, u32 * value)
return ARMul_CANT;
}
unsigned
VFPLDC (ARMul_State * state, unsigned type, u32 instr, u32 value)
unsigned VFPLDC(ARMul_State* state, unsigned type, u32 instr, u32 value)
{
/* LDC{L}<c> <coproc>,<CRd>,[<Rn>] */
int CoProc = BITS (8, 11); /* 10 or 11 */
@ -204,10 +259,19 @@ VFPLDC (ARMul_State * state, unsigned type, u32 instr, u32 value)
DEBUG("In %s, UNDEFINED\n", __FUNCTION__);
exit(-1);
}
if (CoProc == 10 || CoProc == 11) {
#define VFP_LDC_TRANS
#include "core/arm/skyeye_common/vfp/vfpinstr.cpp"
#undef VFP_LDC_TRANS
if (CoProc == 10 || CoProc == 11)
{
if (P == 1 && W == 0)
{
return VLDR(state, type, instr, value);
}
if (P == 0 && U == 1 && W == 1 && Rn == 0xD)
{
return VPOP(state, type, instr, value);
}
return VLDM(state, type, instr, value);
}
DEBUG("Can't identify %x, CoProc %x, CRd %x, Rn %x, imm8 %x, P %x, U %x, D %x, W %x\n",
instr, CoProc, CRd, Rn, imm8, P, U, D, W);
@ -215,8 +279,7 @@ VFPLDC (ARMul_State * state, unsigned type, u32 instr, u32 value)
return ARMul_CANT;
}
unsigned
VFPCDP (ARMul_State * state, unsigned type, u32 instr)
unsigned VFPCDP(ARMul_State* state, unsigned type, u32 instr)
{
/* CDP<c> <coproc>,<opc1>,<CRd>,<CRn>,<CRm>,<opc2> */
int CoProc = BITS (8, 11); /* 10 or 11 */
@ -275,10 +338,83 @@ VFPCDP (ARMul_State * state, unsigned type, u32 instr)
/* CRn/opc1 CRm/opc2 */
if (CoProc == 10 || CoProc == 11) {
#define VFP_CDP_TRANS
#include "core/arm/skyeye_common/vfp/vfpinstr.cpp"
#undef VFP_CDP_TRANS
if (CoProc == 10 || CoProc == 11)
{
if ((OPC_1 & 0xB) == 0 && (OPC_2 & 0x2) == 0)
DBG("VMLA :\n");
if ((OPC_1 & 0xB) == 0 && (OPC_2 & 0x2) == 2)
DBG("VMLS :\n");
if ((OPC_1 & 0xB) == 1 && (OPC_2 & 0x2) == 2)
DBG("VNMLA :\n");
if ((OPC_1 & 0xB) == 1 && (OPC_2 & 0x2) == 0)
DBG("VNMLS :\n");
if ((OPC_1 & 0xB) == 2 && (OPC_2 & 0x2) == 2)
DBG("VNMUL :\n");
if ((OPC_1 & 0xB) == 2 && (OPC_2 & 0x2) == 0)
DBG("VMUL :\n");
if ((OPC_1 & 0xB) == 3 && (OPC_2 & 0x2) == 0)
DBG("VADD :\n");
if ((OPC_1 & 0xB) == 3 && (OPC_2 & 0x2) == 2)
DBG("VSUB :\n");
if ((OPC_1 & 0xB) == 0xA && (OPC_2 & 0x2) == 0)
DBG("VDIV :\n");
if ((OPC_1 & 0xB) == 0xB && BITS(4, 7) == 0)
{
unsigned int single = BIT(8) == 0;
unsigned int d = (single ? BITS(12,15)<<1 | BIT(22) : BITS(12,15) | BIT(22)<<4);
unsigned int imm;
instr = BITS(16, 19) << 4 | BITS(0, 3); /* FIXME dirty workaround to get a correct imm */
if (single)
imm = BIT(7)<<31 | (BIT(6)==0)<<30 | (BIT(6) ? 0x1f : 0)<<25 | BITS(0, 5)<<19;
else
imm = BIT(7)<<31 | (BIT(6)==0)<<30 | (BIT(6) ? 0xff : 0)<<22 | BITS(0, 5)<<16;
VMOVI(state, single, d, imm);
return ARMul_DONE;
}
if ((OPC_1 & 0xB) == 0xB && CRn == 0 && (OPC_2 & 0x6) == 0x2)
{
unsigned int single = BIT(8) == 0;
unsigned int d = (single ? BITS(12,15)<<1 | BIT(22) : BITS(12,15) | BIT(22)<<4);
unsigned int m = (single ? BITS( 0, 3)<<1 | BIT( 5) : BITS( 0, 3) | BIT( 5)<<4);;
VMOVR(state, single, d, m);
return ARMul_DONE;
}
if ((OPC_1 & 0xB) == 0xB && CRn == 0 && (OPC_2 & 0x7) == 6)
DBG("VABS :\n");
if ((OPC_1 & 0xB) == 0xB && CRn == 1 && (OPC_2 & 0x7) == 2)
DBG("VNEG :\n");
if ((OPC_1 & 0xB) == 0xB && CRn == 1 && (OPC_2 & 0x7) == 6)
DBG("VSQRT :\n");
if ((OPC_1 & 0xB) == 0xB && CRn == 4 && (OPC_2 & 0x2) == 2)
DBG("VCMP(1) :\n");
if ((OPC_1 & 0xB) == 0xB && CRn == 5 && (OPC_2 & 0x2) == 2 && CRm == 0)
DBG("VCMP(2) :\n");
if ((OPC_1 & 0xB) == 0xB && CRn == 7 && (OPC_2 & 0x6) == 6)
DBG("VCVT(BDS) :\n");
if ((OPC_1 & 0xB) == 0xB && CRn >= 0xA && (OPC_2 & 0x2) == 2)
DBG("VCVT(BFF) :\n");
if ((OPC_1 & 0xB) == 0xB && CRn > 7 && (OPC_2 & 0x2) == 2)
DBG("VCVT(BFI) :\n");
int exceptions = 0;
if (CoProc == 10)
@ -296,23 +432,93 @@ VFPCDP (ARMul_State * state, unsigned type, u32 instr)
/* ----------- MRC ------------ */
#define VFP_MRC_IMPL
#include "core/arm/skyeye_common/vfp/vfpinstr.cpp"
#undef VFP_MRC_IMPL
#define VFP_MRRC_IMPL
#include "core/arm/skyeye_common/vfp/vfpinstr.cpp"
#undef VFP_MRRC_IMPL
void VMOVBRS(ARMul_State* state, ARMword to_arm, ARMword t, ARMword n, ARMword* value)
{
DBG("VMOV(BRS) :\n");
if (to_arm)
{
DBG("\tr%d <= s%d=[%x]\n", t, n, state->ExtReg[n]);
*value = state->ExtReg[n];
}
else
{
DBG("\ts%d <= r%d=[%x]\n", n, t, *value);
state->ExtReg[n] = *value;
}
}
void VMRS(ARMul_State* state, ARMword reg, ARMword Rt, ARMword* value)
{
DBG("VMRS :");
if (reg == 1)
{
if (Rt != 15)
{
*value = state->VFP[VFP_OFFSET(VFP_FPSCR)];
DBG("\tr%d <= fpscr[%08x]\n", Rt, state->VFP[VFP_OFFSET(VFP_FPSCR)]);
}
else
{
*value = state->VFP[VFP_OFFSET(VFP_FPSCR)] ;
DBG("\tflags <= fpscr[%1xxxxxxxx]\n", state->VFP[VFP_OFFSET(VFP_FPSCR)]>>28);
}
}
else
{
switch (reg)
{
case 0:
*value = state->VFP[VFP_OFFSET(VFP_FPSID)];
DBG("\tr%d <= fpsid[%08x]\n", Rt, state->VFP[VFP_OFFSET(VFP_FPSID)]);
break;
case 6:
/* MVFR1, VFPv3 only ? */
DBG("\tr%d <= MVFR1 unimplemented\n", Rt);
break;
case 7:
/* MVFR0, VFPv3 only? */
DBG("\tr%d <= MVFR0 unimplemented\n", Rt);
break;
case 8:
*value = state->VFP[VFP_OFFSET(VFP_FPEXC)];
DBG("\tr%d <= fpexc[%08x]\n", Rt, state->VFP[VFP_OFFSET(VFP_FPEXC)]);
break;
default:
DBG("\tSUBARCHITECTURE DEFINED\n");
break;
}
}
}
void VMOVBRRD(ARMul_State* state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword* value1, ARMword* value2)
{
DBG("VMOV(BRRD) :\n");
if (to_arm)
{
DBG("\tr[%d-%d] <= s[%d-%d]=[%x-%x]\n", t2, t, n*2+1, n*2, state->ExtReg[n*2+1], state->ExtReg[n*2]);
*value2 = state->ExtReg[n*2+1];
*value1 = state->ExtReg[n*2];
}
else
{
DBG("\ts[%d-%d] <= r[%d-%d]=[%x-%x]\n", n*2+1, n*2, t2, t, *value2, *value1);
state->ExtReg[n*2+1] = *value2;
state->ExtReg[n*2] = *value1;
}
}
/* ----------- MCR ------------ */
#define VFP_MCR_IMPL
#include "core/arm/skyeye_common/vfp/vfpinstr.cpp"
#undef VFP_MCR_IMPL
#define VFP_MCRR_IMPL
#include "core/arm/skyeye_common/vfp/vfpinstr.cpp"
#undef VFP_MCRR_IMPL
void VMSR(ARMul_State* state, ARMword reg, ARMword Rt)
{
if (reg == 1)
{
DBG("VMSR :\tfpscr <= r%d=[%x]\n", Rt, state->Reg[Rt]);
state->VFP[VFP_OFFSET(VFP_FPSCR)] = state->Reg[Rt];
}
else if (reg == 8)
{
DBG("VMSR :\tfpexc <= r%d=[%x]\n", Rt, state->Reg[Rt]);
state->VFP[VFP_OFFSET(VFP_FPEXC)] = state->Reg[Rt];
}
}
/* Memory operation are not inlined, as old Interpreter and Fast interpreter
don't have the same memory operation interface.
@ -322,21 +528,342 @@ VFPCDP (ARMul_State * state, unsigned type, u32 instr)
of vfp instructions in old interpreter and fast interpreter are separate. */
/* ----------- STC ------------ */
#define VFP_STC_IMPL
#include "core/arm/skyeye_common/vfp/vfpinstr.cpp"
#undef VFP_STC_IMPL
int VSTR(ARMul_State* state, int type, ARMword instr, ARMword* value)
{
static int i = 0;
static int single_reg, add, d, n, imm32, regs;
if (type == ARMul_FIRST)
{
single_reg = BIT(8) == 0; /* Double precision */
add = BIT(23); /* */
imm32 = BITS(0,7)<<2; /* may not be used */
d = single_reg ? BITS(12, 15)<<1|BIT(22) : BIT(22)<<4|BITS(12, 15); /* Base register */
n = BITS(16, 19); /* destination register */
DBG("VSTR :\n");
i = 0;
regs = 1;
return ARMul_DONE;
}
else if (type == ARMul_DATA)
{
if (single_reg)
{
*value = state->ExtReg[d+i];
DBG("\taddr[?] <= s%d=[%x]\n", d+i, state->ExtReg[d+i]);
i++;
if (i < regs)
return ARMul_INC;
else
return ARMul_DONE;
}
else
{
/* FIXME Careful of endianness, may need to rework this */
*value = state->ExtReg[d*2+i];
DBG("\taddr[?] <= s[%d]=[%x]\n", d*2+i, state->ExtReg[d*2+i]);
i++;
if (i < regs*2)
return ARMul_INC;
else
return ARMul_DONE;
}
}
return -1;
}
int VPUSH(ARMul_State* state, int type, ARMword instr, ARMword* value)
{
static int i = 0;
static int single_regs, add, wback, d, n, imm32, regs;
if (type == ARMul_FIRST)
{
single_regs = BIT(8) == 0; /* Single precision */
d = single_regs ? BITS(12, 15)<<1|BIT(22) : BIT(22)<<4|BITS(12, 15); /* Base register */
imm32 = BITS(0,7)<<2; /* may not be used */
regs = single_regs ? BITS(0, 7) : BITS(1, 7); /* FSTMX if regs is odd */
DBG("VPUSH :\n");
DBG("\tsp[%x]", state->Reg[R13]);
state->Reg[R13] = state->Reg[R13] - imm32;
DBG("=>[%x]\n", state->Reg[R13]);
i = 0;
return ARMul_DONE;
}
else if (type == ARMul_DATA)
{
if (single_regs)
{
*value = state->ExtReg[d + i];
DBG("\taddr[?] <= s%d=[%x]\n", d+i, state->ExtReg[d + i]);
i++;
if (i < regs)
return ARMul_INC;
else
return ARMul_DONE;
}
else
{
/* FIXME Careful of endianness, may need to rework this */
*value = state->ExtReg[d*2 + i];
DBG("\taddr[?] <= s[%d]=[%x]\n", d*2 + i, state->ExtReg[d*2 + i]);
i++;
if (i < regs*2)
return ARMul_INC;
else
return ARMul_DONE;
}
}
return -1;
}
int VSTM(ARMul_State* state, int type, ARMword instr, ARMword* value)
{
static int i = 0;
static int single_regs, add, wback, d, n, imm32, regs;
if (type == ARMul_FIRST)
{
single_regs = BIT(8) == 0; /* Single precision */
add = BIT(23); /* */
wback = BIT(21); /* write-back */
d = single_regs ? BITS(12, 15)<<1|BIT(22) : BIT(22)<<4|BITS(12, 15); /* Base register */
n = BITS(16, 19); /* destination register */
imm32 = BITS(0,7) * 4; /* may not be used */
regs = single_regs ? BITS(0, 7) : BITS(0, 7)>>1; /* FSTMX if regs is odd */
DBG("VSTM :\n");
if (wback) {
state->Reg[n] = (add ? state->Reg[n] + imm32 : state->Reg[n] - imm32);
DBG("\twback r%d[%x]\n", n, state->Reg[n]);
}
i = 0;
return ARMul_DONE;
}
else if (type == ARMul_DATA)
{
if (single_regs)
{
*value = state->ExtReg[d + i];
DBG("\taddr[?] <= s%d=[%x]\n", d+i, state->ExtReg[d + i]);
i++;
if (i < regs)
return ARMul_INC;
else
return ARMul_DONE;
}
else
{
/* FIXME Careful of endianness, may need to rework this */
*value = state->ExtReg[d*2 + i];
DBG("\taddr[?] <= s[%d]=[%x]\n", d*2 + i, state->ExtReg[d*2 + i]);
i++;
if (i < regs*2)
return ARMul_INC;
else
return ARMul_DONE;
}
}
return -1;
}
/* ----------- LDC ------------ */
#define VFP_LDC_IMPL
#include "core/arm/skyeye_common/vfp/vfpinstr.cpp"
#undef VFP_LDC_IMPL
int VPOP(ARMul_State* state, int type, ARMword instr, ARMword value)
{
static int i = 0;
static int single_regs, add, wback, d, n, imm32, regs;
if (type == ARMul_FIRST)
{
single_regs = BIT(8) == 0; /* Single precision */
d = single_regs ? BITS(12, 15)<<1|BIT(22) : BIT(22)<<4|BITS(12, 15); /* Base register */
imm32 = BITS(0,7)<<2; /* may not be used */
regs = single_regs ? BITS(0, 7) : BITS(1, 7); /* FLDMX if regs is odd */
DBG("VPOP :\n");
DBG("\tsp[%x]", state->Reg[R13]);
state->Reg[R13] = state->Reg[R13] + imm32;
DBG("=>[%x]\n", state->Reg[R13]);
i = 0;
return ARMul_DONE;
}
else if (type == ARMul_TRANSFER)
{
return ARMul_DONE;
}
else if (type == ARMul_DATA)
{
if (single_regs)
{
state->ExtReg[d + i] = value;
DBG("\ts%d <= [%x]\n", d + i, value);
i++;
if (i < regs)
return ARMul_INC;
else
return ARMul_DONE;
}
else
{
/* FIXME Careful of endianness, may need to rework this */
state->ExtReg[d*2 + i] = value;
DBG("\ts%d <= [%x]\n", d*2 + i, value);
i++;
if (i < regs*2)
return ARMul_INC;
else
return ARMul_DONE;
}
}
return -1;
}
int VLDR(ARMul_State* state, int type, ARMword instr, ARMword value)
{
static int i = 0;
static int single_reg, add, d, n, imm32, regs;
if (type == ARMul_FIRST)
{
single_reg = BIT(8) == 0; /* Double precision */
add = BIT(23); /* */
imm32 = BITS(0,7)<<2; /* may not be used */
d = single_reg ? BITS(12, 15)<<1|BIT(22) : BIT(22)<<4|BITS(12, 15); /* Base register */
n = BITS(16, 19); /* destination register */
DBG("VLDR :\n");
i = 0;
regs = 1;
return ARMul_DONE;
}
else if (type == ARMul_TRANSFER)
{
return ARMul_DONE;
}
else if (type == ARMul_DATA)
{
if (single_reg)
{
state->ExtReg[d+i] = value;
DBG("\ts%d <= [%x]\n", d+i, value);
i++;
if (i < regs)
return ARMul_INC;
else
return ARMul_DONE;
}
else
{
/* FIXME Careful of endianness, may need to rework this */
state->ExtReg[d*2+i] = value;
DBG("\ts[%d] <= [%x]\n", d*2+i, value);
i++;
if (i < regs*2)
return ARMul_INC;
else
return ARMul_DONE;
}
}
return -1;
}
int VLDM(ARMul_State* state, int type, ARMword instr, ARMword value)
{
static int i = 0;
static int single_regs, add, wback, d, n, imm32, regs;
if (type == ARMul_FIRST)
{
single_regs = BIT(8) == 0; /* Single precision */
add = BIT(23); /* */
wback = BIT(21); /* write-back */
d = single_regs ? BITS(12, 15)<<1|BIT(22) : BIT(22)<<4|BITS(12, 15); /* Base register */
n = BITS(16, 19); /* destination register */
imm32 = BITS(0,7) * 4; /* may not be used */
regs = single_regs ? BITS(0, 7) : BITS(0, 7)>>1; /* FLDMX if regs is odd */
DBG("VLDM :\n");
if (wback) {
state->Reg[n] = (add ? state->Reg[n] + imm32 : state->Reg[n] - imm32);
DBG("\twback r%d[%x]\n", n, state->Reg[n]);
}
i = 0;
return ARMul_DONE;
}
else if (type == ARMul_DATA)
{
if (single_regs)
{
state->ExtReg[d + i] = value;
DBG("\ts%d <= [%x] addr[?]\n", d+i, state->ExtReg[d + i]);
i++;
if (i < regs)
return ARMul_INC;
else
return ARMul_DONE;
}
else
{
/* FIXME Careful of endianness, may need to rework this */
state->ExtReg[d*2 + i] = value;
DBG("\ts[%d] <= [%x] addr[?]\n", d*2 + i, state->ExtReg[d*2 + i]);
i++;
if (i < regs*2)
return ARMul_INC;
else
return ARMul_DONE;
}
}
return -1;
}
/* ----------- CDP ------------ */
#define VFP_CDP_IMPL
#include "core/arm/skyeye_common/vfp/vfpinstr.cpp"
#undef VFP_CDP_IMPL
void VMOVI(ARMul_State* state, ARMword single, ARMword d, ARMword imm)
{
DBG("VMOV(I) :\n");
if (single)
{
DBG("\ts%d <= [%x]\n", d, imm);
state->ExtReg[d] = imm;
}
else
{
/* Check endian please */
DBG("\ts[%d-%d] <= [%x-%x]\n", d*2+1, d*2, imm, 0);
state->ExtReg[d*2+1] = imm;
state->ExtReg[d*2] = 0;
}
}
void VMOVR(ARMul_State* state, ARMword single, ARMword d, ARMword m)
{
DBG("VMOV(R) :\n");
if (single)
{
DBG("\ts%d <= s%d[%x]\n", d, m, state->ExtReg[m]);
state->ExtReg[d] = state->ExtReg[m];
}
else
{
/* Check endian please */
DBG("\ts[%d-%d] <= s[%d-%d][%x-%x]\n", d*2+1, d*2, m*2+1, m*2, state->ExtReg[m*2+1], state->ExtReg[m*2]);
state->ExtReg[d*2+1] = state->ExtReg[m*2+1];
state->ExtReg[d*2] = state->ExtReg[m*2];
}
}
/* Miscellaneous functions */
int32_t vfp_get_float(arm_core_t* state, unsigned int reg)
@ -366,8 +893,6 @@ void vfp_put_double(arm_core_t* state, uint64_t val, unsigned int reg)
state->ExtReg[reg*2+1] = (uint32_t) (val>>32);
}
/*
* Process bitmask of exception conditions. (from vfpmodule.c)
*/

View file

@ -27,6 +27,12 @@
#include "core/arm/skyeye_common/vfp/vfp_helper.h" /* for references to cdp SoftFloat functions */
#define VFP_DEBUG_TRANSLATE DBG("in func %s, %x\n", __FUNCTION__, inst);
#define VFP_DEBUG_UNIMPLEMENTED(x) printf("in func %s, " #x " unimplemented\n", __FUNCTION__); exit(-1);
#define VFP_DEBUG_UNTESTED(x) printf("in func %s, " #x " untested\n", __FUNCTION__);
#define CHECK_VFP_ENABLED
#define CHECK_VFP_CDP_RET vfp_raise_exceptions(cpu, ret, inst_cream->instr, cpu->VFP[VFP_OFFSET(VFP_FPSCR)]); //if (ret == -1) {printf("VFP CDP FAILURE %x\n", inst_cream->instr); exit(-1);}
unsigned VFPInit (ARMul_State *state);
unsigned VFPMRC (ARMul_State * state, unsigned type, ARMword instr, ARMword * value);
unsigned VFPMCR (ARMul_State * state, unsigned type, ARMword instr, ARMword value);

File diff suppressed because it is too large Load diff

View file

@ -522,8 +522,7 @@ static s64 vfp_single_to_doubleintern(ARMul_State* state, s32 m, u32 fpscr) //ic
if (tm == VFP_QNAN)
vdd.significand |= VFP_DOUBLE_SIGNIFICAND_QNAN;
goto pack_nan;
}
else if (tm & VFP_ZERO)
} else if (tm & VFP_ZERO)
vdd.exponent = 0;
else
vdd.exponent = vsm.exponent + (1023 - 127);
@ -615,12 +614,12 @@ static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 f
exceptions |= FPSCR_IDC;
if (tm & VFP_NAN)
vsm.sign = 0;
vsm.sign = 1;
if (vsm.exponent >= 127 + 32) {
d = vsm.sign ? 0 : 0xffffffff;
exceptions = FPSCR_IOC;
} else if (vsm.exponent >= 127 - 1) {
} else if (vsm.exponent >= 127) {
int shift = 127 + 31 - vsm.exponent;
u32 rem, incr = 0;
@ -705,7 +704,7 @@ static u32 vfp_single_ftosi(ARMul_State* state, int sd, int unused, s32 m, u32 f
if (vsm.sign)
d = ~d;
exceptions |= FPSCR_IOC;
} else if (vsm.exponent >= 127 - 1) {
} else if (vsm.exponent >= 127) {
int shift = 127 + 31 - vsm.exponent;
u32 rem, incr = 0;
@ -1149,7 +1148,10 @@ static u32 vfp_single_fsub(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr)
/*
* Subtraction is addition with one sign inverted.
*/
return vfp_single_fadd(state, sd, sn, vfp_single_packed_negate(m), fpscr);
if (m != 0x7FC00000) // Only negate if m isn't NaN.
m = vfp_single_packed_negate(m);
return vfp_single_fadd(state, sd, sn, m, fpscr);
}
/*

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_types.h"
@ -47,7 +47,7 @@ void Stop() {
/// Initialize the core
int Init() {
NOTICE_LOG(MASTER_LOG, "initialized OK");
LOG_DEBUG(Core, "initialized OK");
disasm = new ARM_Disasm();
g_sys_core = new ARM_Interpreter();
@ -72,7 +72,7 @@ void Shutdown() {
delete g_app_core;
delete g_sys_core;
NOTICE_LOG(MASTER_LOG, "shutdown OK");
LOG_DEBUG(Core, "shutdown OK");
}
} // namespace

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once

View file

@ -1,5 +1,5 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <vector>
@ -67,7 +67,7 @@ s64 idledCycles;
static std::recursive_mutex externalEventSection;
// Warning: not included in save state.
void(*advanceCallback)(int cyclesExecuted) = NULL;
void(*advanceCallback)(int cyclesExecuted) = nullptr;
void SetClockFrequencyMHz(int cpuMhz)
{
@ -124,7 +124,7 @@ int RegisterEvent(const char *name, TimedCallback callback)
void AntiCrashCallback(u64 userdata, int cyclesLate)
{
ERROR_LOG(TIME, "Savestate broken: an unregistered event was called.");
LOG_CRITICAL(Core, "Savestate broken: an unregistered event was called.");
Core::Halt("invalid timing events");
}
@ -176,7 +176,7 @@ void Shutdown()
u64 GetTicks()
{
ERROR_LOG(TIME, "Unimplemented function!");
LOG_ERROR(Core, "Unimplemented function!");
return 0;
//return (u64)globalTimer + slicelength - currentMIPS->downcount;
}
@ -231,7 +231,7 @@ void ClearPendingEvents()
void AddEventToQueue(Event* ne)
{
Event* prev = NULL;
Event* prev = nullptr;
Event** pNext = &first;
for (;;)
{
@ -327,7 +327,7 @@ s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata)
}
if (!tsFirst)
{
tsLast = NULL;
tsLast = nullptr;
return result;
}
@ -433,7 +433,7 @@ void RemoveThreadsafeEvent(int event_type)
}
if (!tsFirst)
{
tsLast = NULL;
tsLast = nullptr;
return;
}
Event *prev = tsFirst;
@ -495,7 +495,7 @@ void MoveEvents()
AddEventToQueue(tsFirst);
tsFirst = next;
}
tsLast = NULL;
tsLast = nullptr;
// Move free events to threadsafe pool
while (allocatedTsEvents > 0 && eventPool)
@ -510,7 +510,7 @@ void MoveEvents()
void Advance()
{
ERROR_LOG(TIME, "Unimplemented function!");
LOG_ERROR(Core, "Unimplemented function!");
//int cyclesExecuted = slicelength - currentMIPS->downcount;
//globalTimer += cyclesExecuted;
//currentMIPS->downcount = slicelength;
@ -547,7 +547,7 @@ void LogPendingEvents()
void Idle(int maxIdle)
{
ERROR_LOG(TIME, "Unimplemented function!");
LOG_ERROR(Core, "Unimplemented function!");
//int cyclesDown = currentMIPS->downcount;
//if (maxIdle != 0 && cyclesDown > maxIdle)
// cyclesDown = maxIdle;
@ -614,7 +614,7 @@ void DoState(PointerWrap &p)
// These (should) be filled in later by the modules.
event_types.resize(n, EventType(AntiCrashCallback, "INVALID EVENT"));
p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **)NULL);
p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **)nullptr);
p.DoLinkedList<BaseEvent, GetNewTsEvent, FreeTsEvent, Event_DoState>(tsFirst, &tsLast);
p.Do(g_clock_rate_arm11);

View file

@ -1,5 +1,5 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once

View file

@ -1,246 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "common/common_types.h"
#include "common/string_util.h"
#include "common/bit_field.h"
#include "core/file_sys/file.h"
#include "core/file_sys/directory.h"
#include "core/mem_map.h"
#include "core/hle/kernel/kernel.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
// Path string type
enum LowPathType : u32 {
Invalid = 0,
Empty = 1,
Binary = 2,
Char = 3,
Wchar = 4
};
union Mode {
u32 hex;
BitField<0, 1, u32> read_flag;
BitField<1, 1, u32> write_flag;
BitField<2, 1, u32> create_flag;
};
class Path {
public:
Path():
type(Invalid)
{
}
Path(LowPathType type, u32 size, u32 pointer):
type(type)
{
switch (type) {
case Binary:
{
u8* data = Memory::GetPointer(pointer);
binary = std::vector<u8>(data, data + size);
break;
}
case Char:
{
const char* data = reinterpret_cast<const char*>(Memory::GetPointer(pointer));
string = std::string(data, size - 1); // Data is always null-terminated.
break;
}
case Wchar:
{
const char16_t* data = reinterpret_cast<const char16_t*>(Memory::GetPointer(pointer));
u16str = std::u16string(data, size/2 - 1); // Data is always null-terminated.
break;
}
}
}
LowPathType GetType() const {
return type;
}
/**
* Gets the string representation of the path for debugging
* @return String representation of the path for debugging
*/
const std::string DebugStr() const {
switch (GetType()) {
case Invalid:
return "[Invalid]";
case Empty:
return "[Empty]";
case Binary:
{
std::stringstream res;
res << "[Binary: ";
for (unsigned byte : binary)
res << std::hex << std::setw(2) << std::setfill('0') << byte;
res << ']';
return res.str();
}
case Char:
return "[Char: " + AsString() + ']';
case Wchar:
return "[Wchar: " + AsString() + ']';
default:
ERROR_LOG(KERNEL, "LowPathType cannot be converted to string!");
return {};
}
}
const std::string AsString() const {
switch (GetType()) {
case Char:
return string;
case Wchar:
return Common::UTF16ToUTF8(u16str);
case Empty:
return {};
default:
ERROR_LOG(KERNEL, "LowPathType cannot be converted to string!");
return {};
}
}
const std::u16string AsU16Str() const {
switch (GetType()) {
case Char:
return Common::UTF8ToUTF16(string);
case Wchar:
return u16str;
case Empty:
return {};
default:
ERROR_LOG(KERNEL, "LowPathType cannot be converted to u16string!");
return {};
}
}
const std::vector<u8> AsBinary() const {
switch (GetType()) {
case Binary:
return binary;
case Char:
return std::vector<u8>(string.begin(), string.end());
case Wchar:
return std::vector<u8>(u16str.begin(), u16str.end());
case Empty:
return {};
default:
ERROR_LOG(KERNEL, "LowPathType cannot be converted to binary!");
return {};
}
}
private:
LowPathType type;
std::vector<u8> binary;
std::string string;
std::u16string u16str;
};
class Archive : NonCopyable {
public:
/// Supported archive types
enum class IdCode : u32 {
RomFS = 0x00000003,
SaveData = 0x00000004,
ExtSaveData = 0x00000006,
SharedExtSaveData = 0x00000007,
SystemSaveData = 0x00000008,
SDMC = 0x00000009,
SDMCWriteOnly = 0x0000000A,
};
Archive() { }
virtual ~Archive() { }
/**
* Get the IdCode of the archive (e.g. RomFS, SaveData, etc.)
* @return IdCode of the archive
*/
virtual IdCode GetIdCode() const = 0;
/**
* Open a file specified by its path, using the specified mode
* @param path Path relative to the archive
* @param mode Mode to open the file with
* @return Opened file, or nullptr
*/
virtual std::unique_ptr<File> OpenFile(const Path& path, const Mode mode) const = 0;
/**
* Delete a file specified by its path
* @param path Path relative to the archive
* @return Whether the file could be deleted
*/
virtual bool DeleteFile(const FileSys::Path& path) const = 0;
/**
* Delete a directory specified by its path
* @param path Path relative to the archive
* @return Whether the directory could be deleted
*/
virtual bool DeleteDirectory(const FileSys::Path& path) const = 0;
/**
* Create a directory specified by its path
* @param path Path relative to the archive
* @return Whether the directory could be created
*/
virtual bool CreateDirectory(const Path& path) const = 0;
/**
* Open a directory specified by its path
* @param path Path relative to the archive
* @return Opened directory, or nullptr
*/
virtual std::unique_ptr<Directory> OpenDirectory(const Path& path) const = 0;
/**
* Read data from the archive
* @param offset Offset in bytes to start reading data from
* @param length Length in bytes of data to read from archive
* @param buffer Buffer to read data into
* @return Number of bytes read
*/
virtual size_t Read(const u64 offset, const u32 length, u8* buffer) const = 0;
/**
* Write data to the archive
* @param offset Offset in bytes to start writing data to
* @param length Length in bytes of data to write to archive
* @param buffer Buffer to write data from
* @param flush The flush parameters (0 == do not flush)
* @return Number of bytes written
*/
virtual size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) = 0;
/**
* Get the size of the archive in bytes
* @return Size of the archive in bytes
*/
virtual size_t GetSize() const = 0;
/**
* Set the size of the archive in bytes
*/
virtual void SetSize(const u64 size) = 0;
};
} // namespace FileSys

View file

@ -0,0 +1,243 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "common/common_types.h"
#include "common/string_util.h"
#include "common/bit_field.h"
#include "core/file_sys/file_backend.h"
#include "core/file_sys/directory_backend.h"
#include "core/mem_map.h"
#include "core/hle/kernel/kernel.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
// Path string type
enum LowPathType : u32 {
Invalid = 0,
Empty = 1,
Binary = 2,
Char = 3,
Wchar = 4
};
union Mode {
u32 hex;
BitField<0, 1, u32> read_flag;
BitField<1, 1, u32> write_flag;
BitField<2, 1, u32> create_flag;
};
class Path {
public:
Path() : type(Invalid) {
}
Path(const char* path) : type(Char), string(path) {
}
Path(LowPathType type, u32 size, u32 pointer) : type(type) {
switch (type) {
case Binary:
{
u8* data = Memory::GetPointer(pointer);
binary = std::vector<u8>(data, data + size);
break;
}
case Char:
{
const char* data = reinterpret_cast<const char*>(Memory::GetPointer(pointer));
string = std::string(data, size - 1); // Data is always null-terminated.
break;
}
case Wchar:
{
const char16_t* data = reinterpret_cast<const char16_t*>(Memory::GetPointer(pointer));
u16str = std::u16string(data, size/2 - 1); // Data is always null-terminated.
break;
}
default:
break;
}
}
LowPathType GetType() const {
return type;
}
/**
* Gets the string representation of the path for debugging
* @return String representation of the path for debugging
*/
const std::string DebugStr() const {
switch (GetType()) {
case Invalid:
return "[Invalid]";
case Empty:
return "[Empty]";
case Binary:
{
std::stringstream res;
res << "[Binary: ";
for (unsigned byte : binary)
res << std::hex << std::setw(2) << std::setfill('0') << byte;
res << ']';
return res.str();
}
case Char:
return "[Char: " + AsString() + ']';
case Wchar:
return "[Wchar: " + AsString() + ']';
}
}
const std::string AsString() const {
switch (GetType()) {
case Char:
return string;
case Wchar:
return Common::UTF16ToUTF8(u16str);
case Empty:
return {};
case Invalid:
case Binary:
// TODO(yuriks): Add assert
LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!");
return {};
}
}
const std::u16string AsU16Str() const {
switch (GetType()) {
case Char:
return Common::UTF8ToUTF16(string);
case Wchar:
return u16str;
case Empty:
return {};
case Invalid:
case Binary:
// TODO(yuriks): Add assert
LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!");
return {};
}
}
const std::vector<u8> AsBinary() const {
switch (GetType()) {
case Binary:
return binary;
case Char:
return std::vector<u8>(string.begin(), string.end());
case Wchar:
{
// use two u8 for each character of u16str
std::vector<u8> to_return(u16str.size() * 2);
for (size_t i = 0; i < u16str.size(); ++i) {
u16 tmp_char = u16str.at(i);
to_return[i*2] = (tmp_char & 0xFF00) >> 8;
to_return[i*2 + 1] = (tmp_char & 0x00FF);
}
return to_return;
}
case Empty:
return {};
case Invalid:
// TODO(yuriks): Add assert
LOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!");
return {};
}
}
private:
LowPathType type;
std::vector<u8> binary;
std::string string;
std::u16string u16str;
};
class ArchiveBackend : NonCopyable {
public:
virtual ~ArchiveBackend() {
}
/**
* Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
*/
virtual std::string GetName() const = 0;
/**
* Open a file specified by its path, using the specified mode
* @param path Path relative to the archive
* @param mode Mode to open the file with
* @return Opened file, or nullptr
*/
virtual std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const = 0;
/**
* Delete a file specified by its path
* @param path Path relative to the archive
* @return Whether the file could be deleted
*/
virtual bool DeleteFile(const Path& path) const = 0;
/**
* Rename a File specified by its path
* @param src_path Source path relative to the archive
* @param dest_path Destination path relative to the archive
* @return Whether rename succeeded
*/
virtual bool RenameFile(const Path& src_path, const Path& dest_path) const = 0;
/**
* Delete a directory specified by its path
* @param path Path relative to the archive
* @return Whether the directory could be deleted
*/
virtual bool DeleteDirectory(const Path& path) const = 0;
/**
* Create a file specified by its path
* @param path Path relative to the Archive
* @param size The size of the new file, filled with zeroes
* @return File creation result code
*/
virtual ResultCode CreateFile(const Path& path, u32 size) const = 0;
/**
* Create a directory specified by its path
* @param path Path relative to the archive
* @return Whether the directory could be created
*/
virtual bool CreateDirectory(const Path& path) const = 0;
/**
* Rename a Directory specified by its path
* @param src_path Source path relative to the archive
* @param dest_path Destination path relative to the archive
* @return Whether rename succeeded
*/
virtual bool RenameDirectory(const Path& src_path, const Path& dest_path) const = 0;
/**
* Open a directory specified by its path
* @param path Path relative to the archive
* @return Opened directory, or nullptr
*/
virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0;
};
} // namespace FileSys

View file

@ -1,8 +1,11 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include "common/common_types.h"
#include "common/make_unique.h"
#include "core/file_sys/archive_romfs.h"
#include "core/file_sys/directory_romfs.h"
@ -16,101 +19,47 @@ namespace FileSys {
Archive_RomFS::Archive_RomFS(const Loader::AppLoader& app_loader) {
// Load the RomFS from the app
if (Loader::ResultStatus::Success != app_loader.ReadRomFS(raw_data)) {
WARN_LOG(FILESYS, "Unable to read RomFS!");
LOG_ERROR(Service_FS, "Unable to read RomFS!");
}
}
Archive_RomFS::~Archive_RomFS() {
std::unique_ptr<FileBackend> Archive_RomFS::OpenFile(const Path& path, const Mode mode) const {
return Common::make_unique<File_RomFS>(this);
}
/**
* Open a file specified by its path, using the specified mode
* @param path Path relative to the archive
* @param mode Mode to open the file with
* @return Opened file, or nullptr
*/
std::unique_ptr<File> Archive_RomFS::OpenFile(const Path& path, const Mode mode) const {
return std::unique_ptr<File>(new File_RomFS);
}
/**
* Delete a file specified by its path
* @param path Path relative to the archive
* @return Whether the file could be deleted
*/
bool Archive_RomFS::DeleteFile(const FileSys::Path& path) const {
ERROR_LOG(FILESYS, "Attempted to delete a file from ROMFS.");
bool Archive_RomFS::DeleteFile(const Path& path) const {
LOG_WARNING(Service_FS, "Attempted to delete a file from ROMFS.");
return false;
}
/**
* Delete a directory specified by its path
* @param path Path relative to the archive
* @return Whether the directory could be deleted
*/
bool Archive_RomFS::DeleteDirectory(const FileSys::Path& path) const {
ERROR_LOG(FILESYS, "Attempted to delete a directory from ROMFS.");
bool Archive_RomFS::RenameFile(const Path& src_path, const Path& dest_path) const {
LOG_WARNING(Service_FS, "Attempted to rename a file within ROMFS.");
return false;
}
/**
* Create a directory specified by its path
* @param path Path relative to the archive
* @return Whether the directory could be created
*/
bool Archive_RomFS::DeleteDirectory(const Path& path) const {
LOG_WARNING(Service_FS, "Attempted to delete a directory from ROMFS.");
return false;
}
ResultCode Archive_RomFS::CreateFile(const Path& path, u32 size) const {
LOG_WARNING(Service_FS, "Attempted to create a file in ROMFS.");
// TODO: Verify error code
return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported, ErrorLevel::Permanent);
}
bool Archive_RomFS::CreateDirectory(const Path& path) const {
ERROR_LOG(FILESYS, "Attempted to create a directory in ROMFS.");
LOG_WARNING(Service_FS, "Attempted to create a directory in ROMFS.");
return false;
}
/**
* Open a directory specified by its path
* @param path Path relative to the archive
* @return Opened directory, or nullptr
*/
std::unique_ptr<Directory> Archive_RomFS::OpenDirectory(const Path& path) const {
return std::unique_ptr<Directory>(new Directory_RomFS);
bool Archive_RomFS::RenameDirectory(const Path& src_path, const Path& dest_path) const {
LOG_WARNING(Service_FS, "Attempted to rename a file within ROMFS.");
return false;
}
/**
* Read data from the archive
* @param offset Offset in bytes to start reading data from
* @param length Length in bytes of data to read from archive
* @param buffer Buffer to read data into
* @return Number of bytes read
*/
size_t Archive_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const {
DEBUG_LOG(FILESYS, "called offset=%llu, length=%d", offset, length);
memcpy(buffer, &raw_data[(u32)offset], length);
return length;
}
/**
* Write data to the archive
* @param offset Offset in bytes to start writing data to
* @param length Length in bytes of data to write to archive
* @param buffer Buffer to write data from
* @param flush The flush parameters (0 == do not flush)
* @return Number of bytes written
*/
size_t Archive_RomFS::Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) {
ERROR_LOG(FILESYS, "Attempted to write to ROMFS.");
return 0;
}
/**
* Get the size of the archive in bytes
* @return Size of the archive in bytes
*/
size_t Archive_RomFS::GetSize() const {
return sizeof(u8) * raw_data.size();
}
/**
* Set the size of the archive in bytes
*/
void Archive_RomFS::SetSize(const u64 size) {
ERROR_LOG(FILESYS, "Attempted to set the size of ROMFS");
std::unique_ptr<DirectoryBackend> Archive_RomFS::OpenDirectory(const Path& path) const {
return Common::make_unique<Directory_RomFS>();
}
} // namespace FileSys

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -8,7 +8,7 @@
#include "common/common_types.h"
#include "core/file_sys/archive.h"
#include "core/file_sys/archive_backend.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -17,16 +17,11 @@
namespace FileSys {
/// File system interface to the RomFS archive
class Archive_RomFS final : public Archive {
class Archive_RomFS final : public ArchiveBackend {
public:
Archive_RomFS(const Loader::AppLoader& app_loader);
~Archive_RomFS() override;
/**
* Get the IdCode of the archive (e.g. RomFS, SaveData, etc.)
* @return IdCode of the archive
*/
IdCode GetIdCode() const override { return IdCode::RomFS; }
std::string GetName() const override { return "RomFS"; }
/**
* Open a file specified by its path, using the specified mode
@ -34,21 +29,37 @@ public:
* @param mode Mode to open the file with
* @return Opened file, or nullptr
*/
std::unique_ptr<File> OpenFile(const Path& path, const Mode mode) const override;
std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override;
/**
* Delete a file specified by its path
* @param path Path relative to the archive
* @return Whether the file could be deleted
*/
bool DeleteFile(const FileSys::Path& path) const override;
bool DeleteFile(const Path& path) const override;
/**
* Rename a File specified by its path
* @param src_path Source path relative to the archive
* @param dest_path Destination path relative to the archive
* @return Whether rename succeeded
*/
bool RenameFile(const Path& src_path, const Path& dest_path) const override;
/**
* Delete a directory specified by its path
* @param path Path relative to the archive
* @return Whether the directory could be deleted
*/
bool DeleteDirectory(const FileSys::Path& path) const override;
bool DeleteDirectory(const Path& path) const override;
/**
* Create a file specified by its path
* @param path Path relative to the Archive
* @param size The size of the new file, filled with zeroes
* @return File creation result code
*/
ResultCode CreateFile(const Path& path, u32 size) const override;
/**
* Create a directory specified by its path
@ -57,44 +68,24 @@ public:
*/
bool CreateDirectory(const Path& path) const override;
/**
* Rename a Directory specified by its path
* @param src_path Source path relative to the archive
* @param dest_path Destination path relative to the archive
* @return Whether rename succeeded
*/
bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
/**
* Open a directory specified by its path
* @param path Path relative to the archive
* @return Opened directory, or nullptr
*/
std::unique_ptr<Directory> OpenDirectory(const Path& path) const override;
/**
* Read data from the archive
* @param offset Offset in bytes to start reading data from
* @param length Length in bytes of data to read from archive
* @param buffer Buffer to read data into
* @return Number of bytes read
*/
size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
/**
* Write data to the archive
* @param offset Offset in bytes to start writing data to
* @param length Length in bytes of data to write to archive
* @param buffer Buffer to write data from
* @param flush The flush parameters (0 == do not flush)
* @return Number of bytes written
*/
size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) override;
/**
* Get the size of the archive in bytes
* @return Size of the archive in bytes
*/
size_t GetSize() const override;
/**
* Set the size of the archive in bytes
*/
void SetSize(const u64 size) override;
std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
private:
friend class File_RomFS;
std::vector<u8> raw_data;
};

View file

@ -0,0 +1,33 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <sys/stat.h>
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/archive_savedata.h"
#include "core/file_sys/disk_archive.h"
#include "core/settings.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
Archive_SaveData::Archive_SaveData(const std::string& mount_point, u64 program_id)
: DiskArchive(mount_point + Common::StringFromFormat("%016X", program_id) + DIR_SEP) {
LOG_INFO(Service_FS, "Directory %s set as SaveData.", this->mount_point.c_str());
}
bool Archive_SaveData::Initialize() {
if (!FileUtil::CreateFullPath(mount_point)) {
LOG_ERROR(Service_FS, "Unable to create SaveData path.");
return false;
}
return true;
}
} // namespace FileSys

View file

@ -0,0 +1,31 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/file_sys/disk_archive.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
/// File system interface to the SaveData archive
class Archive_SaveData final : public DiskArchive {
public:
Archive_SaveData(const std::string& mount_point, u64 program_id);
/**
* Initialize the archive.
* @return true if it initialized successfully
*/
bool Initialize();
std::string GetName() const override { return "SaveData"; }
};
} // namespace FileSys

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <sys/stat.h>
@ -8,8 +8,7 @@
#include "common/file_util.h"
#include "core/file_sys/archive_sdmc.h"
#include "core/file_sys/directory_sdmc.h"
#include "core/file_sys/file_sdmc.h"
#include "core/file_sys/disk_archive.h"
#include "core/settings.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -17,131 +16,22 @@
namespace FileSys {
Archive_SDMC::Archive_SDMC(const std::string& mount_point) {
this->mount_point = mount_point;
DEBUG_LOG(FILESYS, "Directory %s set as SDMC.", mount_point.c_str());
Archive_SDMC::Archive_SDMC(const std::string& mount_point) : DiskArchive(mount_point) {
LOG_INFO(Service_FS, "Directory %s set as SDMC.", mount_point.c_str());
}
Archive_SDMC::~Archive_SDMC() {
}
/**
* Initialize the archive.
* @return true if it initialized successfully
*/
bool Archive_SDMC::Initialize() {
if (!Settings::values.use_virtual_sd) {
WARN_LOG(FILESYS, "SDMC disabled by config.");
LOG_WARNING(Service_FS, "SDMC disabled by config.");
return false;
}
if (!FileUtil::CreateFullPath(mount_point)) {
WARN_LOG(FILESYS, "Unable to create SDMC path.");
LOG_ERROR(Service_FS, "Unable to create SDMC path.");
return false;
}
return true;
}
/**
* Open a file specified by its path, using the specified mode
* @param path Path relative to the archive
* @param mode Mode to open the file with
* @return Opened file, or nullptr
*/
std::unique_ptr<File> Archive_SDMC::OpenFile(const Path& path, const Mode mode) const {
DEBUG_LOG(FILESYS, "called path=%s mode=%d", path.DebugStr().c_str(), mode);
File_SDMC* file = new File_SDMC(this, path, mode);
if (!file->Open())
return nullptr;
return std::unique_ptr<File>(file);
}
/**
* Delete a file specified by its path
* @param path Path relative to the archive
* @return Whether the file could be deleted
*/
bool Archive_SDMC::DeleteFile(const FileSys::Path& path) const {
return FileUtil::Delete(GetMountPoint() + path.AsString());
}
/**
* Delete a directory specified by its path
* @param path Path relative to the archive
* @return Whether the directory could be deleted
*/
bool Archive_SDMC::DeleteDirectory(const FileSys::Path& path) const {
return FileUtil::DeleteDir(GetMountPoint() + path.AsString());
}
/**
* Create a directory specified by its path
* @param path Path relative to the archive
* @return Whether the directory could be created
*/
bool Archive_SDMC::CreateDirectory(const Path& path) const {
return FileUtil::CreateDir(GetMountPoint() + path.AsString());
}
/**
* Open a directory specified by its path
* @param path Path relative to the archive
* @return Opened directory, or nullptr
*/
std::unique_ptr<Directory> Archive_SDMC::OpenDirectory(const Path& path) const {
DEBUG_LOG(FILESYS, "called path=%s", path.DebugStr().c_str());
Directory_SDMC* directory = new Directory_SDMC(this, path);
return std::unique_ptr<Directory>(directory);
}
/**
* Read data from the archive
* @param offset Offset in bytes to start reading archive from
* @param length Length in bytes to read data from archive
* @param buffer Buffer to read data into
* @return Number of bytes read
*/
size_t Archive_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const {
ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
return -1;
}
/**
* Write data to the archive
* @param offset Offset in bytes to start writing data to
* @param length Length in bytes of data to write to archive
* @param buffer Buffer to write data from
* @param flush The flush parameters (0 == do not flush)
* @return Number of bytes written
*/
size_t Archive_SDMC::Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) {
ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
return -1;
}
/**
* Get the size of the archive in bytes
* @return Size of the archive in bytes
*/
size_t Archive_SDMC::GetSize() const {
ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
return 0;
}
/**
* Set the size of the archive in bytes
*/
void Archive_SDMC::SetSize(const u64 size) {
ERROR_LOG(FILESYS, "(UNIMPLEMENTED)");
}
/**
* Getter for the path used for this Archive
* @return Mount point of that passthrough archive
*/
std::string Archive_SDMC::GetMountPoint() const {
return mount_point;
}
} // namespace FileSys

View file

@ -1,12 +1,12 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/file_sys/archive.h"
#include "core/file_sys/disk_archive.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -15,10 +15,9 @@
namespace FileSys {
/// File system interface to the SDMC archive
class Archive_SDMC final : public Archive {
class Archive_SDMC final : public DiskArchive {
public:
Archive_SDMC(const std::string& mount_point);
~Archive_SDMC() override;
/**
* Initialize the archive.
@ -26,86 +25,7 @@ public:
*/
bool Initialize();
/**
* Get the IdCode of the archive (e.g. RomFS, SaveData, etc.)
* @return IdCode of the archive
*/
IdCode GetIdCode() const override { return IdCode::SDMC; }
/**
* Open a file specified by its path, using the specified mode
* @param path Path relative to the archive
* @param mode Mode to open the file with
* @return Opened file, or nullptr
*/
std::unique_ptr<File> OpenFile(const Path& path, const Mode mode) const override;
/**
* Delete a file specified by its path
* @param path Path relative to the archive
* @return Whether the file could be deleted
*/
bool DeleteFile(const FileSys::Path& path) const override;
/**
* Delete a directory specified by its path
* @param path Path relative to the archive
* @return Whether the directory could be deleted
*/
bool DeleteDirectory(const FileSys::Path& path) const override;
/**
* Create a directory specified by its path
* @param path Path relative to the archive
* @return Whether the directory could be created
*/
bool CreateDirectory(const Path& path) const override;
/**
* Open a directory specified by its path
* @param path Path relative to the archive
* @return Opened directory, or nullptr
*/
std::unique_ptr<Directory> OpenDirectory(const Path& path) const override;
/**
* Read data from the archive
* @param offset Offset in bytes to start reading archive from
* @param length Length in bytes to read data from archive
* @param buffer Buffer to read data into
* @return Number of bytes read
*/
size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
/**
* Write data to the archive
* @param offset Offset in bytes to start writing data to
* @param length Length in bytes of data to write to archive
* @param buffer Buffer to write data from
* @param flush The flush parameters (0 == do not flush)
* @return Number of bytes written
*/
size_t Write(const u64 offset, const u32 length, const u32 flush, u8* buffer) override;
/**
* Get the size of the archive in bytes
* @return Size of the archive in bytes
*/
size_t GetSize() const override;
/**
* Set the size of the archive in bytes
*/
void SetSize(const u64 size) override;
/**
* Getter for the path used for this Archive
* @return Mount point of that passthrough archive
*/
std::string GetMountPoint() const;
private:
std::string mount_point;
std::string GetName() const override { return "SDMC"; }
};
} // namespace FileSys

View file

@ -0,0 +1,39 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <sys/stat.h>
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/archive_systemsavedata.h"
#include "core/file_sys/disk_archive.h"
#include "core/settings.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
static std::string GetSystemSaveDataPath(const std::string& mount_point, u64 save_id) {
u32 save_high = static_cast<u32>((save_id >> 32) & 0xFFFFFFFF);
u32 save_low = static_cast<u32>(save_id & 0xFFFFFFFF);
return Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), save_low, save_high);
}
Archive_SystemSaveData::Archive_SystemSaveData(const std::string& mount_point, u64 save_id)
: DiskArchive(GetSystemSaveDataPath(mount_point, save_id)) {
LOG_INFO(Service_FS, "Directory %s set as SystemSaveData.", this->mount_point.c_str());
}
bool Archive_SystemSaveData::Initialize() {
if (!FileUtil::CreateFullPath(mount_point)) {
LOG_ERROR(Service_FS, "Unable to create SystemSaveData path.");
return false;
}
return true;
}
} // namespace FileSys

View file

@ -0,0 +1,33 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/file_sys/disk_archive.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
/// File system interface to the SystemSaveData archive
/// TODO(Subv): This archive should point to a location in the NAND,
/// specifically nand:/data/<ID0>/sysdata/<SaveID-Low>/<SaveID-High>
class Archive_SystemSaveData final : public DiskArchive {
public:
Archive_SystemSaveData(const std::string& mount_point, u64 save_id);
/**
* Initialize the archive.
* @return true if it initialized successfully
*/
bool Initialize();
std::string GetName() const override { return "SystemSaveData"; }
};
} // namespace FileSys

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -36,10 +36,16 @@ static_assert(offsetof(Entry, extension) == 0x216, "Wrong offset for extension i
static_assert(offsetof(Entry, is_archive) == 0x21E, "Wrong offset for is_archive in Entry.");
static_assert(offsetof(Entry, file_size) == 0x220, "Wrong offset for file_size in Entry.");
class Directory : NonCopyable {
class DirectoryBackend : NonCopyable {
public:
Directory() { }
virtual ~Directory() { }
DirectoryBackend() { }
virtual ~DirectoryBackend() { }
/**
* Open the directory
* @return true if the directory opened correctly
*/
virtual bool Open() = 0;
/**
* List files contained in the directory

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_types.h"
@ -17,20 +17,14 @@ Directory_RomFS::Directory_RomFS() {
Directory_RomFS::~Directory_RomFS() {
}
/**
* List files contained in the directory
* @param count Number of entries to return at once in entries
* @param entries Buffer to read data into
* @return Number of entries listed
*/
bool Directory_RomFS::Open() {
return false;
}
u32 Directory_RomFS::Read(const u32 count, Entry* entries) {
return 0;
}
/**
* Close the directory
* @return true if the directory closed correctly
*/
bool Directory_RomFS::Close() const {
return false;
}

View file

@ -1,12 +1,12 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/directory_backend.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -14,11 +14,17 @@
namespace FileSys {
class Directory_RomFS final : public Directory {
class Directory_RomFS final : public DirectoryBackend {
public:
Directory_RomFS();
~Directory_RomFS() override;
/**
* Open the directory
* @return true if the directory opened correctly
*/
bool Open() override;
/**
* List files contained in the directory
* @param count Number of entries to return at once in entries

View file

@ -1,81 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include <sys/stat.h>
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/directory_sdmc.h"
#include "core/file_sys/archive_sdmc.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
Directory_SDMC::Directory_SDMC(const Archive_SDMC* archive, const Path& path) {
// TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
// the root directory we set while opening the archive.
// For example, opening /../../usr/bin can give the emulated program your installed programs.
std::string absolute_path = archive->GetMountPoint() + path.AsString();
FileUtil::ScanDirectoryTree(absolute_path, directory);
children_iterator = directory.children.begin();
}
Directory_SDMC::~Directory_SDMC() {
Close();
}
/**
* List files contained in the directory
* @param count Number of entries to return at once in entries
* @param entries Buffer to read data into
* @return Number of entries listed
*/
u32 Directory_SDMC::Read(const u32 count, Entry* entries) {
u32 entries_read = 0;
while (entries_read < count && children_iterator != directory.children.cend()) {
const FileUtil::FSTEntry& file = *children_iterator;
const std::string& filename = file.virtualName;
Entry& entry = entries[entries_read];
WARN_LOG(FILESYS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory);
// TODO(Link Mauve): use a proper conversion to UTF-16.
for (size_t j = 0; j < FILENAME_LENGTH; ++j) {
entry.filename[j] = filename[j];
if (!filename[j])
break;
}
FileUtil::SplitFilename83(filename, entry.short_name, entry.extension);
entry.is_directory = file.isDirectory;
entry.is_hidden = (filename[0] == '.');
entry.is_read_only = 0;
entry.file_size = file.size;
// We emulate a SD card where the archive bit has never been cleared, as it would be on
// most user SD cards.
// Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a
// file bit.
entry.is_archive = !file.isDirectory;
++entries_read;
++children_iterator;
}
return entries_read;
}
/**
* Close the directory
* @return true if the directory closed correctly
*/
bool Directory_SDMC::Close() const {
return true;
}
} // namespace FileSys

View file

@ -1,48 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/archive_sdmc.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
class Directory_SDMC final : public Directory {
public:
Directory_SDMC();
Directory_SDMC(const Archive_SDMC* archive, const Path& path);
~Directory_SDMC() override;
/**
* List files contained in the directory
* @param count Number of entries to return at once in entries
* @param entries Buffer to read data into
* @return Number of entries listed
*/
u32 Read(const u32 count, Entry* entries) override;
/**
* Close the directory
* @return true if the directory closed correctly
*/
bool Close() const override;
private:
u32 total_entries_in_directory;
FileUtil::FSTEntry directory;
// We need to remember the last entry we returned, so a subsequent call to Read will continue
// from the next one. This iterator will always point to the next unread entry.
std::vector<FileUtil::FSTEntry>::iterator children_iterator;
};
} // namespace FileSys

View file

@ -0,0 +1,188 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <sys/stat.h>
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/disk_archive.h"
#include "core/settings.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
std::unique_ptr<FileBackend> DiskArchive::OpenFile(const Path& path, const Mode mode) const {
LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
DiskFile* file = new DiskFile(this, path, mode);
if (!file->Open())
return nullptr;
return std::unique_ptr<FileBackend>(file);
}
bool DiskArchive::DeleteFile(const Path& path) const {
return FileUtil::Delete(GetMountPoint() + path.AsString());
}
bool DiskArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString());
}
bool DiskArchive::DeleteDirectory(const Path& path) const {
return FileUtil::DeleteDir(GetMountPoint() + path.AsString());
}
ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u32 size) const {
std::string full_path = GetMountPoint() + path.AsString();
if (FileUtil::Exists(full_path))
return ResultCode(ErrorDescription::AlreadyExists, ErrorModule::FS, ErrorSummary::NothingHappened, ErrorLevel::Info);
if (size == 0) {
FileUtil::CreateEmptyFile(full_path);
return RESULT_SUCCESS;
}
FileUtil::IOFile file(full_path, "wb");
// Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
// We do this by seeking to the right size, then writing a single null byte.
if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1)
return RESULT_SUCCESS;
return ResultCode(ErrorDescription::TooLarge, ErrorModule::FS, ErrorSummary::OutOfResource, ErrorLevel::Info);
}
bool DiskArchive::CreateDirectory(const Path& path) const {
return FileUtil::CreateDir(GetMountPoint() + path.AsString());
}
bool DiskArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
return FileUtil::Rename(GetMountPoint() + src_path.AsString(), GetMountPoint() + dest_path.AsString());
}
std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const {
LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str());
DiskDirectory* directory = new DiskDirectory(this, path);
if (!directory->Open())
return nullptr;
return std::unique_ptr<DirectoryBackend>(directory);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
DiskFile::DiskFile(const DiskArchive* archive, const Path& path, const Mode mode) {
// TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
// the root directory we set while opening the archive.
// For example, opening /../../etc/passwd can give the emulated program your users list.
this->path = archive->GetMountPoint() + path.AsString();
this->mode.hex = mode.hex;
this->archive = archive;
}
bool DiskFile::Open() {
if (!mode.create_flag && !FileUtil::Exists(path)) {
LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.", path.c_str());
return false;
}
std::string mode_string;
if (mode.create_flag)
mode_string = "w+";
else if (mode.write_flag)
mode_string = "r+"; // Files opened with Write access can be read from
else if (mode.read_flag)
mode_string = "r";
// Open the file in binary mode, to avoid problems with CR/LF on Windows systems
mode_string += "b";
file = new FileUtil::IOFile(path, mode_string.c_str());
return true;
}
size_t DiskFile::Read(const u64 offset, const u32 length, u8* buffer) const {
file->Seek(offset, SEEK_SET);
return file->ReadBytes(buffer, length);
}
size_t DiskFile::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const {
file->Seek(offset, SEEK_SET);
size_t written = file->WriteBytes(buffer, length);
if (flush)
file->Flush();
return written;
}
size_t DiskFile::GetSize() const {
return static_cast<size_t>(file->GetSize());
}
bool DiskFile::SetSize(const u64 size) const {
file->Resize(size);
file->Flush();
return true;
}
bool DiskFile::Close() const {
return file->Close();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
DiskDirectory::DiskDirectory(const DiskArchive* archive, const Path& path) {
// TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
// the root directory we set while opening the archive.
// For example, opening /../../usr/bin can give the emulated program your installed programs.
this->path = archive->GetMountPoint() + path.AsString();
this->archive = archive;
}
bool DiskDirectory::Open() {
if (!FileUtil::IsDirectory(path))
return false;
FileUtil::ScanDirectoryTree(path, directory);
children_iterator = directory.children.begin();
return true;
}
u32 DiskDirectory::Read(const u32 count, Entry* entries) {
u32 entries_read = 0;
while (entries_read < count && children_iterator != directory.children.cend()) {
const FileUtil::FSTEntry& file = *children_iterator;
const std::string& filename = file.virtualName;
Entry& entry = entries[entries_read];
LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, file.isDirectory);
// TODO(Link Mauve): use a proper conversion to UTF-16.
for (size_t j = 0; j < FILENAME_LENGTH; ++j) {
entry.filename[j] = filename[j];
if (!filename[j])
break;
}
FileUtil::SplitFilename83(filename, entry.short_name, entry.extension);
entry.is_directory = file.isDirectory;
entry.is_hidden = (filename[0] == '.');
entry.is_read_only = 0;
entry.file_size = file.size;
// We emulate a SD card where the archive bit has never been cleared, as it would be on
// most user SD cards.
// Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a
// file bit.
entry.is_archive = !file.isDirectory;
++entries_read;
++children_iterator;
}
return entries_read;
}
} // namespace FileSys

View file

@ -0,0 +1,103 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/archive_backend.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
/**
* Helper which implements a backend accessing the host machine's filesystem.
* This should be subclassed by concrete archive types, which will provide the
* base directory on the host filesystem and override any required functionality.
*/
class DiskArchive : public ArchiveBackend {
public:
DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
virtual std::string GetName() const = 0;
std::unique_ptr<FileBackend> OpenFile(const Path& path, const Mode mode) const override;
bool DeleteFile(const Path& path) const override;
bool RenameFile(const Path& src_path, const Path& dest_path) const override;
bool DeleteDirectory(const Path& path) const override;
ResultCode CreateFile(const Path& path, u32 size) const override;
bool CreateDirectory(const Path& path) const override;
bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
/**
* Getter for the path used for this Archive
* @return Mount point of that passthrough archive
*/
const std::string& GetMountPoint() const {
return mount_point;
}
protected:
std::string mount_point;
};
class DiskFile : public FileBackend {
public:
DiskFile();
DiskFile(const DiskArchive* archive, const Path& path, const Mode mode);
~DiskFile() override {
Close();
}
bool Open() override;
size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override;
size_t GetSize() const override;
bool SetSize(const u64 size) const override;
bool Close() const override;
void Flush() const override {
file->Flush();
}
protected:
const DiskArchive* archive;
std::string path;
Mode mode;
FileUtil::IOFile* file;
};
class DiskDirectory : public DirectoryBackend {
public:
DiskDirectory();
DiskDirectory(const DiskArchive* archive, const Path& path);
~DiskDirectory() override {
Close();
}
bool Open() override;
u32 Read(const u32 count, Entry* entries) override;
bool Close() const override {
return true;
}
protected:
const DiskArchive* archive;
std::string path;
u32 total_entries_in_directory;
FileUtil::FSTEntry directory;
// We need to remember the last entry we returned, so a subsequent call to Read will continue
// from the next one. This iterator will always point to the next unread entry.
std::vector<FileUtil::FSTEntry>::iterator children_iterator;
};
} // namespace FileSys

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -13,10 +13,10 @@
namespace FileSys {
class File : NonCopyable {
class FileBackend : NonCopyable {
public:
File() { }
virtual ~File() { }
FileBackend() { }
virtual ~FileBackend() { }
/**
* Open the file
@ -61,6 +61,11 @@ public:
* @return true if the file closed correctly
*/
virtual bool Close() const = 0;
/**
* Flushes the file
*/
virtual void Flush() const = 0;
};
} // namespace FileSys

View file

@ -1,74 +1,41 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_types.h"
#include "core/file_sys/file_romfs.h"
#include "core/file_sys/archive_romfs.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
File_RomFS::File_RomFS() {
}
File_RomFS::~File_RomFS() {
}
/**
* Open the file
* @return true if the file opened correctly
*/
bool File_RomFS::Open() {
return false;
return true;
}
/**
* Read data from the file
* @param offset Offset in bytes to start reading data from
* @param length Length in bytes of data to read from file
* @param buffer Buffer to read data into
* @return Number of bytes read
*/
size_t File_RomFS::Read(const u64 offset, const u32 length, u8* buffer) const {
return -1;
LOG_TRACE(Service_FS, "called offset=%llu, length=%d", offset, length);
memcpy(buffer, &archive->raw_data[(u32)offset], length);
return length;
}
/**
* Write data to the file
* @param offset Offset in bytes to start writing data to
* @param length Length in bytes of data to write to file
* @param flush The flush parameters (0 == do not flush)
* @param buffer Buffer to read data from
* @return Number of bytes written
*/
size_t File_RomFS::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const {
return -1;
LOG_WARNING(Service_FS, "Attempted to write to ROMFS.");
return 0;
}
/**
* Get the size of the file in bytes
* @return Size of the file in bytes
*/
size_t File_RomFS::GetSize() const {
return -1;
return sizeof(u8) * archive->raw_data.size();
}
/**
* Set the size of the file in bytes
* @param size New size of the file
* @return true if successful
*/
bool File_RomFS::SetSize(const u64 size) const {
LOG_WARNING(Service_FS, "Attempted to set the size of ROMFS");
return false;
}
/**
* Close the file
* @return true if the file closed correctly
*/
bool File_RomFS::Close() const {
return false;
}

View file

@ -1,12 +1,12 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/file_sys/file.h"
#include "core/file_sys/file_backend.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -14,10 +14,11 @@
namespace FileSys {
class File_RomFS final : public File {
class Archive_RomFS;
class File_RomFS final : public FileBackend {
public:
File_RomFS();
~File_RomFS() override;
File_RomFS(const Archive_RomFS* archive) : archive(archive) {}
/**
* Open the file
@ -62,6 +63,11 @@ public:
* @return true if the file closed correctly
*/
bool Close() const override;
void Flush() const override { }
private:
const Archive_RomFS* archive;
};
} // namespace FileSys

View file

@ -1,107 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include <sys/stat.h>
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/file_sdmc.h"
#include "core/file_sys/archive_sdmc.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
File_SDMC::File_SDMC(const Archive_SDMC* archive, const Path& path, const Mode mode) {
// TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
// the root directory we set while opening the archive.
// For example, opening /../../etc/passwd can give the emulated program your users list.
this->path = archive->GetMountPoint() + path.AsString();
this->mode.hex = mode.hex;
}
File_SDMC::~File_SDMC() {
Close();
}
/**
* Open the file
* @return true if the file opened correctly
*/
bool File_SDMC::Open() {
if (!mode.create_flag && !FileUtil::Exists(path)) {
ERROR_LOG(FILESYS, "Non-existing file %s cant be open without mode create.", path.c_str());
return false;
}
std::string mode_string;
if (mode.read_flag && mode.write_flag)
mode_string = "w+";
else if (mode.read_flag)
mode_string = "r";
else if (mode.write_flag)
mode_string = "w";
file = new FileUtil::IOFile(path, mode_string.c_str());
return true;
}
/**
* Read data from the file
* @param offset Offset in bytes to start reading data from
* @param length Length in bytes of data to read from file
* @param buffer Buffer to read data into
* @return Number of bytes read
*/
size_t File_SDMC::Read(const u64 offset, const u32 length, u8* buffer) const {
file->Seek(offset, SEEK_SET);
return file->ReadBytes(buffer, length);
}
/**
* Write data to the file
* @param offset Offset in bytes to start writing data to
* @param length Length in bytes of data to write to file
* @param flush The flush parameters (0 == do not flush)
* @param buffer Buffer to read data from
* @return Number of bytes written
*/
size_t File_SDMC::Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const {
file->Seek(offset, SEEK_SET);
size_t written = file->WriteBytes(buffer, length);
if (flush)
file->Flush();
return written;
}
/**
* Get the size of the file in bytes
* @return Size of the file in bytes
*/
size_t File_SDMC::GetSize() const {
return static_cast<size_t>(file->GetSize());
}
/**
* Set the size of the file in bytes
* @param size New size of the file
* @return true if successful
*/
bool File_SDMC::SetSize(const u64 size) const {
file->Resize(size);
file->Flush();
return true;
}
/**
* Close the file
* @return true if the file closed correctly
*/
bool File_SDMC::Close() const {
return file->Close();
}
} // namespace FileSys

View file

@ -1,75 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/file.h"
#include "core/file_sys/archive_sdmc.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// FileSys namespace
namespace FileSys {
class File_SDMC final : public File {
public:
File_SDMC();
File_SDMC(const Archive_SDMC* archive, const Path& path, const Mode mode);
~File_SDMC() override;
/**
* Open the file
* @return true if the file opened correctly
*/
bool Open() override;
/**
* Read data from the file
* @param offset Offset in bytes to start reading data from
* @param length Length in bytes of data to read from file
* @param buffer Buffer to read data into
* @return Number of bytes read
*/
size_t Read(const u64 offset, const u32 length, u8* buffer) const override;
/**
* Write data to the file
* @param offset Offset in bytes to start writing data to
* @param length Length in bytes of data to write to file
* @param flush The flush parameters (0 == do not flush)
* @param buffer Buffer to read data from
* @return Number of bytes written
*/
size_t Write(const u64 offset, const u32 length, const u32 flush, const u8* buffer) const override;
/**
* Get the size of the file in bytes
* @return Size of the file in bytes
*/
size_t GetSize() const override;
/**
* Set the size of the file in bytes
* @param size New size of the file
* @return true if successful
*/
bool SetSize(const u64 size) const override;
/**
* Close the file
* @return true if the file closed correctly
*/
bool Close() const override;
private:
std::string path;
Mode mode;
FileUtil::IOFile* file;
};
} // namespace FileSys

View file

@ -1,8 +1,9 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_types.h"
#include "common/log.h"
#include "core/hle/config_mem.h"
@ -54,7 +55,7 @@ inline void Read(T &var, const u32 addr) {
break;
default:
ERROR_LOG(HLE, "unknown addr=0x%08X", addr);
LOG_ERROR(Kernel, "unknown addr=0x%08X", addr);
}
}

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/coprocessor.h"

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -114,6 +114,20 @@ template<s32 func(u32*, const char*)> void Wrap() {
FuncReturn(retval);
}
template<s32 func(u32*, s32, s32)> void Wrap() {
u32 param_1 = 0;
u32 retval = func(&param_1, PARAM(1), PARAM(2));
Core::g_app_core->SetReg(1, param_1);
FuncReturn(retval);
}
template<s32 func(s32*, u32, s32)> void Wrap() {
s32 param_1 = 0;
u32 retval = func(&param_1, PARAM(1), PARAM(2));
Core::g_app_core->SetReg(1, param_1);
FuncReturn(retval);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u32

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <vector>
@ -8,6 +8,8 @@
#include "core/hle/hle.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/service.h"
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/cfg/cfg.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -20,7 +22,7 @@ bool g_reschedule = false; ///< If true, immediately reschedules the CPU to a n
const FunctionDef* GetSVCInfo(u32 opcode) {
u32 func_num = opcode & 0xFFFFFF; // 8 bits
if (func_num > 0xFF) {
ERROR_LOG(HLE,"unknown svc=0x%02X", func_num);
LOG_ERROR(Kernel_SVC,"unknown svc=0x%02X", func_num);
return nullptr;
}
return &g_module_db[0].func_table[func_num];
@ -35,15 +37,21 @@ void CallSVC(u32 opcode) {
if (info->func) {
info->func();
} else {
ERROR_LOG(HLE, "unimplemented SVC function %s(..)", info->name.c_str());
LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name.c_str());
}
}
void Reschedule(const char *reason) {
#ifdef _DEBUG
_dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason.");
#endif
_dbg_assert_msg_(Kernel, reason != 0 && strlen(reason) < 256, "Reschedule: Invalid or too long reason.");
// TODO(bunnei): It seems that games depend on some CPU execution time elapsing during HLE
// routines. This simulates that time by artificially advancing the number of CPU "ticks".
// The value was chosen empirically, it seems to work well enough for everything tested, but
// is likely not ideal. We should find a more accurate way to simulate timing with HLE.
Core::g_app_core->AddTicks(4000);
Core::g_app_core->PrepareReschedule();
g_reschedule = true;
}
@ -58,18 +66,22 @@ void RegisterAllModules() {
void Init() {
Service::Init();
Service::FS::ArchiveInit();
Service::CFG::CFGInit();
RegisterAllModules();
NOTICE_LOG(HLE, "initialized OK");
LOG_DEBUG(Kernel, "initialized OK");
}
void Shutdown() {
Service::CFG::CFGShutdown();
Service::FS::ArchiveShutdown();
Service::Shutdown();
g_module_db.clear();
NOTICE_LOG(HLE, "shutdown OK");
LOG_DEBUG(Kernel, "shutdown OK");
}
} // namespace

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_types.h"
@ -20,16 +20,10 @@ public:
std::string GetTypeName() const override { return "Arbiter"; }
std::string GetName() const override { return name; }
static Kernel::HandleType GetStaticHandleType() { return HandleType::AddressArbiter; }
Kernel::HandleType GetHandleType() const override { return HandleType::AddressArbiter; }
static const HandleType HANDLE_TYPE = HandleType::AddressArbiter;
HandleType GetHandleType() const override { return HANDLE_TYPE; }
std::string name; ///< Name of address arbiter object (optional)
ResultVal<bool> WaitSynchronization() override {
// TODO(bunnei): ImplementMe
ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
return UnimplementedFunction(ErrorModule::OS);
}
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -53,13 +47,13 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3
// Wait current thread (acquire the arbiter)...
case ArbitrationType::WaitIfLessThan:
if ((s32)Memory::Read32(address) <= value) {
Kernel::WaitCurrentThread(WAITTYPE_ARB, handle);
Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address);
HLE::Reschedule(__func__);
}
break;
default:
ERROR_LOG(KERNEL, "unknown type=%d", type);
LOG_ERROR(Kernel, "unknown type=%d", type);
return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage);
}
return RESULT_SUCCESS;
@ -68,7 +62,8 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3
/// Create an address arbiter
AddressArbiter* CreateAddressArbiter(Handle& handle, const std::string& name) {
AddressArbiter* address_arbiter = new AddressArbiter;
handle = Kernel::g_object_pool.Create(address_arbiter);
// TOOD(yuriks): Fix error reporting
handle = Kernel::g_handle_table.Create(address_arbiter).ValueOr(INVALID_HANDLE);
address_arbiter->name = name;
return address_arbiter;
}

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once

View file

@ -1,429 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/math_util.h"
#include "core/file_sys/archive.h"
#include "core/file_sys/archive_sdmc.h"
#include "core/file_sys/directory.h"
#include "core/hle/kernel/archive.h"
#include "core/hle/result.h"
#include "core/hle/service/service.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Kernel namespace
namespace Kernel {
// Command to access archive file
enum class FileCommand : u32 {
Dummy1 = 0x000100C6,
Control = 0x040100C4,
OpenSubFile = 0x08010100,
Read = 0x080200C2,
Write = 0x08030102,
GetSize = 0x08040000,
SetSize = 0x08050080,
GetAttributes = 0x08060000,
SetAttributes = 0x08070040,
Close = 0x08080000,
Flush = 0x08090000,
};
// Command to access directory
enum class DirectoryCommand : u32 {
Dummy1 = 0x000100C6,
Control = 0x040100C4,
Read = 0x08010042,
Close = 0x08020000,
};
class Archive : public Object {
public:
std::string GetTypeName() const override { return "Archive"; }
std::string GetName() const override { return name; }
static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; }
Kernel::HandleType GetHandleType() const override { return HandleType::Archive; }
std::string name; ///< Name of archive (optional)
FileSys::Archive* backend; ///< Archive backend interface
ResultVal<bool> SyncRequest() override {
u32* cmd_buff = Service::GetCommandBuffer();
FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
switch (cmd) {
// Read from archive...
case FileCommand::Read:
{
u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
u32 length = cmd_buff[3];
u32 address = cmd_buff[5];
// Number of bytes read
cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
break;
}
// Write to archive...
case FileCommand::Write:
{
u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
u32 length = cmd_buff[3];
u32 flush = cmd_buff[4];
u32 address = cmd_buff[6];
// Number of bytes written
cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
break;
}
case FileCommand::GetSize:
{
u64 filesize = (u64) backend->GetSize();
cmd_buff[2] = (u32) filesize; // Lower word
cmd_buff[3] = (u32) (filesize >> 32); // Upper word
break;
}
case FileCommand::SetSize:
{
backend->SetSize(cmd_buff[1] | ((u64)cmd_buff[2] << 32));
break;
}
case FileCommand::Close:
{
DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
CloseArchive(backend->GetIdCode());
break;
}
// Unknown command...
default:
{
ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
return UnimplementedFunction(ErrorModule::FS);
}
}
cmd_buff[1] = 0; // No error
return MakeResult<bool>(false);
}
ResultVal<bool> WaitSynchronization() override {
// TODO(bunnei): ImplementMe
ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
return UnimplementedFunction(ErrorModule::FS);
}
};
class File : public Object {
public:
std::string GetTypeName() const override { return "File"; }
std::string GetName() const override { return path.DebugStr(); }
static Kernel::HandleType GetStaticHandleType() { return HandleType::File; }
Kernel::HandleType GetHandleType() const override { return HandleType::File; }
FileSys::Path path; ///< Path of the file
std::unique_ptr<FileSys::File> backend; ///< File backend interface
ResultVal<bool> SyncRequest() override {
u32* cmd_buff = Service::GetCommandBuffer();
FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]);
switch (cmd) {
// Read from file...
case FileCommand::Read:
{
u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
u32 length = cmd_buff[3];
u32 address = cmd_buff[5];
DEBUG_LOG(KERNEL, "Read %s %s: offset=0x%llx length=%d address=0x%x",
GetTypeName().c_str(), GetName().c_str(), offset, length, address);
cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address));
break;
}
// Write to file...
case FileCommand::Write:
{
u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32;
u32 length = cmd_buff[3];
u32 flush = cmd_buff[4];
u32 address = cmd_buff[6];
DEBUG_LOG(KERNEL, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x",
GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush);
cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address));
break;
}
case FileCommand::GetSize:
{
DEBUG_LOG(KERNEL, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str());
u64 size = backend->GetSize();
cmd_buff[2] = (u32)size;
cmd_buff[3] = size >> 32;
break;
}
case FileCommand::SetSize:
{
u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32);
DEBUG_LOG(KERNEL, "SetSize %s %s size=%llu",
GetTypeName().c_str(), GetName().c_str(), size);
backend->SetSize(size);
break;
}
case FileCommand::Close:
{
DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
Kernel::g_object_pool.Destroy<File>(GetHandle());
break;
}
// Unknown command...
default:
ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
ResultCode error = UnimplementedFunction(ErrorModule::FS);
cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
return error;
}
cmd_buff[1] = 0; // No error
return MakeResult<bool>(false);
}
ResultVal<bool> WaitSynchronization() override {
// TODO(bunnei): ImplementMe
ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
return UnimplementedFunction(ErrorModule::FS);
}
};
class Directory : public Object {
public:
std::string GetTypeName() const override { return "Directory"; }
std::string GetName() const override { return path.DebugStr(); }
static Kernel::HandleType GetStaticHandleType() { return HandleType::Directory; }
Kernel::HandleType GetHandleType() const override { return HandleType::Directory; }
FileSys::Path path; ///< Path of the directory
std::unique_ptr<FileSys::Directory> backend; ///< File backend interface
ResultVal<bool> SyncRequest() override {
u32* cmd_buff = Service::GetCommandBuffer();
DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]);
switch (cmd) {
// Read from directory...
case DirectoryCommand::Read:
{
u32 count = cmd_buff[1];
u32 address = cmd_buff[3];
auto entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address));
DEBUG_LOG(KERNEL, "Read %s %s: count=%d",
GetTypeName().c_str(), GetName().c_str(), count);
// Number of entries actually read
cmd_buff[2] = backend->Read(count, entries);
break;
}
case DirectoryCommand::Close:
{
DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str());
Kernel::g_object_pool.Destroy<Directory>(GetHandle());
break;
}
// Unknown command...
default:
ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd);
ResultCode error = UnimplementedFunction(ErrorModule::FS);
cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that.
return error;
}
cmd_buff[1] = 0; // No error
return MakeResult<bool>(false);
}
ResultVal<bool> WaitSynchronization() override {
// TODO(bunnei): ImplementMe
ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
return UnimplementedFunction(ErrorModule::FS);
}
};
////////////////////////////////////////////////////////////////////////////////////////////////////
std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode
ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code) {
auto itr = g_archive_map.find(id_code);
if (itr == g_archive_map.end()) {
return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
ErrorSummary::NotFound, ErrorLevel::Permanent);
}
return MakeResult<Handle>(itr->second);
}
ResultCode CloseArchive(FileSys::Archive::IdCode id_code) {
auto itr = g_archive_map.find(id_code);
if (itr == g_archive_map.end()) {
ERROR_LOG(KERNEL, "Cannot close archive %d, does not exist!", (int)id_code);
return InvalidHandle(ErrorModule::FS);
}
INFO_LOG(KERNEL, "Closed archive %d", (int) id_code);
return RESULT_SUCCESS;
}
/**
* Mounts an archive
* @param archive Pointer to the archive to mount
*/
ResultCode MountArchive(Archive* archive) {
FileSys::Archive::IdCode id_code = archive->backend->GetIdCode();
ResultVal<Handle> archive_handle = OpenArchive(id_code);
if (archive_handle.Succeeded()) {
ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code);
return archive_handle.Code();
}
g_archive_map[id_code] = archive->GetHandle();
INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str());
return RESULT_SUCCESS;
}
ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name) {
Archive* archive = new Archive;
Handle handle = Kernel::g_object_pool.Create(archive);
archive->name = name;
archive->backend = backend;
ResultCode result = MountArchive(archive);
if (result.IsError()) {
return result;
}
return RESULT_SUCCESS;
}
ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) {
// TODO(bunnei): Binary type files get a raw file pointer to the archive. Currently, we create
// the archive file handles at app loading, and then keep them persistent throughout execution.
// Archives file handles are just reused and not actually freed until emulation shut down.
// Verify if real hardware works this way, or if new handles are created each time
if (path.GetType() == FileSys::Binary)
// TODO(bunnei): FixMe - this is a hack to compensate for an incorrect FileSys backend
// design. While the functionally of this is OK, our implementation decision to separate
// normal files from archive file pointers is very likely wrong.
// See https://github.com/citra-emu/citra/issues/205
return MakeResult<Handle>(archive_handle);
File* file = new File;
Handle handle = Kernel::g_object_pool.Create(file);
Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle);
if (archive == nullptr) {
return InvalidHandle(ErrorModule::FS);
}
file->path = path;
file->backend = archive->backend->OpenFile(path, mode);
if (!file->backend) {
return ResultCode(ErrorDescription::NotFound, ErrorModule::FS,
ErrorSummary::NotFound, ErrorLevel::Permanent);
}
return MakeResult<Handle>(handle);
}
/**
* Delete a File from an Archive
* @param archive_handle Handle to an open Archive object
* @param path Path to the File inside of the Archive
* @return Whether deletion succeeded
*/
Result DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path) {
Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
if (archive == nullptr)
return -1;
if (archive->backend->DeleteFile(path))
return 0;
return -1;
}
/**
* Delete a Directory from an Archive
* @param archive_handle Handle to an open Archive object
* @param path Path to the Directory inside of the Archive
* @return Whether deletion succeeded
*/
Result DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) {
Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
if (archive == nullptr)
return -1;
if (archive->backend->DeleteDirectory(path))
return 0;
return -1;
}
/**
* Create a Directory from an Archive
* @param archive_handle Handle to an open Archive object
* @param path Path to the Directory inside of the Archive
* @return Whether creation succeeded
*/
Result CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) {
Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle);
if (archive == nullptr)
return -1;
if (archive->backend->CreateDirectory(path))
return 0;
return -1;
}
/**
* Open a Directory from an Archive
* @param archive_handle Handle to an open Archive object
* @param path Path to the Directory inside of the Archive
* @return Opened Directory object
*/
ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path) {
Directory* directory = new Directory;
Handle handle = Kernel::g_object_pool.Create(directory);
Archive* archive = Kernel::g_object_pool.Get<Archive>(archive_handle);
if (archive == nullptr) {
return InvalidHandle(ErrorModule::FS);
}
directory->path = path;
directory->backend = archive->backend->OpenDirectory(path);
return MakeResult<Handle>(handle);
}
/// Initialize archives
void ArchiveInit() {
g_archive_map.clear();
// TODO(Link Mauve): Add the other archive types (see here for the known types:
// http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished
// archive type is SDMC, so it is the only one getting exposed.
std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX);
auto archive = new FileSys::Archive_SDMC(sdmc_directory);
if (archive->Initialize())
CreateArchive(archive, "SDMC");
else
ERROR_LOG(KERNEL, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str());
}
/// Shutdown archives
void ArchiveShutdown() {
g_archive_map.clear();
}
} // namespace Kernel

View file

@ -1,85 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/file_sys/archive.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Kernel namespace
namespace Kernel {
/**
* Opens an archive
* @param id_code IdCode of the archive to open
* @return Handle to the opened archive
*/
ResultVal<Handle> OpenArchive(FileSys::Archive::IdCode id_code);
/**
* Closes an archive
* @param id_code IdCode of the archive to open
*/
ResultCode CloseArchive(FileSys::Archive::IdCode id_code);
/**
* Creates an Archive
* @param backend File system backend interface to the archive
* @param name Name of Archive
*/
ResultCode CreateArchive(FileSys::Archive* backend, const std::string& name);
/**
* Open a File from an Archive
* @param archive_handle Handle to an open Archive object
* @param path Path to the File inside of the Archive
* @param mode Mode under which to open the File
* @return Handle to the opened File object
*/
ResultVal<Handle> OpenFileFromArchive(Handle archive_handle, const FileSys::Path& path, const FileSys::Mode mode);
/**
* Delete a File from an Archive
* @param archive_handle Handle to an open Archive object
* @param path Path to the File inside of the Archive
* @return Whether deletion succeeded
*/
Result DeleteFileFromArchive(Handle archive_handle, const FileSys::Path& path);
/**
* Delete a Directory from an Archive
* @param archive_handle Handle to an open Archive object
* @param path Path to the Directory inside of the Archive
* @return Whether deletion succeeded
*/
Result DeleteDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path);
/**
* Create a Directory from an Archive
* @param archive_handle Handle to an open Archive object
* @param path Path to the Directory inside of the Archive
* @return Whether creation of directory succeeded
*/
Result CreateDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path);
/**
* Open a Directory from an Archive
* @param archive_handle Handle to an open Archive object
* @param path Path to the Directory inside of the Archive
* @return Handle to the opened File object
*/
ResultVal<Handle> OpenDirectoryFromArchive(Handle archive_handle, const FileSys::Path& path);
/// Initialize archives
void ArchiveInit();
/// Shutdown archives
void ArchiveShutdown();
} // namespace FileSys

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <map>
@ -19,8 +19,8 @@ public:
std::string GetTypeName() const override { return "Event"; }
std::string GetName() const override { return name; }
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Event; }
Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Event; }
static const HandleType HANDLE_TYPE = HandleType::Event;
HandleType GetHandleType() const override { return HANDLE_TYPE; }
ResetType intitial_reset_type; ///< ResetType specified at Event initialization
ResetType reset_type; ///< Current ResetType
@ -53,7 +53,7 @@ public:
* @return Result of operation, 0 on success, otherwise error code
*/
ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) {
Event* evt = g_object_pool.Get<Event>(handle);
Event* evt = g_handle_table.Get<Event>(handle);
if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
evt->permanent_locked = permanent_locked;
@ -67,7 +67,7 @@ ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) {
* @return Result of operation, 0 on success, otherwise error code
*/
ResultCode SetEventLocked(const Handle handle, const bool locked) {
Event* evt = g_object_pool.Get<Event>(handle);
Event* evt = g_handle_table.Get<Event>(handle);
if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!evt->permanent_locked) {
@ -82,7 +82,7 @@ ResultCode SetEventLocked(const Handle handle, const bool locked) {
* @return Result of operation, 0 on success, otherwise error code
*/
ResultCode SignalEvent(const Handle handle) {
Event* evt = g_object_pool.Get<Event>(handle);
Event* evt = g_handle_table.Get<Event>(handle);
if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
// Resume threads waiting for event to signal
@ -110,7 +110,7 @@ ResultCode SignalEvent(const Handle handle) {
* @return Result of operation, 0 on success, otherwise error code
*/
ResultCode ClearEvent(Handle handle) {
Event* evt = g_object_pool.Get<Event>(handle);
Event* evt = g_handle_table.Get<Event>(handle);
if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!evt->permanent_locked) {
@ -129,7 +129,8 @@ ResultCode ClearEvent(Handle handle) {
Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) {
Event* evt = new Event;
handle = Kernel::g_object_pool.Create(evt);
// TOOD(yuriks): Fix error reporting
handle = Kernel::g_handle_table.Create(evt).ValueOr(INVALID_HANDLE);
evt->locked = true;
evt->permanent_locked = false;

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once

View file

@ -1,106 +1,117 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "common/common.h"
#include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/archive.h"
namespace Kernel {
Handle g_main_thread = 0;
ObjectPool g_object_pool;
HandleTable g_handle_table;
u64 g_program_id = 0;
ObjectPool::ObjectPool() {
next_id = INITIAL_NEXT_ID;
HandleTable::HandleTable() {
next_generation = 1;
Clear();
}
Handle ObjectPool::Create(Object* obj, int range_bottom, int range_top) {
if (range_top > MAX_COUNT) {
range_top = MAX_COUNT;
ResultVal<Handle> HandleTable::Create(Object* obj) {
_dbg_assert_(Kernel, obj != nullptr);
u16 slot = next_free_slot;
if (slot >= generations.size()) {
LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use.");
return ERR_OUT_OF_HANDLES;
}
if (next_id >= range_bottom && next_id < range_top) {
range_bottom = next_id++;
next_free_slot = generations[slot];
u16 generation = next_generation++;
// Overflow count so it fits in the 15 bits dedicated to the generation in the handle.
// CTR-OS doesn't use generation 0, so skip straight to 1.
if (next_generation >= (1 << 15)) next_generation = 1;
generations[slot] = generation;
intrusive_ptr_add_ref(obj);
objects[slot] = obj;
Handle handle = generation | (slot << 15);
obj->handle = handle;
return MakeResult<Handle>(handle);
}
ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
Object* object = GetGeneric(handle);
if (object == nullptr) {
LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle);
return ERR_INVALID_HANDLE;
}
for (int i = range_bottom; i < range_top; i++) {
if (!occupied[i]) {
occupied[i] = true;
pool[i] = obj;
pool[i]->handle = i + HANDLE_OFFSET;
return i + HANDLE_OFFSET;
}
return Create(object);
}
ResultCode HandleTable::Close(Handle handle) {
if (!IsValid(handle))
return ERR_INVALID_HANDLE;
size_t slot = GetSlot(handle);
u16 generation = GetGeneration(handle);
intrusive_ptr_release(objects[slot]);
objects[slot] = nullptr;
generations[generation] = next_free_slot;
next_free_slot = slot;
return RESULT_SUCCESS;
}
bool HandleTable::IsValid(Handle handle) const {
size_t slot = GetSlot(handle);
u16 generation = GetGeneration(handle);
return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation;
}
Object* HandleTable::GetGeneric(Handle handle) const {
if (handle == CurrentThread) {
// TODO(yuriks) Directly return the pointer once this is possible.
handle = GetCurrentThreadHandle();
} else if (handle == CurrentProcess) {
LOG_ERROR(Kernel, "Current process (%08X) pseudo-handle not supported", CurrentProcess);
return nullptr;
}
ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use.");
return 0;
}
bool ObjectPool::IsValid(Handle handle) {
int index = handle - HANDLE_OFFSET;
if (index < 0)
return false;
if (index >= MAX_COUNT)
return false;
return occupied[index];
}
void ObjectPool::Clear() {
for (int i = 0; i < MAX_COUNT; i++) {
//brutally clear everything, no validation
if (occupied[i])
delete pool[i];
occupied[i] = false;
if (!IsValid(handle)) {
return nullptr;
}
pool.fill(nullptr);
next_id = INITIAL_NEXT_ID;
return objects[GetSlot(handle)];
}
Object* &ObjectPool::operator [](Handle handle)
{
_dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ");
return pool[handle - HANDLE_OFFSET];
}
void ObjectPool::List() {
for (int i = 0; i < MAX_COUNT; i++) {
if (occupied[i]) {
if (pool[i]) {
INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(),
pool[i]->GetName().c_str());
}
}
void HandleTable::Clear() {
for (size_t i = 0; i < MAX_COUNT; ++i) {
generations[i] = i + 1;
if (objects[i] != nullptr)
intrusive_ptr_release(objects[i]);
objects[i] = nullptr;
}
}
int ObjectPool::GetCount() {
int count = 0;
for (int i = 0; i < MAX_COUNT; i++) {
if (occupied[i])
count++;
}
return count;
}
Object* ObjectPool::CreateByIDType(int type) {
ERROR_LOG(COMMON, "Unimplemented: %d.", type);
return nullptr;
next_free_slot = 0;
}
/// Initialize the kernel
void Init() {
Kernel::ThreadingInit();
Kernel::ArchiveInit();
}
/// Shutdown the kernel
void Shutdown() {
Kernel::ThreadingShutdown();
Kernel::ArchiveShutdown();
g_object_pool.Clear(); // Free all kernel objects
g_handle_table.Clear(); // Free all kernel objects
}
/**
@ -109,8 +120,6 @@ void Shutdown() {
* @return True on success, otherwise false
*/
bool LoadExec(u32 entry_point) {
Init();
Core::g_app_core->SetPC(entry_point);
// 0x30 is the typical main thread priority I've seen used so far

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -12,9 +12,17 @@
typedef u32 Handle;
typedef s32 Result;
const Handle INVALID_HANDLE = 0;
namespace Kernel {
enum KernelHandle {
// TODO: Verify code
const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel,
ErrorSummary::OutOfResource, ErrorLevel::Temporary);
// TOOD: Verify code
const ResultCode ERR_INVALID_HANDLE = InvalidHandle(ErrorModule::Kernel);
enum KernelHandle : Handle {
CurrentThread = 0xFFFF8000,
CurrentProcess = 0xFFFF8001,
};
@ -22,7 +30,7 @@ enum KernelHandle {
enum class HandleType : u32 {
Unknown = 0,
Port = 1,
Service = 2,
Session = 2,
Event = 3,
Mutex = 4,
SharedMemory = 5,
@ -30,20 +38,17 @@ enum class HandleType : u32 {
Thread = 7,
Process = 8,
AddressArbiter = 9,
File = 10,
Semaphore = 11,
Archive = 12,
Directory = 13,
Semaphore = 10,
};
enum {
DEFAULT_STACK_SIZE = 0x4000,
};
class ObjectPool;
class HandleTable;
class Object : NonCopyable {
friend class ObjectPool;
friend class HandleTable;
u32 handle;
public:
virtual ~Object() {}
@ -52,114 +57,146 @@ public:
virtual std::string GetName() const { return "[UNKNOWN KERNEL OBJECT]"; }
virtual Kernel::HandleType GetHandleType() const = 0;
/**
* Synchronize kernel object.
* @return True if the current thread should wait as a result of the sync
*/
virtual ResultVal<bool> SyncRequest() {
ERROR_LOG(KERNEL, "(UNIMPLEMENTED)");
return UnimplementedFunction(ErrorModule::Kernel);
}
/**
* Wait for kernel object to synchronize.
* @return True if the current thread should wait as a result of the wait
*/
virtual ResultVal<bool> WaitSynchronization() = 0;
};
class ObjectPool : NonCopyable {
public:
ObjectPool();
~ObjectPool() {}
// Allocates a handle within the range and inserts the object into the map.
Handle Create(Object* obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF);
static Object* CreateByIDType(int type);
template <class T>
void Destroy(Handle handle) {
if (Get<T>(handle)) {
occupied[handle - HANDLE_OFFSET] = false;
delete pool[handle - HANDLE_OFFSET];
}
virtual ResultVal<bool> WaitSynchronization() {
LOG_ERROR(Kernel, "(UNIMPLEMENTED)");
return UnimplementedFunction(ErrorModule::Kernel);
}
bool IsValid(Handle handle);
template <class T>
T* Get(Handle handle) {
if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) {
if (handle != 0) {
WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
}
return nullptr;
} else {
Object* t = pool[handle - HANDLE_OFFSET];
if (t->GetHandleType() != T::GetStaticHandleType()) {
WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle);
return nullptr;
}
return static_cast<T*>(t);
}
}
// ONLY use this when you know the handle is valid.
template <class T>
T *GetFast(Handle handle) {
const Handle realHandle = handle - HANDLE_OFFSET;
_dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]);
return static_cast<T*>(pool[realHandle]);
}
template <class T, typename ArgT>
void Iterate(bool func(T*, ArgT), ArgT arg) {
int type = T::GetStaticIDType();
for (int i = 0; i < MAX_COUNT; i++)
{
if (!occupied[i])
continue;
T* t = static_cast<T*>(pool[i]);
if (t->GetIDType() == type) {
if (!func(t, arg))
break;
}
}
}
bool GetIDType(Handle handle, HandleType* type) const {
if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) ||
!occupied[handle - HANDLE_OFFSET]) {
ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
return false;
}
Object* t = pool[handle - HANDLE_OFFSET];
*type = t->GetHandleType();
return true;
}
Object* &operator [](Handle handle);
void List();
void Clear();
int GetCount();
private:
friend void intrusive_ptr_add_ref(Object*);
friend void intrusive_ptr_release(Object*);
enum {
MAX_COUNT = 0x1000,
HANDLE_OFFSET = 0x100,
INITIAL_NEXT_ID = 0x10,
};
std::array<Object*, MAX_COUNT> pool;
std::array<bool, MAX_COUNT> occupied;
int next_id;
unsigned int ref_count = 0;
};
extern ObjectPool g_object_pool;
// Special functions that will later be used by boost::instrusive_ptr to do automatic ref-counting
inline void intrusive_ptr_add_ref(Object* object) {
++object->ref_count;
}
inline void intrusive_ptr_release(Object* object) {
if (--object->ref_count == 0) {
delete object;
}
}
/**
* This class allows the creation of Handles, which are references to objects that can be tested
* for validity and looked up. Here they are used to pass references to kernel objects to/from the
* emulated process. it has been designed so that it follows the same handle format and has
* approximately the same restrictions as the handle manager in the CTR-OS.
*
* Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0).
* The slot index is used to index into the arrays in this class to access the data corresponding
* to the Handle.
*
* To prevent accidental use of a freed Handle whose slot has already been reused, a global counter
* is kept and incremented every time a Handle is created. This is the Handle's "generation". The
* value of the counter is stored into the Handle as well as in the handle table (in the
* "generations" array). When looking up a handle, the Handle's generation must match with the
* value stored on the class, otherwise the Handle is considered invalid.
*
* To find free slots when allocating a Handle without needing to scan the entire object array, the
* generations field of unallocated slots is re-purposed as a linked list of indices to free slots.
* When a Handle is created, an index is popped off the list and used for the new Handle. When it
* is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is
* likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been
* verified and isn't likely to cause any problems.
*/
class HandleTable final : NonCopyable {
public:
HandleTable();
/**
* Allocates a handle for the given object.
* @return The created Handle or one of the following errors:
* - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded.
*/
ResultVal<Handle> Create(Object* obj);
/**
* Returns a new handle that points to the same object as the passed in handle.
* @return The duplicated Handle or one of the following errors:
* - `ERR_INVALID_HANDLE`: an invalid handle was passed in.
* - Any errors returned by `Create()`.
*/
ResultVal<Handle> Duplicate(Handle handle);
/**
* Closes a handle, removing it from the table and decreasing the object's ref-count.
* @return `RESULT_SUCCESS` or one of the following errors:
* - `ERR_INVALID_HANDLE`: an invalid handle was passed in.
*/
ResultCode Close(Handle handle);
/// Checks if a handle is valid and points to an existing object.
bool IsValid(Handle handle) const;
/**
* Looks up a handle.
* @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid.
*/
Object* GetGeneric(Handle handle) const;
/**
* Looks up a handle while verifying its type.
* @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid or its
* type differs from the handle type `T::HANDLE_TYPE`.
*/
template <class T>
T* Get(Handle handle) const {
Object* object = GetGeneric(handle);
if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
return static_cast<T*>(object);
}
return nullptr;
}
/// Closes all handles held in this table.
void Clear();
private:
/**
* This is the maximum limit of handles allowed per process in CTR-OS. It can be further
* reduced by ExHeader values, but this is not emulated here.
*/
static const size_t MAX_COUNT = 4096;
static size_t GetSlot(Handle handle) { return handle >> 15; }
static u16 GetGeneration(Handle handle) { return handle & 0x7FFF; }
/// Stores the Object referenced by the handle or null if the slot is empty.
std::array<Object*, MAX_COUNT> objects;
/**
* The value of `next_generation` when the handle was created, used to check for validity. For
* empty slots, contains the index of the next free slot in the list.
*/
std::array<u16, MAX_COUNT> generations;
/**
* Global counter of the number of created handles. Stored in `generations` when a handle is
* created, and wraps around to 1 when it hits 0x8000.
*/
u16 next_generation;
/// Head of the free slots linked list.
u16 next_free_slot;
};
extern HandleTable g_handle_table;
extern Handle g_main_thread;
/// The ID code of the currently running game
/// TODO(Subv): This variable should not be here,
/// we need a way to store information about the currently loaded application
/// for later query during runtime, maybe using the LDR service?
extern u64 g_program_id;
/// Initialize the kernel
void Init();

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <map>
@ -18,8 +18,8 @@ public:
std::string GetTypeName() const override { return "Mutex"; }
std::string GetName() const override { return name; }
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; }
Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Mutex; }
static const HandleType HANDLE_TYPE = HandleType::Mutex;
HandleType GetHandleType() const override { return HANDLE_TYPE; }
bool initial_locked; ///< Initial lock state when mutex was created
bool locked; ///< Current locked state
@ -27,21 +27,7 @@ public:
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
std::string name; ///< Name of mutex (optional)
ResultVal<bool> SyncRequest() override {
// TODO(bunnei): ImplementMe
locked = true;
return MakeResult<bool>(false);
}
ResultVal<bool> WaitSynchronization() override {
// TODO(bunnei): ImplementMe
bool wait = locked;
if (locked) {
Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
}
return MakeResult<bool>(wait);
}
ResultVal<bool> WaitSynchronization() override;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -49,21 +35,46 @@ public:
typedef std::multimap<Handle, Handle> MutexMap;
static MutexMap g_mutex_held_locks;
void MutexAcquireLock(Mutex* mutex, Handle thread) {
/**
* Acquires the specified mutex for the specified thread
* @param mutex Mutex that is to be acquired
* @param thread Thread that will acquired
*/
void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) {
g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle()));
mutex->lock_thread = thread;
}
void MutexAcquireLock(Mutex* mutex) {
Handle thread = GetCurrentThreadHandle();
bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
MutexAcquireLock(mutex, thread);
Kernel::ResumeThreadFromWait(thread);
return true;
}
/**
* Resumes a thread waiting for the specified mutex
* @param mutex The mutex that some thread is waiting on
*/
void ResumeWaitingThread(Mutex* mutex) {
// Find the next waiting thread for the mutex...
if (mutex->waiting_threads.empty()) {
// Reset mutex lock thread handle, nothing is waiting
mutex->locked = false;
mutex->lock_thread = -1;
}
else {
// Resume the next waiting thread and re-lock the mutex
std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
ReleaseMutexForThread(mutex, *iter);
mutex->waiting_threads.erase(iter);
}
}
void MutexEraseLock(Mutex* mutex) {
Handle handle = mutex->GetHandle();
auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread);
for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
if ((*iter).second == handle) {
if (iter->second == handle) {
g_mutex_held_locks.erase(iter);
break;
}
@ -71,6 +82,19 @@ void MutexEraseLock(Mutex* mutex) {
mutex->lock_thread = -1;
}
void ReleaseThreadMutexes(Handle thread) {
auto locked = g_mutex_held_locks.equal_range(thread);
// Release every mutex that the thread holds, and resume execution on the waiting threads
for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
Mutex* mutex = g_handle_table.Get<Mutex>(iter->second);
ResumeWaitingThread(mutex);
}
// Erase all the locks that this thread holds
g_mutex_held_locks.erase(thread);
}
bool LockMutex(Mutex* mutex) {
// Mutex alread locked?
if (mutex->locked) {
@ -80,28 +104,10 @@ bool LockMutex(Mutex* mutex) {
return true;
}
bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
MutexAcquireLock(mutex, thread);
Kernel::ResumeThreadFromWait(thread);
return true;
}
bool ReleaseMutex(Mutex* mutex) {
MutexEraseLock(mutex);
bool woke_threads = false;
// Find the next waiting thread for the mutex...
while (!woke_threads && !mutex->waiting_threads.empty()) {
std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
woke_threads |= ReleaseMutexForThread(mutex, *iter);
mutex->waiting_threads.erase(iter);
}
// Reset mutex lock thread handle, nothing is waiting
if (!woke_threads) {
mutex->locked = false;
mutex->lock_thread = -1;
}
return woke_threads;
ResumeWaitingThread(mutex);
return true;
}
/**
@ -109,7 +115,7 @@ bool ReleaseMutex(Mutex* mutex) {
* @param handle Handle to mutex to release
*/
ResultCode ReleaseMutex(Handle handle) {
Mutex* mutex = Kernel::g_object_pool.Get<Mutex>(handle);
Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle);
if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!ReleaseMutex(mutex)) {
@ -130,7 +136,8 @@ ResultCode ReleaseMutex(Handle handle) {
*/
Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) {
Mutex* mutex = new Mutex;
handle = Kernel::g_object_pool.Create(mutex);
// TODO(yuriks): Fix error reporting
handle = Kernel::g_handle_table.Create(mutex).ValueOr(INVALID_HANDLE);
mutex->locked = mutex->initial_locked = initial_locked;
mutex->name = name;
@ -158,4 +165,17 @@ Handle CreateMutex(bool initial_locked, const std::string& name) {
return handle;
}
ResultVal<bool> Mutex::WaitSynchronization() {
bool wait = locked;
if (locked) {
Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
}
else {
// Lock the mutex when the first thread accesses it
locked = true;
MutexAcquireLock(this);
}
return MakeResult<bool>(wait);
}
} // namespace

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -24,4 +24,10 @@ ResultCode ReleaseMutex(Handle handle);
*/
Handle CreateMutex(bool initial_locked, const std::string& name="Unknown");
/**
* Releases all the mutexes held by the specified thread
* @param thread Thread that is holding the mutexes
*/
void ReleaseThreadMutexes(Handle thread);
} // namespace

View file

@ -0,0 +1,95 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <queue>
#include "common/common.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/semaphore.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
class Semaphore : public Object {
public:
std::string GetTypeName() const override { return "Semaphore"; }
std::string GetName() const override { return name; }
static const HandleType HANDLE_TYPE = HandleType::Semaphore;
HandleType GetHandleType() const override { return HANDLE_TYPE; }
s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have
s32 available_count; ///< Number of free slots left in the semaphore
std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore
std::string name; ///< Name of semaphore (optional)
/**
* Tests whether a semaphore still has free slots
* @return Whether the semaphore is available
*/
bool IsAvailable() const {
return available_count > 0;
}
ResultVal<bool> WaitSynchronization() override {
bool wait = !IsAvailable();
if (wait) {
Kernel::WaitCurrentThread(WAITTYPE_SEMA, GetHandle());
waiting_threads.push(GetCurrentThreadHandle());
} else {
--available_count;
}
return MakeResult<bool>(wait);
}
};
////////////////////////////////////////////////////////////////////////////////////////////////////
ResultCode CreateSemaphore(Handle* handle, s32 initial_count,
s32 max_count, const std::string& name) {
if (initial_count > max_count)
return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel,
ErrorSummary::WrongArgument, ErrorLevel::Permanent);
Semaphore* semaphore = new Semaphore;
// TOOD(yuriks): Fix error reporting
*handle = g_handle_table.Create(semaphore).ValueOr(INVALID_HANDLE);
// When the semaphore is created, some slots are reserved for other threads,
// and the rest is reserved for the caller thread
semaphore->max_count = max_count;
semaphore->available_count = initial_count;
semaphore->name = name;
return RESULT_SUCCESS;
}
ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) {
Semaphore* semaphore = g_handle_table.Get<Semaphore>(handle);
if (semaphore == nullptr)
return InvalidHandle(ErrorModule::Kernel);
if (semaphore->max_count - semaphore->available_count < release_count)
return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
*count = semaphore->available_count;
semaphore->available_count += release_count;
// Notify some of the threads that the semaphore has been released
// stop once the semaphore is full again or there are no more waiting threads
while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) {
Kernel::ResumeThreadFromWait(semaphore->waiting_threads.front());
semaphore->waiting_threads.pop();
--semaphore->available_count;
}
return RESULT_SUCCESS;
}
} // namespace

View file

@ -0,0 +1,32 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
namespace Kernel {
/**
* Creates a semaphore.
* @param handle Pointer to the handle of the newly created object
* @param initial_count Number of slots reserved for other threads
* @param max_count Maximum number of slots the semaphore can have
* @param name Optional name of semaphore
* @return ResultCode of the error
*/
ResultCode CreateSemaphore(Handle* handle, s32 initial_count, s32 max_count, const std::string& name = "Unknown");
/**
* Releases a certain number of slots from a semaphore.
* @param count The number of free slots the semaphore had before this call
* @param handle The handle of the semaphore to release
* @param release_count The number of slots to release
* @return ResultCode of the error
*/
ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count);
} // namespace

View file

@ -0,0 +1,58 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/kernel/kernel.h"
namespace Kernel {
static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header
/**
* Returns a pointer to the command buffer in kernel memory
* @param offset Optional offset into command buffer
* @return Pointer to command buffer
*/
inline static u32* GetCommandBuffer(const int offset=0) {
return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset);
}
/**
* Kernel object representing the client endpoint of an IPC session. Sessions are the basic CTR-OS
* primitive for communication between different processes, and are used to implement service calls
* to the various system services.
*
* To make a service call, the client must write the command header and parameters to the buffer
* located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest
* SVC call with its Session handle. The kernel will read the command header, using it to marshall
* the parameters to the process at the server endpoint of the session. After the server replies to
* the request, the response is marshalled back to the caller's TLS buffer and control is
* transferred back to it.
*
* In Citra, only the client endpoint is currently implemented and only HLE calls, where the IPC
* request is answered by C++ code in the emulator, are supported. When SendSyncRequest is called
* with the session handle, this class's SyncRequest method is called, which should read the TLS
* buffer and emulate the call accordingly. Since the code can directly read the emulated memory,
* no parameter marshalling is done.
*
* In the long term, this should be turned into the full-fledged IPC mechanism implemented by
* CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as
* opposed to HLE simulations.
*/
class Session : public Object {
public:
std::string GetTypeName() const override { return "Session"; }
static const HandleType HANDLE_TYPE = HandleType::Session;
HandleType GetHandleType() const override { return HANDLE_TYPE; }
/**
* Handles a synchronous call to this session using HLE emulation. Emulated <-> emulated calls
* aren't supported yet.
*/
virtual ResultVal<bool> SyncRequest() = 0;
};
}

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common.h"
@ -13,14 +13,8 @@ class SharedMemory : public Object {
public:
std::string GetTypeName() const override { return "SharedMemory"; }
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; }
Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::SharedMemory; }
ResultVal<bool> WaitSynchronization() override {
// TODO(bunnei): ImplementMe
ERROR_LOG(OSHLE, "(UNIMPLEMENTED)");
return UnimplementedFunction(ErrorModule::OS);
}
static const HandleType HANDLE_TYPE = HandleType::SharedMemory;
HandleType GetHandleType() const override { return HANDLE_TYPE; }
u32 base_address; ///< Address of shared memory block in RAM
MemoryPermission permissions; ///< Permissions of shared memory block (SVC field)
@ -38,7 +32,8 @@ public:
*/
SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) {
SharedMemory* shared_memory = new SharedMemory;
handle = Kernel::g_object_pool.Create(shared_memory);
// TOOD(yuriks): Fix error reporting
handle = Kernel::g_handle_table.Create(shared_memory).ValueOr(INVALID_HANDLE);
shared_memory->name = name;
return shared_memory;
}
@ -61,12 +56,12 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions
MemoryPermission other_permissions) {
if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) {
ERROR_LOG(KERNEL, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!",
LOG_ERROR(Kernel_SVC, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!",
handle, address);
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
}
SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle);
SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
shared_memory->base_address = address;
@ -77,13 +72,13 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions
}
ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) {
SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle);
SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (0 != shared_memory->base_address)
return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset));
ERROR_LOG(KERNEL, "memory block handle=0x%08X not mapped!", handle);
LOG_ERROR(Kernel_SVC, "memory block handle=0x%08X not mapped!", handle);
// TODO(yuriks): Verify error code.
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
ErrorSummary::InvalidState, ErrorLevel::Permanent);

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -12,11 +12,15 @@ namespace Kernel {
/// Permissions for mapped shared memory blocks
enum class MemoryPermission : u32 {
None = 0,
Read = (1u << 0),
Write = (1u << 1),
ReadWrite = (Read | Write),
DontCare = (1u << 28)
None = 0,
Read = (1u << 0),
Write = (1u << 1),
ReadWrite = (Read | Write),
Execute = (1u << 2),
ReadExecute = (Read | Execute),
WriteExecute = (Write | Execute),
ReadWriteExecute = (Read | Write | Execute),
DontCare = (1u << 28)
};
/**

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
@ -14,6 +14,7 @@
#include "core/hle/hle.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/result.h"
#include "core/mem_map.h"
@ -25,8 +26,8 @@ public:
std::string GetName() const override { return name; }
std::string GetTypeName() const override { return "Thread"; }
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; }
Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Thread; }
static const HandleType HANDLE_TYPE = HandleType::Thread;
HandleType GetHandleType() const override { return HANDLE_TYPE; }
inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; }
inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; }
@ -49,6 +50,8 @@ public:
ThreadContext context;
u32 thread_id;
u32 status;
u32 entry_point;
u32 stack_top;
@ -61,6 +64,7 @@ public:
WaitType wait_type;
Handle wait_handle;
VAddr wait_address;
std::vector<Handle> waiting_threads;
@ -76,8 +80,10 @@ static Common::ThreadQueueList<Handle> thread_ready_queue;
static Handle current_thread_handle;
static Thread* current_thread;
/// Gets the current thread
inline Thread* GetCurrentThread() {
static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup
static u32 next_thread_id; ///< The next available thread id
Thread* GetCurrentThread() {
return current_thread;
}
@ -121,6 +127,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
}
t->wait_type = WAITTYPE_NONE;
t->wait_handle = 0;
t->wait_address = 0;
}
/// Change a thread to "ready" state
@ -140,30 +147,43 @@ void ChangeReadyState(Thread* t, bool ready) {
}
}
/// Verify that a thread has not been released from waiting
inline bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) {
_dbg_assert_(KERNEL, thread != nullptr);
return type == thread->wait_type && wait_handle == thread->wait_handle;
/// Check if a thread is blocking on a specified wait type
static bool CheckWaitType(const Thread* thread, WaitType type) {
return (type == thread->wait_type) && (thread->IsWaiting());
}
/// Check if a thread is blocking on a specified wait type with a specified handle
static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle) {
return CheckWaitType(thread, type) && (wait_handle == thread->wait_handle);
}
/// Check if a thread is blocking on a specified wait type with a specified handle and address
static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) {
return CheckWaitType(thread, type, wait_handle) && (wait_address == thread->wait_address);
}
/// Stops the current thread
ResultCode StopThread(Handle handle, const char* reason) {
Thread* thread = g_object_pool.Get<Thread>(handle);
Thread* thread = g_handle_table.Get<Thread>(handle);
if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
// Release all the mutexes that this thread holds
ReleaseThreadMutexes(handle);
ChangeReadyState(thread, false);
thread->status = THREADSTATUS_DORMANT;
for (Handle waiting_handle : thread->waiting_threads) {
Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle);
if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) {
Thread* waiting_thread = g_handle_table.Get<Thread>(waiting_handle);
if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, handle))
ResumeThreadFromWait(waiting_handle);
}
}
thread->waiting_threads.clear();
// Stopped threads are never waiting.
thread->wait_type = WAITTYPE_NONE;
thread->wait_handle = 0;
thread->wait_address = 0;
return RESULT_SUCCESS;
}
@ -178,7 +198,7 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
if (new_status == THREADSTATUS_WAIT) {
if (t->wait_type == WAITTYPE_NONE) {
ERROR_LOG(KERNEL, "Waittype none not allowed");
LOG_ERROR(Kernel, "Waittype none not allowed");
}
}
}
@ -190,14 +210,14 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
for (Handle handle : thread_queue) {
Thread* thread = g_object_pool.Get<Thread>(handle);
Thread* thread = g_handle_table.Get<Thread>(handle);
// TODO(bunnei): Verify arbiter address...
if (!VerifyWait(thread, WAITTYPE_ARB, arbiter))
if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address))
continue;
if (thread == nullptr)
continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up.
if(thread->current_priority <= priority) {
highest_priority_thread = handle;
priority = thread->current_priority;
@ -215,10 +235,9 @@ void ArbitrateAllThreads(u32 arbiter, u32 address) {
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
for (Handle handle : thread_queue) {
Thread* thread = g_object_pool.Get<Thread>(handle);
Thread* thread = g_handle_table.Get<Thread>(handle);
// TODO(bunnei): Verify arbiter address...
if (VerifyWait(thread, WAITTYPE_ARB, arbiter))
if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address))
ResumeThreadFromWait(handle);
}
}
@ -269,14 +288,9 @@ Thread* NextThread() {
if (next == 0) {
return nullptr;
}
return Kernel::g_object_pool.Get<Thread>(next);
return Kernel::g_handle_table.Get<Thread>(next);
}
/**
* Puts the current thread in the wait state for the given type
* @param wait_type Type of wait
* @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
*/
void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
Thread* thread = GetCurrentThread();
thread->wait_type = wait_type;
@ -284,11 +298,18 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
}
void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) {
WaitCurrentThread(wait_type, wait_handle);
GetCurrentThread()->wait_address = wait_address;
}
/// Resumes a thread from waiting by marking it as "ready"
void ResumeThreadFromWait(Handle handle) {
Thread* thread = Kernel::g_object_pool.Get<Thread>(handle);
Thread* thread = Kernel::g_handle_table.Get<Thread>(handle);
if (thread) {
thread->status &= ~THREADSTATUS_WAIT;
thread->wait_handle = 0;
thread->wait_type = WAITTYPE_NONE;
if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
ChangeReadyState(thread, true);
}
@ -301,12 +322,12 @@ void DebugThreadQueue() {
if (!thread) {
return;
}
INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
for (u32 i = 0; i < thread_queue.size(); i++) {
Handle handle = thread_queue[i];
s32 priority = thread_ready_queue.contains(handle);
if (priority != -1) {
INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle);
LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle);
}
}
}
@ -316,15 +337,17 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
s32 processor_id, u32 stack_top, int stack_size) {
_assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
"CreateThread priority=%d, outside of allowable range!", priority)
"priority=%d, outside of allowable range!", priority)
Thread* thread = new Thread;
handle = Kernel::g_object_pool.Create(thread);
// TOOD(yuriks): Fix error reporting
handle = Kernel::g_handle_table.Create(thread).ValueOr(INVALID_HANDLE);
thread_queue.push_back(handle);
thread_ready_queue.prepare(priority);
thread->thread_id = next_thread_id++;
thread->status = THREADSTATUS_DORMANT;
thread->entry_point = entry_point;
thread->stack_top = stack_top;
@ -333,6 +356,7 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
thread->processor_id = processor_id;
thread->wait_type = WAITTYPE_NONE;
thread->wait_handle = 0;
thread->wait_address = 0;
thread->name = name;
return thread;
@ -343,24 +367,24 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
u32 stack_top, int stack_size) {
if (name == nullptr) {
ERROR_LOG(KERNEL, "CreateThread(): nullptr name");
LOG_ERROR(Kernel_SVC, "nullptr name");
return -1;
}
if ((u32)stack_size < 0x200) {
ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid stack_size=0x%08X", name,
LOG_ERROR(Kernel_SVC, "(name=%s): invalid stack_size=0x%08X", name,
stack_size);
return -1;
}
if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X",
LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d",
name, priority, new_priority);
// TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
// validity of this
priority = new_priority;
}
if (!Memory::GetPointer(entry_point)) {
ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, entry_point);
LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point);
return -1;
}
Handle handle;
@ -375,7 +399,7 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
/// Get the priority of the thread specified by handle
ResultVal<u32> GetThreadPriority(const Handle handle) {
Thread* thread = g_object_pool.Get<Thread>(handle);
Thread* thread = g_handle_table.Get<Thread>(handle);
if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
return MakeResult<u32>(thread->current_priority);
@ -387,7 +411,7 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) {
if (!handle) {
thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
} else {
thread = g_object_pool.Get<Thread>(handle);
thread = g_handle_table.Get<Thread>(handle);
if (thread == nullptr) {
return InvalidHandle(ErrorModule::Kernel);
}
@ -397,7 +421,7 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) {
// If priority is invalid, clamp to valid range
if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority);
LOG_WARNING(Kernel_SVC, "invalid priority=%d, clamping to %d", priority, new_priority);
// TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
// validity of this
priority = new_priority;
@ -450,24 +474,44 @@ void Reschedule() {
Thread* prev = GetCurrentThread();
Thread* next = NextThread();
HLE::g_reschedule = false;
if (next > 0) {
INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
if (next != nullptr) {
LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
SwitchContext(next);
} else {
LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle());
// Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep
// by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again.
// This results in the current thread yielding on a VBLANK once, and then it will be
// immediately placed back in the queue for execution.
if (prev->wait_type == WAITTYPE_VBLANK) {
ResumeThreadFromWait(prev->GetHandle());
for (Handle handle : thread_queue) {
Thread* thread = g_handle_table.Get<Thread>(handle);
LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X",
thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle);
}
}
// TODO(bunnei): Hack - There is no timing mechanism yet to wake up a thread if it has been put
// to sleep. So, we'll just immediately set it to "ready" again after an attempted context
// switch has occurred. This results in the current thread yielding on a sleep once, and then it
// will immediately be placed back in the queue for execution.
if (CheckWaitType(prev, WAITTYPE_SLEEP))
ResumeThreadFromWait(prev->GetHandle());
}
ResultCode GetThreadId(u32* thread_id, Handle handle) {
Thread* thread = g_handle_table.Get<Thread>(handle);
if (thread == nullptr)
return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS,
ErrorSummary::WrongArgument, ErrorLevel::Permanent);
*thread_id = thread->thread_id;
return RESULT_SUCCESS;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void ThreadingInit() {
next_thread_id = INITIAL_THREAD_ID;
}
void ThreadingShutdown() {

View file

@ -1,10 +1,13 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "core/mem_map.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"
@ -37,7 +40,6 @@ enum WaitType {
WAITTYPE_SEMA,
WAITTYPE_EVENT,
WAITTYPE_THREADEND,
WAITTYPE_VBLANK,
WAITTYPE_MUTEX,
WAITTYPE_SYNCH,
WAITTYPE_ARB,
@ -58,6 +60,14 @@ void Reschedule();
/// Stops the current thread
ResultCode StopThread(Handle thread, const char* reason);
/**
* Retrieves the ID of the specified thread handle
* @param thread_id Will contain the output thread id
* @param handle Handle to the thread we want
* @return Whether the function was successful or not
*/
ResultCode GetThreadId(u32* thread_id, Handle handle);
/// Resumes a thread from waiting by marking it as "ready"
void ResumeThreadFromWait(Handle handle);
@ -77,6 +87,14 @@ Handle GetCurrentThreadHandle();
*/
void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
/**
* Puts the current thread in the wait state for the given type
* @param wait_type Type of wait
* @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
* @param wait_address Arbitration address used to resume from wait
*/
void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address);
/// Put current thread in a wait state - on WaitSynchronization
void WaitThread_Synchronization();

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -17,6 +17,8 @@
/// Detailed description of the error. This listing is likely incomplete.
enum class ErrorDescription : u32 {
Success = 0,
FS_NotFound = 100,
FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
InvalidSection = 1000,
TooLarge = 1001,
NotAuthorized = 1002,

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/log.h"
@ -11,6 +11,24 @@
namespace AC_U {
/**
* AC_U::GetWifiStatus service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Output connection type, 0 = none, 1 = Old3DS Internet, 2 = New3DS Internet.
*/
void GetWifiStatus(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
// TODO(purpasmart96): This function is only a stub,
// it returns a valid result without implementing full functionality.
cmd_buff[1] = 0; // No error
cmd_buff[2] = 0; // Connection type set to none
LOG_WARNING(Service_AC, "(STUBBED) called");
}
const Interface::FunctionInfo FunctionTable[] = {
{0x00010000, nullptr, "CreateDefaultConfig"},
{0x00040006, nullptr, "ConnectAsync"},
@ -18,7 +36,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00080004, nullptr, "CloseAsync"},
{0x00090002, nullptr, "GetCloseResult"},
{0x000A0000, nullptr, "GetLastErrorCode"},
{0x000D0000, nullptr, "GetWifiStatus"},
{0x000D0000, GetWifiStatus, "GetWifiStatus"},
{0x000E0042, nullptr, "GetCurrentAPInfo"},
{0x00100042, nullptr, "GetCurrentNZoneInfo"},
{0x00110042, nullptr, "GetNZoneApNumService"},
@ -38,7 +56,4 @@ Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
Interface::~Interface() {
}
} // namespace

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -16,11 +16,7 @@ namespace AC_U {
class Interface : public Service::Interface {
public:
Interface();
~Interface();
/**
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
std::string GetPortName() const override {
return "ac:u";
}

View file

@ -0,0 +1,24 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/log.h"
#include "core/hle/hle.h"
#include "core/hle/service/act_u.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace ACT_U
namespace ACT_U {
// Empty arrays are illegal -- commented out until an entry is added.
//const Interface::FunctionInfo FunctionTable[] = { };
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
//Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
} // namespace

View file

@ -0,0 +1,23 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace ACT_U
namespace ACT_U {
class Interface : public Service::Interface {
public:
Interface();
std::string GetPortName() const override {
return "act:u";
}
};
} // namespace

View file

@ -0,0 +1,24 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/log.h"
#include "core/hle/hle.h"
#include "core/hle/service/am_app.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace AM_APP
namespace AM_APP {
// Empty arrays are illegal -- commented out until an entry is added.
//const Interface::FunctionInfo FunctionTable[] = { };
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
//Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
} // namespace

View file

@ -0,0 +1,23 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace AM_APP
namespace AM_APP {
class Interface : public Service::Interface {
public:
Interface();
std::string GetPortName() const override {
return "am:app";
}
};
} // namespace

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/log.h"
@ -41,7 +41,4 @@ Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
Interface::~Interface() {
}
} // namespace

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -14,11 +14,7 @@ namespace AM_NET {
class Interface : public Service::Interface {
public:
Interface();
~Interface();
/**
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
std::string GetPortName() const override {
return "am:net";
}

View file

@ -0,0 +1,34 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/log.h"
#include "core/hle/hle.h"
#include "core/hle/service/apt_a.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace APT_A
namespace APT_A {
const Interface::FunctionInfo FunctionTable[] = {
{0x00010040, nullptr, "GetLockHandle?"},
{0x00020080, nullptr, "Initialize?"},
{0x00030040, nullptr, "Enable?"},
{0x00040040, nullptr, "Finalize?"},
{0x00050040, nullptr, "GetAppletManInfo?"},
{0x00060040, nullptr, "GetAppletInfo?"},
{0x003B0040, nullptr, "CancelLibraryApplet?"},
{0x00430040, nullptr, "NotifyToWait?"},
{0x004B00C2, nullptr, "AppletUtility?"},
{0x00550040, nullptr, "WriteInputToNsState?"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
} // namespace

View file

@ -0,0 +1,23 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace APT_A
namespace APT_A {
class Interface : public Service::Interface {
public:
Interface();
std::string GetPortName() const override {
return "APT:A";
}
};
} // namespace

View file

@ -1,13 +1,15 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common.h"
#include "common/file_util.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/shared_memory.h"
#include "apt_u.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -15,7 +17,19 @@
namespace APT_U {
// Address used for shared font (as observed on HW)
// TODO(bunnei): This is the hard-coded address where we currently dump the shared font from via
// https://github.com/citra-emu/3dsutils. This is technically a hack, and will not work at any
// address other than 0x18000000 due to internal pointers in the shared font dump that would need to
// be relocated. This might be fixed by dumping the shared font @ address 0x00000000 and then
// correctly mapping it in Citra, however we still do not understand how the mapping is determined.
static const VAddr SHARED_FONT_VADDR = 0x18000000;
// Handle to shared memory region designated to for shared system font
static Handle shared_font_mem = 0;
static Handle lock_handle = 0;
static std::vector<u8> shared_font;
/// Signals used by APT functions
enum class SignalType : u32 {
@ -26,7 +40,7 @@ enum class SignalType : u32 {
};
void Initialize(Service::Interface* self) {
u32* cmd_buff = Service::GetCommandBuffer();
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle
cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle
@ -39,11 +53,11 @@ void Initialize(Service::Interface* self) {
cmd_buff[1] = 0; // No error
DEBUG_LOG(KERNEL, "called");
LOG_DEBUG(Service_APT, "called");
}
void GetLockHandle(Service::Interface* self) {
u32* cmd_buff = Service::GetCommandBuffer();
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field
if (0 == lock_handle) {
@ -60,22 +74,22 @@ void GetLockHandle(Service::Interface* self) {
cmd_buff[4] = 0;
cmd_buff[5] = lock_handle;
DEBUG_LOG(KERNEL, "called handle=0x%08X", cmd_buff[5]);
LOG_TRACE(Service_APT, "called handle=0x%08X", cmd_buff[5]);
}
void Enable(Service::Interface* self) {
u32* cmd_buff = Service::GetCommandBuffer();
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for?
cmd_buff[1] = 0; // No error
WARN_LOG(KERNEL, "(STUBBED) called unk=0x%08X", unk);
LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk);
}
void InquireNotification(Service::Interface* self) {
u32* cmd_buff = Service::GetCommandBuffer();
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 app_id = cmd_buff[2];
cmd_buff[1] = 0; // No error
cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type
WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X", app_id);
LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
}
/**
@ -84,21 +98,21 @@ void InquireNotification(Service::Interface* self) {
* state so that this command will return an error if this command is used again if parameters were
* not set again. This is called when the second Initialize event is triggered. It returns a signal
* type indicating why it was triggered.
* Inputs:
* 1 : AppID
* 2 : Parameter buffer size, max size is 0x1000
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Unknown, for now assume AppID of the process which sent these parameters
* 3 : Unknown, for now assume Signal type
* 4 : Actual parameter buffer size, this is <= to the the input size
* 5 : Value
* 6 : Handle from the source process which set the parameters, likely used for shared memory
* 7 : Size
* 8 : Output parameter buffer ptr
* Inputs:
* 1 : AppID
* 2 : Parameter buffer size, max size is 0x1000
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Unknown, for now assume AppID of the process which sent these parameters
* 3 : Unknown, for now assume Signal type
* 4 : Actual parameter buffer size, this is <= to the the input size
* 5 : Value
* 6 : Handle from the source process which set the parameters, likely used for shared memory
* 7 : Size
* 8 : Output parameter buffer ptr
*/
void ReceiveParameter(Service::Interface* self) {
u32* cmd_buff = Service::GetCommandBuffer();
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 app_id = cmd_buff[1];
u32 buffer_size = cmd_buff[2];
cmd_buff[1] = 0; // No error
@ -108,28 +122,28 @@ void ReceiveParameter(Service::Interface* self) {
cmd_buff[5] = 0;
cmd_buff[6] = 0;
cmd_buff[7] = 0;
WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
}
/**
* APT_U::GlanceParameter service function. This is exactly the same as APT_U::ReceiveParameter
* (except for the word value prior to the output handle), except this will not clear the flag
* (except when responseword[3]==8 || responseword[3]==9) in NS state.
* Inputs:
* 1 : AppID
* 2 : Parameter buffer size, max size is 0x1000
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Unknown, for now assume AppID of the process which sent these parameters
* 3 : Unknown, for now assume Signal type
* 4 : Actual parameter buffer size, this is <= to the the input size
* 5 : Value
* 6 : Handle from the source process which set the parameters, likely used for shared memory
* 7 : Size
* 8 : Output parameter buffer ptr
* Inputs:
* 1 : AppID
* 2 : Parameter buffer size, max size is 0x1000
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Unknown, for now assume AppID of the process which sent these parameters
* 3 : Unknown, for now assume Signal type
* 4 : Actual parameter buffer size, this is <= to the the input size
* 5 : Value
* 6 : Handle from the source process which set the parameters, likely used for shared memory
* 7 : Size
* 8 : Output parameter buffer ptr
*/
void GlanceParameter(Service::Interface* self) {
u32* cmd_buff = Service::GetCommandBuffer();
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 app_id = cmd_buff[1];
u32 buffer_size = cmd_buff[2];
@ -141,22 +155,22 @@ void GlanceParameter(Service::Interface* self) {
cmd_buff[6] = 0;
cmd_buff[7] = 0;
WARN_LOG(KERNEL, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
}
/**
* APT_U::AppletUtility service function
* Inputs:
* 1 : Unknown, but clearly used for something
* 2 : Buffer 1 size (purpose is unknown)
* 3 : Buffer 2 size (purpose is unknown)
* 5 : Buffer 1 address (purpose is unknown)
* 65 : Buffer 2 address (purpose is unknown)
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* Inputs:
* 1 : Unknown, but clearly used for something
* 2 : Buffer 1 size (purpose is unknown)
* 3 : Buffer 2 size (purpose is unknown)
* 5 : Buffer 1 address (purpose is unknown)
* 65 : Buffer 2 address (purpose is unknown)
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void AppletUtility(Service::Interface* self) {
u32* cmd_buff = Service::GetCommandBuffer();
u32* cmd_buff = Kernel::GetCommandBuffer();
// These are from 3dbrew - I'm not really sure what they're used for.
u32 unk = cmd_buff[1];
@ -167,11 +181,39 @@ void AppletUtility(Service::Interface* self) {
cmd_buff[1] = 0; // No error
WARN_LOG(KERNEL, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08x, buffer2_size=0x%08x, "
LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08x, buffer2_size=0x%08x, "
"buffer1_addr=0x%08x, buffer2_addr=0x%08x", unk, buffer1_size, buffer2_size,
buffer1_addr, buffer2_addr);
}
/**
* APT_U::GetSharedFont service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Virtual address of where shared font will be loaded in memory
* 4 : Handle to shared font memory
*/
void GetSharedFont(Service::Interface* self) {
LOG_TRACE(Kernel_SVC, "called");
u32* cmd_buff = Kernel::GetCommandBuffer();
if (!shared_font.empty()) {
// TODO(bunnei): This function shouldn't copy the shared font every time it's called.
// Instead, it should probably map the shared font as RO memory. We don't currently have
// an easy way to do this, but the copy should be sufficient for now.
memcpy(Memory::GetPointer(SHARED_FONT_VADDR), shared_font.data(), shared_font.size());
cmd_buff[0] = 0x00440082;
cmd_buff[1] = 0; // No error
cmd_buff[2] = SHARED_FONT_VADDR;
cmd_buff[4] = shared_font_mem;
} else {
cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware)
LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT);
}
}
const Interface::FunctionInfo FunctionTable[] = {
{0x00010040, GetLockHandle, "GetLockHandle"},
{0x00020080, Initialize, "Initialize"},
@ -240,7 +282,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
{0x00420080, nullptr, "SleepSystem"},
{0x00430040, nullptr, "NotifyToWait"},
{0x00440000, nullptr, "GetSharedFont"},
{0x00440000, GetSharedFont, "GetSharedFont"},
{0x00450040, nullptr, "GetWirelessRebootInfo"},
{0x00460104, nullptr, "Wrap"},
{0x00470104, nullptr, "Unwrap"},
@ -259,12 +301,33 @@ const Interface::FunctionInfo FunctionTable[] = {
// Interface class
Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
// Load the shared system font (if available).
// The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
// generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
// a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
// "shared_font.bin" in the Citra "sysdata" directory.
shared_font.clear();
std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT;
FileUtil::CreateFullPath(filepath); // Create path if not already created
FileUtil::IOFile file(filepath, "rb");
if (file.IsOpen()) {
// Read shared font data
shared_font.resize((size_t)file.GetSize());
file.ReadBytes(shared_font.data(), (size_t)file.GetSize());
// Create shared font memory object
shared_font_mem = Kernel::CreateSharedMemory("APT_U:shared_font_mem");
} else {
LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str());
shared_font_mem = 0;
}
lock_handle = 0;
}
Interface::~Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
} // namespace

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -20,15 +20,8 @@ namespace APT_U {
/// Interface to "APT:U" service
class Interface : public Service::Interface {
public:
Interface();
~Interface();
/**
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
std::string GetPortName() const override {
return "APT:U";
}

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/log.h"
@ -11,18 +11,15 @@
namespace BOSS_U {
const Interface::FunctionInfo FunctionTable[] = {
{0x00020100, nullptr, "GetStorageInfo"},
};
const Interface::FunctionInfo FunctionTable[] = {
{0x00020100, nullptr, "GetStorageInfo"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
Interface::~Interface() {
}
Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
} // namespace

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -11,17 +11,13 @@
namespace BOSS_U {
class Interface : public Service::Interface {
public:
Interface();
~Interface();
/**
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
std::string GetPortName() const {
return "boss:U";
}
};
class Interface : public Service::Interface {
public:
Interface();
std::string GetPortName() const override {
return "boss:U";
}
};
} // namespace

View file

@ -0,0 +1,24 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/log.h"
#include "core/hle/hle.h"
#include "core/hle/service/cecd_u.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace CECD_U
namespace CECD_U {
// Empty arrays are illegal -- commented out until an entry is added.
//const Interface::FunctionInfo FunctionTable[] = { };
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
//Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
} // namespace

View file

@ -0,0 +1,23 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace CECD_U
namespace CECD_U {
class Interface : public Service::Interface {
public:
Interface();
std::string GetPortName() const override {
return "cecd:u";
}
};
} // namespace

View file

@ -0,0 +1,202 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "common/log.h"
#include "common/make_unique.h"
#include "core/file_sys/archive_systemsavedata.h"
#include "core/hle/service/cfg/cfg.h"
namespace Service {
namespace CFG {
const u64 CFG_SAVE_ID = 0x00010017;
const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE;
const ConsoleModelInfo CONSOLE_MODEL = { NINTENDO_3DS_XL, { 0, 0, 0 } };
const u8 CONSOLE_LANGUAGE = LANGUAGE_EN;
const char CONSOLE_USERNAME[0x14] = "CITRA";
/// This will be initialized in CFGInit, and will be used when creating the block
UsernameBlock CONSOLE_USERNAME_BLOCK;
/// TODO(Subv): Find out what this actually is
const u8 SOUND_OUTPUT_MODE = 2;
const u8 UNITED_STATES_COUNTRY_ID = 49;
/// TODO(Subv): Find what the other bytes are
const ConsoleCountryInfo COUNTRY_INFO = { { 0, 0, 0 }, UNITED_STATES_COUNTRY_ID };
/**
* TODO(Subv): Find out what this actually is, these values fix some NaN uniforms in some games,
* for example Nintendo Zone
* Thanks Normmatt for providing this information
*/
const std::array<float, 8> STEREO_CAMERA_SETTINGS = {
62.0f, 289.0f, 76.80000305175781f, 46.08000183105469f,
10.0f, 5.0f, 55.58000183105469f, 21.56999969482422f
};
static const u32 CONFIG_SAVEFILE_SIZE = 0x8000;
static std::array<u8, CONFIG_SAVEFILE_SIZE> cfg_config_file_buffer;
static std::unique_ptr<FileSys::Archive_SystemSaveData> cfg_system_save_data;
ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output) {
// Read the header
SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data());
auto itr = std::find_if(std::begin(config->block_entries), std::end(config->block_entries),
[&](const SaveConfigBlockEntry& entry) {
return entry.block_id == block_id && entry.size == size && (entry.flags & flag);
});
if (itr == std::end(config->block_entries)) {
LOG_ERROR(Service_CFG, "Config block %u with size %u and flags %u not found", block_id, size, flag);
return ResultCode(-1); // TODO(Subv): Find the correct error code
}
// The data is located in the block header itself if the size is less than 4 bytes
if (itr->size <= 4)
memcpy(output, &itr->offset_or_data, itr->size);
else
memcpy(output, &cfg_config_file_buffer[itr->offset_or_data], itr->size);
return RESULT_SUCCESS;
}
ResultCode CreateConfigInfoBlk(u32 block_id, u16 size, u16 flags, const u8* data) {
SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data());
if (config->total_entries >= CONFIG_FILE_MAX_BLOCK_ENTRIES)
return ResultCode(-1); // TODO(Subv): Find the right error code
// Insert the block header with offset 0 for now
config->block_entries[config->total_entries] = { block_id, 0, size, flags };
if (size > 4) {
u32 offset = config->data_entries_offset;
// Perform a search to locate the next offset for the new data
// use the offset and size of the previous block to determine the new position
for (int i = config->total_entries - 1; i >= 0; --i) {
// Ignore the blocks that don't have a separate data offset
if (config->block_entries[i].size > 4) {
offset = config->block_entries[i].offset_or_data +
config->block_entries[i].size;
break;
}
}
config->block_entries[config->total_entries].offset_or_data = offset;
// Write the data at the new offset
memcpy(&cfg_config_file_buffer[offset], data, size);
}
else {
// The offset_or_data field in the header contains the data itself if it's 4 bytes or less
memcpy(&config->block_entries[config->total_entries].offset_or_data, data, size);
}
++config->total_entries;
return RESULT_SUCCESS;
}
ResultCode DeleteConfigNANDSaveFile() {
FileSys::Path path("config");
if (cfg_system_save_data->DeleteFile(path))
return RESULT_SUCCESS;
return ResultCode(-1); // TODO(Subv): Find the right error code
}
ResultCode UpdateConfigNANDSavegame() {
FileSys::Mode mode = {};
mode.write_flag = 1;
mode.create_flag = 1;
FileSys::Path path("config");
auto file = cfg_system_save_data->OpenFile(path, mode);
_assert_msg_(Service_CFG, file != nullptr, "could not open file");
file->Write(0, CONFIG_SAVEFILE_SIZE, 1, cfg_config_file_buffer.data());
return RESULT_SUCCESS;
}
ResultCode FormatConfig() {
ResultCode res = DeleteConfigNANDSaveFile();
if (!res.IsSuccess())
return res;
// Delete the old data
cfg_config_file_buffer.fill(0);
// Create the header
SaveFileConfig* config = reinterpret_cast<SaveFileConfig*>(cfg_config_file_buffer.data());
// This value is hardcoded, taken from 3dbrew, verified by hardware, it's always the same value
config->data_entries_offset = 0x455C;
// Insert the default blocks
res = CreateConfigInfoBlk(0x00050005, sizeof(STEREO_CAMERA_SETTINGS), 0xE,
reinterpret_cast<const u8*>(STEREO_CAMERA_SETTINGS.data()));
if (!res.IsSuccess())
return res;
res = CreateConfigInfoBlk(0x00090001, sizeof(CONSOLE_UNIQUE_ID), 0xE,
reinterpret_cast<const u8*>(&CONSOLE_UNIQUE_ID));
if (!res.IsSuccess())
return res;
res = CreateConfigInfoBlk(0x000F0004, sizeof(CONSOLE_MODEL), 0x8,
reinterpret_cast<const u8*>(&CONSOLE_MODEL));
if (!res.IsSuccess())
return res;
res = CreateConfigInfoBlk(0x000A0002, sizeof(CONSOLE_LANGUAGE), 0xA, &CONSOLE_LANGUAGE);
if (!res.IsSuccess())
return res;
res = CreateConfigInfoBlk(0x00070001, sizeof(SOUND_OUTPUT_MODE), 0xE, &SOUND_OUTPUT_MODE);
if (!res.IsSuccess())
return res;
res = CreateConfigInfoBlk(0x000B0000, sizeof(COUNTRY_INFO), 0xE,
reinterpret_cast<const u8*>(&COUNTRY_INFO));
if (!res.IsSuccess())
return res;
res = CreateConfigInfoBlk(0x000A0000, sizeof(CONSOLE_USERNAME_BLOCK), 0xE,
reinterpret_cast<const u8*>(&CONSOLE_USERNAME_BLOCK));
if (!res.IsSuccess())
return res;
// Save the buffer to the file
res = UpdateConfigNANDSavegame();
if (!res.IsSuccess())
return res;
return RESULT_SUCCESS;
}
void CFGInit() {
// TODO(Subv): In the future we should use the FS service to query this archive,
// currently it is not possible because you can only have one open archive of the same type at any time
std::string syssavedata_directory = FileUtil::GetUserPath(D_SYSSAVEDATA_IDX);
cfg_system_save_data = Common::make_unique<FileSys::Archive_SystemSaveData>(
syssavedata_directory, CFG_SAVE_ID);
if (!cfg_system_save_data->Initialize()) {
LOG_CRITICAL(Service_CFG, "Could not initialize SystemSaveData archive for the CFG:U service");
return;
}
// TODO(Subv): All this code should be moved to cfg:i,
// it's only here because we do not currently emulate the lower level code that uses that service
// Try to open the file in read-only mode to check its existence
FileSys::Mode mode = {};
mode.read_flag = 1;
FileSys::Path path("config");
auto file = cfg_system_save_data->OpenFile(path, mode);
// Load the config if it already exists
if (file != nullptr) {
file->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data());
return;
}
// Initialize the Username block
// TODO(Subv): Initialize this directly in the variable when MSVC supports char16_t string literals
CONSOLE_USERNAME_BLOCK.ng_word = 0;
CONSOLE_USERNAME_BLOCK.zero = 0;
// Copy string to buffer and pad with zeros at the end
auto size = Common::UTF8ToUTF16(CONSOLE_USERNAME).copy(CONSOLE_USERNAME_BLOCK.username, 0x14);
std::fill(std::begin(CONSOLE_USERNAME_BLOCK.username) + size,
std::end(CONSOLE_USERNAME_BLOCK.username), 0);
FormatConfig();
}
void CFGShutdown() {
}
} // namespace CFG
} // namespace Service

View file

@ -0,0 +1,144 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "core/hle/result.h"
namespace Service {
namespace CFG {
enum SystemModel {
NINTENDO_3DS = 0,
NINTENDO_3DS_XL = 1,
NEW_NINTENDO_3DS = 2,
NINTENDO_2DS = 3,
NEW_NINTENDO_3DS_XL = 4
};
enum SystemLanguage {
LANGUAGE_JP = 0,
LANGUAGE_EN = 1,
LANGUAGE_FR = 2,
LANGUAGE_DE = 3,
LANGUAGE_IT = 4,
LANGUAGE_ES = 5,
LANGUAGE_ZH = 6,
LANGUAGE_KO = 7,
LANGUAGE_NL = 8,
LANGUAGE_PT = 9,
LANGUAGE_RU = 10
};
/// Block header in the config savedata file
struct SaveConfigBlockEntry {
u32 block_id; ///< The id of the current block
u32 offset_or_data; ///< This is the absolute offset to the block data if the size is greater than 4 bytes, otherwise it contains the data itself
u16 size; ///< The size of the block
u16 flags; ///< The flags of the block, possibly used for access control
};
/// The maximum number of block entries that can exist in the config file
static const u32 CONFIG_FILE_MAX_BLOCK_ENTRIES = 1479;
/**
* The header of the config savedata file,
* contains information about the blocks in the file
*/
struct SaveFileConfig {
u16 total_entries; ///< The total number of set entries in the config file
u16 data_entries_offset; ///< The offset where the data for the blocks start, this is hardcoded to 0x455C as per hardware
SaveConfigBlockEntry block_entries[CONFIG_FILE_MAX_BLOCK_ENTRIES]; ///< The block headers, the maximum possible value is 1479 as per hardware
u32 unknown; ///< This field is unknown, possibly padding, 0 has been observed in hardware
};
static_assert(sizeof(SaveFileConfig) == 0x455C, "The SaveFileConfig header must be exactly 0x455C bytes");
struct UsernameBlock {
char16_t username[10]; ///< Exactly 20 bytes long, padded with zeros at the end if necessary
u32 zero;
u32 ng_word;
};
static_assert(sizeof(UsernameBlock) == 0x1C, "Size of UsernameBlock must be 0x1C");
struct ConsoleModelInfo {
u8 model; ///< The console model (3DS, 2DS, etc)
u8 unknown[3]; ///< Unknown data
};
static_assert(sizeof(ConsoleModelInfo) == 4, "ConsoleModelInfo must be exactly 4 bytes");
struct ConsoleCountryInfo {
u8 unknown[3]; ///< Unknown data
u8 country_code; ///< The country code of the console
};
static_assert(sizeof(ConsoleCountryInfo) == 4, "ConsoleCountryInfo must be exactly 4 bytes");
extern const u64 CFG_SAVE_ID;
extern const u64 CONSOLE_UNIQUE_ID;
extern const ConsoleModelInfo CONSOLE_MODEL;
extern const u8 CONSOLE_LANGUAGE;
extern const char CONSOLE_USERNAME[0x14];
/// This will be initialized in the Interface constructor, and will be used when creating the block
extern UsernameBlock CONSOLE_USERNAME_BLOCK;
/// TODO(Subv): Find out what this actually is
extern const u8 SOUND_OUTPUT_MODE;
extern const u8 UNITED_STATES_COUNTRY_ID;
/// TODO(Subv): Find what the other bytes are
extern const ConsoleCountryInfo COUNTRY_INFO;
extern const std::array<float, 8> STEREO_CAMERA_SETTINGS;
static_assert(sizeof(STEREO_CAMERA_SETTINGS) == 0x20, "STEREO_CAMERA_SETTINGS must be exactly 0x20 bytes");
static_assert(sizeof(CONSOLE_UNIQUE_ID) == 8, "CONSOLE_UNIQUE_ID must be exactly 8 bytes");
static_assert(sizeof(CONSOLE_LANGUAGE) == 1, "CONSOLE_LANGUAGE must be exactly 1 byte");
static_assert(sizeof(SOUND_OUTPUT_MODE) == 1, "SOUND_OUTPUT_MODE must be exactly 1 byte");
/**
* Reads a block with the specified id and flag from the Config savegame buffer
* and writes the output to output.
* The input size must match exactly the size of the requested block
* @param block_id The id of the block we want to read
* @param size The size of the block we want to read
* @param flag The requested block must have this flag set
* @param output A pointer where we will write the read data
* @returns ResultCode indicating the result of the operation, 0 on success
*/
ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output);
/**
* Creates a block with the specified id and writes the input data to the cfg savegame buffer in memory.
* The config savegame file in the filesystem is not updated.
* @param block_id The id of the block we want to create
* @param size The size of the block we want to create
* @param flag The flags of the new block
* @param data A pointer containing the data we will write to the new block
* @returns ResultCode indicating the result of the operation, 0 on success
*/
ResultCode CreateConfigInfoBlk(u32 block_id, u16 size, u16 flags, const u8* data);
/**
* Deletes the config savegame file from the filesystem, the buffer in memory is not affected
* @returns ResultCode indicating the result of the operation, 0 on success
*/
ResultCode DeleteConfigNANDSaveFile();
/**
* Writes the config savegame memory buffer to the config savegame file in the filesystem
* @returns ResultCode indicating the result of the operation, 0 on success
*/
ResultCode UpdateConfigNANDSavegame();
/**
* Re-creates the config savegame file in memory and the filesystem with the default blocks
* @returns ResultCode indicating the result of the operation, 0 on success
*/
ResultCode FormatConfig();
/// Initialize the config service
void CFGInit();
/// Shutdown the config service
void CFGShutdown();
} // namespace CFG
} // namespace Service

View file

@ -0,0 +1,110 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/log.h"
#include "core/hle/hle.h"
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/cfg/cfg_i.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace CFG_I
namespace CFG_I {
/**
* CFG_I::GetConfigInfoBlk8 service function
* This function is called by two command headers,
* there appears to be no difference between them according to 3dbrew
* Inputs:
* 0 : 0x04010082 / 0x08010082
* 1 : Size
* 2 : Block ID
* 3 : Descriptor for the output buffer
* 4 : Output buffer pointer
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
static void GetConfigInfoBlk8(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 size = cmd_buffer[1];
u32 block_id = cmd_buffer[2];
u8* data_pointer = Memory::GetPointer(cmd_buffer[4]);
if (data_pointer == nullptr) {
cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
return;
}
cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x8, data_pointer).raw;
}
/**
* CFG_I::UpdateConfigNANDSavegame service function
* This function is called by two command headers,
* there appears to be no difference between them according to 3dbrew
* Inputs:
* 0 : 0x04030000 / 0x08030000
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
static void UpdateConfigNANDSavegame(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
cmd_buffer[1] = Service::CFG::UpdateConfigNANDSavegame().raw;
}
/**
* CFG_I::FormatConfig service function
* Inputs:
* 0 : 0x08060000
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
static void FormatConfig(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
cmd_buffer[1] = Service::CFG::FormatConfig().raw;
}
const Interface::FunctionInfo FunctionTable[] = {
{0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
{0x04020082, nullptr, "SetConfigInfoBlk4"},
{0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
{0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
{0x04050000, nullptr, "GetLocalFriendCodeSeed"},
{0x04060000, nullptr, "SecureInfoGetRegion"},
{0x04070000, nullptr, "SecureInfoGetByte101"},
{0x04080042, nullptr, "SecureInfoGetSerialNo"},
{0x04090000, nullptr, "UpdateConfigBlk00040003"},
{0x08010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
{0x08020082, nullptr, "SetConfigInfoBlk4"},
{0x08030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
{0x080400C2, nullptr, "CreateConfigInfoBlk"},
{0x08050000, nullptr, "DeleteConfigNANDSavefile"},
{0x08060000, FormatConfig, "FormatConfig"},
{0x08080000, nullptr, "UpdateConfigBlk1"},
{0x08090000, nullptr, "UpdateConfigBlk2"},
{0x080A0000, nullptr, "UpdateConfigBlk3"},
{0x080B0082, nullptr, "SetGetLocalFriendCodeSeedData"},
{0x080C0042, nullptr, "SetLocalFriendCodeSeedSignature"},
{0x080D0000, nullptr, "DeleteCreateNANDLocalFriendCodeSeed"},
{0x080E0000, nullptr, "VerifySigLocalFriendCodeSeed"},
{0x080F0042, nullptr, "GetLocalFriendCodeSeedData"},
{0x08100000, nullptr, "GetLocalFriendCodeSeed"},
{0x08110084, nullptr, "SetSecureInfo"},
{0x08120000, nullptr, "DeleteCreateNANDSecureInfo"},
{0x08130000, nullptr, "VerifySigSecureInfo"},
{0x08140042, nullptr, "SecureInfoGetData"},
{0x08150042, nullptr, "SecureInfoGetSignature"},
{0x08160000, nullptr, "SecureInfoGetRegion"},
{0x08170000, nullptr, "SecureInfoGetByte101"},
{0x08180042, nullptr, "SecureInfoGetSerialNo"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
} // namespace

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -14,11 +14,7 @@ namespace CFG_I {
class Interface : public Service::Interface {
public:
Interface();
~Interface();
/**
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
std::string GetPortName() const override {
return "cfg:i";
}

View file

@ -0,0 +1,192 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/file_util.h"
#include "common/log.h"
#include "common/string_util.h"
#include "core/file_sys/archive_systemsavedata.h"
#include "core/hle/hle.h"
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/cfg/cfg_u.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace CFG_U
namespace CFG_U {
// TODO(Link Mauve): use a constexpr once MSVC starts supporting it.
#define C(code) ((code)[0] | ((code)[1] << 8))
static const std::array<u16, 187> country_codes = {
0, C("JP"), 0, 0, 0, 0, 0, 0, // 0-7
C("AI"), C("AG"), C("AR"), C("AW"), C("BS"), C("BB"), C("BZ"), C("BO"), // 8-15
C("BR"), C("VG"), C("CA"), C("KY"), C("CL"), C("CO"), C("CR"), C("DM"), // 16-23
C("DO"), C("EC"), C("SV"), C("GF"), C("GD"), C("GP"), C("GT"), C("GY"), // 24-31
C("HT"), C("HN"), C("JM"), C("MQ"), C("MX"), C("MS"), C("AN"), C("NI"), // 32-39
C("PA"), C("PY"), C("PE"), C("KN"), C("LC"), C("VC"), C("SR"), C("TT"), // 40-47
C("TC"), C("US"), C("UY"), C("VI"), C("VE"), 0, 0, 0, // 48-55
0, 0, 0, 0, 0, 0, 0, 0, // 56-63
C("AL"), C("AU"), C("AT"), C("BE"), C("BA"), C("BW"), C("BG"), C("HR"), // 64-71
C("CY"), C("CZ"), C("DK"), C("EE"), C("FI"), C("FR"), C("DE"), C("GR"), // 72-79
C("HU"), C("IS"), C("IE"), C("IT"), C("LV"), C("LS"), C("LI"), C("LT"), // 80-87
C("LU"), C("MK"), C("MT"), C("ME"), C("MZ"), C("NA"), C("NL"), C("NZ"), // 88-95
C("NO"), C("PL"), C("PT"), C("RO"), C("RU"), C("RS"), C("SK"), C("SI"), // 96-103
C("ZA"), C("ES"), C("SZ"), C("SE"), C("CH"), C("TR"), C("GB"), C("ZM"), // 104-111
C("ZW"), C("AZ"), C("MR"), C("ML"), C("NE"), C("TD"), C("SD"), C("ER"), // 112-119
C("DJ"), C("SO"), C("AD"), C("GI"), C("GG"), C("IM"), C("JE"), C("MC"), // 120-127
C("TW"), 0, 0, 0, 0, 0, 0, 0, // 128-135
C("KR"), 0, 0, 0, 0, 0, 0, 0, // 136-143
C("HK"), C("MO"), 0, 0, 0, 0, 0, 0, // 144-151
C("ID"), C("SG"), C("TH"), C("PH"), C("MY"), 0, 0, 0, // 152-159
C("CN"), 0, 0, 0, 0, 0, 0, 0, // 160-167
C("AE"), C("IN"), C("EG"), C("OM"), C("QA"), C("KW"), C("SA"), C("SY"), // 168-175
C("BH"), C("JO"), 0, 0, 0, 0, 0, 0, // 176-183
C("SM"), C("VA"), C("BM") // 184-186
};
#undef C
/**
* CFG_User::GetCountryCodeString service function
* Inputs:
* 1 : Country Code ID
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Country's 2-char string
*/
static void GetCountryCodeString(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 country_code_id = cmd_buffer[1];
if (country_code_id >= country_codes.size() || 0 == country_codes[country_code_id]) {
LOG_ERROR(Service_CFG, "requested country code id=%d is invalid", country_code_id);
cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw;
return;
}
cmd_buffer[1] = 0;
cmd_buffer[2] = country_codes[country_code_id];
}
/**
* CFG_User::GetCountryCodeID service function
* Inputs:
* 1 : Country Code 2-char string
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Country Code ID
*/
static void GetCountryCodeID(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u16 country_code = cmd_buffer[1];
u16 country_code_id = 0;
// The following algorithm will fail if the first country code isn't 0.
_dbg_assert_(Service_CFG, country_codes[0] == 0);
for (size_t id = 0; id < country_codes.size(); ++id) {
if (country_codes[id] == country_code) {
country_code_id = id;
break;
}
}
if (0 == country_code_id) {
LOG_ERROR(Service_CFG, "requested country code name=%c%c is invalid", country_code & 0xff, country_code >> 8);
cmd_buffer[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Config, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw;
cmd_buffer[2] = 0xFFFF;
return;
}
cmd_buffer[1] = 0;
cmd_buffer[2] = country_code_id;
}
/**
* CFG_User::GetConfigInfoBlk2 service function
* Inputs:
* 0 : 0x00010082
* 1 : Size
* 2 : Block ID
* 3 : Descriptor for the output buffer
* 4 : Output buffer pointer
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
static void GetConfigInfoBlk2(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 size = cmd_buffer[1];
u32 block_id = cmd_buffer[2];
u8* data_pointer = Memory::GetPointer(cmd_buffer[4]);
if (data_pointer == nullptr) {
cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
return;
}
cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x2, data_pointer).raw;
}
/**
* CFG_User::GetSystemModel service function
* Inputs:
* 0 : 0x00050000
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Model of the console
*/
static void GetSystemModel(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 data;
// TODO(Subv): Find out the correct error codes
cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(0x000F0004, 4, 0x8,
reinterpret_cast<u8*>(&data)).raw;
cmd_buffer[2] = data & 0xFF;
}
/**
* CFG_User::GetModelNintendo2DS service function
* Inputs:
* 0 : 0x00060000
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : 0 if the system is a Nintendo 2DS, 1 otherwise
*/
static void GetModelNintendo2DS(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 data;
// TODO(Subv): Find out the correct error codes
cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(0x000F0004, 4, 0x8,
reinterpret_cast<u8*>(&data)).raw;
u8 model = data & 0xFF;
if (model == Service::CFG::NINTENDO_2DS)
cmd_buffer[2] = 0;
else
cmd_buffer[2] = 1;
}
const Interface::FunctionInfo FunctionTable[] = {
{0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"},
{0x00020000, nullptr, "SecureInfoGetRegion"},
{0x00030000, nullptr, "GenHashConsoleUnique"},
{0x00040000, nullptr, "GetRegionCanadaUSA"},
{0x00050000, GetSystemModel, "GetSystemModel"},
{0x00060000, GetModelNintendo2DS, "GetModelNintendo2DS"},
{0x00070040, nullptr, "unknown"},
{0x00080080, nullptr, "unknown"},
{0x00090040, GetCountryCodeString, "GetCountryCodeString"},
{0x000A0040, GetCountryCodeID, "GetCountryCodeID"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
} // namespace

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -14,11 +14,7 @@ namespace CFG_U {
class Interface : public Service::Interface {
public:
Interface();
~Interface();
/**
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
std::string GetPortName() const override {
return "cfg:u";
}

View file

@ -1,59 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "common/log.h"
#include "core/hle/hle.h"
#include "core/hle/service/cfg_i.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace CFG_I
namespace CFG_I {
const Interface::FunctionInfo FunctionTable[] = {
{0x04010082, nullptr, "GetConfigInfoBlk8"},
{0x04020082, nullptr, "GetConfigInfoBlk4"},
{0x04030000, nullptr, "UpdateConfigNANDSavegame"},
{0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
{0x04050000, nullptr, "GetLocalFriendCodeSeed"},
{0x04060000, nullptr, "SecureInfoGetRegion"},
{0x04070000, nullptr, "SecureInfoGetByte101"},
{0x04080042, nullptr, "SecureInfoGetSerialNo"},
{0x04090000, nullptr, "UpdateConfigBlk00040003"},
{0x08010082, nullptr, "GetConfigInfoBlk8"},
{0x08020082, nullptr, "GetConfigInfoBlk4"},
{0x08030000, nullptr, "UpdateConfigNANDSavegame"},
{0x080400C2, nullptr, "CreateConfigInfoBlk"},
{0x08050000, nullptr, "DeleteConfigNANDSavefile"},
{0x08060000, nullptr, "FormatConfig"},
{0x08070000, nullptr, "Unknown"},
{0x08080000, nullptr, "UpdateConfigBlk1"},
{0x08090000, nullptr, "UpdateConfigBlk2"},
{0x080A0000, nullptr, "UpdateConfigBlk3"},
{0x080B0082, nullptr, "SetGetLocalFriendCodeSeedData"},
{0x080C0042, nullptr, "SetLocalFriendCodeSeedSignature"},
{0x080D0000, nullptr, "DeleteCreateNANDLocalFriendCodeSeed"},
{0x080E0000, nullptr, "VerifySigLocalFriendCodeSeed"},
{0x080F0042, nullptr, "GetLocalFriendCodeSeedData"},
{0x08100000, nullptr, "GetLocalFriendCodeSeed"},
{0x08110084, nullptr, "SetSecureInfo"},
{0x08120000, nullptr, "DeleteCreateNANDSecureInfo"},
{0x08130000, nullptr, "VerifySigSecureInfo"},
{0x08140042, nullptr, "SecureInfoGetData"},
{0x08150042, nullptr, "SecureInfoGetSignature"},
{0x08160000, nullptr, "SecureInfoGetRegion"},
{0x08170000, nullptr, "SecureInfoGetByte101"},
{0x08180042, nullptr, "SecureInfoGetSerialNo"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
Interface::~Interface() {
}
} // namespace

View file

@ -1,36 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "common/log.h"
#include "core/hle/hle.h"
#include "core/hle/service/cfg_u.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace CFG_U
namespace CFG_U {
const Interface::FunctionInfo FunctionTable[] = {
{0x00010082, nullptr, "GetConfigInfoBlk2"},
{0x00020000, nullptr, "SecureInfoGetRegion"},
{0x00030000, nullptr, "GenHashConsoleUnique"},
{0x00040000, nullptr, "GetRegionCanadaUSA"},
{0x00050000, nullptr, "GetSystemModel"},
{0x00060000, nullptr, "GetModelNintendo2DS"},
{0x00070040, nullptr, "unknown"},
{0x00080080, nullptr, "unknown"},
{0x00090080, nullptr, "GetCountryCodeString"},
{0x000A0040, nullptr, "GetCountryCodeID"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
Interface::~Interface() {
}
} // namespace

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/log.h"
@ -33,7 +33,4 @@ Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
Interface::~Interface() {
}
} // namespace

View file

@ -1,5 +1,5 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
@ -14,11 +14,7 @@ namespace CSND_SND {
class Interface : public Service::Interface {
public:
Interface();
~Interface();
/**
* Gets the string port name used by CTROS for the service
* @return Port name of service
*/
std::string GetPortName() const override {
return "csnd:SND";
}

View file

@ -1,9 +1,10 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/log.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/dsp_dsp.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -11,42 +12,182 @@
namespace DSP_DSP {
static u32 read_pipe_count;
static Handle semaphore_event;
static Handle interrupt_event;
/**
* DSP_DSP::ConvertProcessAddressFromDspDram service function
* Inputs:
* 1 : Address
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : (inaddr << 1) + 0x1FF40000 (where 0x1FF00000 is the DSP RAM address)
*/
void ConvertProcessAddressFromDspDram(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 addr = cmd_buff[1];
cmd_buff[1] = 0; // No error
cmd_buff[2] = (addr << 1) + (Memory::DSP_MEMORY_VADDR + 0x40000);
LOG_WARNING(Service_DSP, "(STUBBED) called with address %u", addr);
}
/**
* DSP_DSP::LoadComponent service function
* Inputs:
* 1 : Size
* 2 : Unknown (observed only half word used)
* 3 : Unknown (observed only half word used)
* 4 : (size << 4) | 0xA
* 5 : Buffer address
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Component loaded, 0 on not loaded, 1 on loaded
*/
void LoadComponent(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = 0; // No error
cmd_buff[2] = 1; // Pretend that we actually loaded the DSP firmware
// TODO(bunnei): Implement real DSP firmware loading
LOG_WARNING(Service_DSP, "(STUBBED) called");
}
/**
* DSP_DSP::GetSemaphoreEventHandle service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 3 : Semaphore event handle
*/
void GetSemaphoreEventHandle(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = 0; // No error
cmd_buff[3] = semaphore_event; // Event handle
LOG_WARNING(Service_DSP, "(STUBBED) called");
}
/**
* DSP_DSP::RegisterInterruptEvents service function
* Inputs:
* 1 : Parameter 0 (purpose unknown)
* 2 : Parameter 1 (purpose unknown)
* 4 : Interrupt event handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void RegisterInterruptEvents(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
interrupt_event = static_cast<Handle>(cmd_buff[4]);
cmd_buff[1] = 0; // No error
LOG_WARNING(Service_DSP, "(STUBBED) called");
}
/**
* DSP_DSP::WriteReg0x10 service function
* Inputs:
* 1 : Unknown (observed only half word used)
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void WriteReg0x10(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
Kernel::SignalEvent(interrupt_event);
cmd_buff[1] = 0; // No error
LOG_WARNING(Service_DSP, "(STUBBED) called");
}
/**
* DSP_DSP::ReadPipeIfPossible service function
* Inputs:
* 1 : Unknown
* 2 : Unknown
* 3 : Size in bytes of read (observed only lower half word used)
* 0x41 : Virtual address to read from DSP pipe to in memory
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Number of bytes read from pipe
*/
void ReadPipeIfPossible(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size
VAddr addr = cmd_buff[0x41];
// Canned DSP responses that games expect. These were taken from HW by 3dmoo team.
// TODO: Remove this hack :)
static const std::array<u16, 16> canned_read_pipe = {
0x000F, 0xBFFF, 0x9E8E, 0x8680, 0xA78E, 0x9430, 0x8400, 0x8540,
0x948E, 0x8710, 0x8410, 0xA90E, 0xAA0E, 0xAACE, 0xAC4E, 0xAC58
};
u32 initial_size = read_pipe_count;
for (unsigned offset = 0; offset < size; offset += sizeof(u16)) {
if (read_pipe_count < canned_read_pipe.size()) {
Memory::Write16(addr + offset, canned_read_pipe[read_pipe_count]);
read_pipe_count++;
} else {
LOG_ERROR(Service_DSP, "canned read pipe log exceeded!");
break;
}
}
cmd_buff[1] = 0; // No error
cmd_buff[2] = (read_pipe_count - initial_size) * sizeof(u16);
LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%08X, buffer=0x%08X", size, addr);
}
const Interface::FunctionInfo FunctionTable[] = {
{0x00010040, nullptr, "RecvData"},
{0x00020040, nullptr, "RecvDataIsReady"},
{0x00030080, nullptr, "SendData"},
{0x00040040, nullptr, "SendDataIsEmpty"},
{0x00070040, nullptr, "WriteReg0x10"},
{0x00080000, nullptr, "GetSemaphore"},
{0x00090040, nullptr, "ClearSemaphore"},
{0x000B0000, nullptr, "CheckSemaphoreRequest"},
{0x000C0040, nullptr, "ConvertProcessAddressFromDspDram"},
{0x000D0082, nullptr, "WriteProcessPipe"},
{0x001000C0, nullptr, "ReadPipeIfPossible"},
{0x001100C2, nullptr, "LoadComponent"},
{0x00120000, nullptr, "UnloadComponent"},
{0x00130082, nullptr, "FlushDataCache"},
{0x00140082, nullptr, "InvalidateDCache"},
{0x00150082, nullptr, "RegisterInterruptEvents"},
{0x00160000, nullptr, "GetSemaphoreEventHandle"},
{0x00170040, nullptr, "SetSemaphoreMask"},
{0x00180040, nullptr, "GetPhysicalAddress"},
{0x00190040, nullptr, "GetVirtualAddress"},
{0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"},
{0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"},
{0x001C0082, nullptr, "SetIirFilterEQ"},
{0x001F0000, nullptr, "GetHeadphoneStatus"},
{0x00210000, nullptr, "GetIsDspOccupied"},
{0x00010040, nullptr, "RecvData"},
{0x00020040, nullptr, "RecvDataIsReady"},
{0x00030080, nullptr, "SendData"},
{0x00040040, nullptr, "SendDataIsEmpty"},
{0x00070040, WriteReg0x10, "WriteReg0x10"},
{0x00080000, nullptr, "GetSemaphore"},
{0x00090040, nullptr, "ClearSemaphore"},
{0x000B0000, nullptr, "CheckSemaphoreRequest"},
{0x000C0040, ConvertProcessAddressFromDspDram, "ConvertProcessAddressFromDspDram"},
{0x000D0082, nullptr, "WriteProcessPipe"},
{0x001000C0, ReadPipeIfPossible, "ReadPipeIfPossible"},
{0x001100C2, LoadComponent, "LoadComponent"},
{0x00120000, nullptr, "UnloadComponent"},
{0x00130082, nullptr, "FlushDataCache"},
{0x00140082, nullptr, "InvalidateDCache"},
{0x00150082, RegisterInterruptEvents, "RegisterInterruptEvents"},
{0x00160000, GetSemaphoreEventHandle, "GetSemaphoreEventHandle"},
{0x00170040, nullptr, "SetSemaphoreMask"},
{0x00180040, nullptr, "GetPhysicalAddress"},
{0x00190040, nullptr, "GetVirtualAddress"},
{0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"},
{0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"},
{0x001C0082, nullptr, "SetIirFilterEQ"},
{0x001F0000, nullptr, "GetHeadphoneStatus"},
{0x00210000, nullptr, "GetIsDspOccupied"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
semaphore_event = Kernel::CreateEvent(RESETTYPE_ONESHOT, "DSP_DSP::semaphore_event");
interrupt_event = 0;
read_pipe_count = 0;
Register(FunctionTable, ARRAY_SIZE(FunctionTable));
}
Interface::~Interface() {
}
} // namespace

Some files were not shown because too many files have changed in this diff Show more