1
0
Fork 0
mirror of https://github.com/Atmosphere-NX/Atmosphere.git synced 2024-12-18 08:22:04 +00:00

fusee_cpp: cache cleanup, confirmed working on hardware

This commit is contained in:
Michael Scire 2021-08-23 10:13:46 -07:00 committed by SciresM
parent 5cff5e629b
commit e7d7d8adfb
9 changed files with 110 additions and 121 deletions

View file

@ -58,8 +58,8 @@ namespace ams::sc7fw {
void EnterSc7() {
/* Disable read buffering and write buffering in the BPMP cache. */
reg::ReadWrite(AVP_CACHE_ADDRESS(AVP_CACHE_CONFIG), AVP_CACHE_REG_BITS_ENUM(DISABLE_WB, TRUE),
AVP_CACHE_REG_BITS_ENUM(DISABLE_RB, TRUE));
reg::ReadWrite(AVP_CACHE_ADDR(AVP_CACHE_CONFIG), AVP_CACHE_REG_BITS_ENUM(CONFIG_DISABLE_WB, TRUE),
AVP_CACHE_REG_BITS_ENUM(CONFIG_DISABLE_RB, TRUE));
/* Ensure the CPU Rail is turned off. */
DisableCrail();

View file

@ -44,6 +44,7 @@ namespace ams::nxboot {
*reinterpret_cast<volatile u32 *>(0x4003800C) = static_cast<u32>(sm);
*reinterpret_cast<volatile u32 *>(0x40038010) = static_cast<u32>(bw);
}
*reinterpret_cast<volatile u32 *>(0x7000E400) = 0x10;
}

View file

@ -328,6 +328,10 @@ namespace ams::nxboot {
clkrst::EnableI2c5Clock();
clkrst::EnableTzramClock();
/* Ensure avp cache is enabled, since we'll be using it. */
clkrst::EnableCache2Clock();
clkrst::EnableCram2Clock();
/* Initialize I2C5. */
i2c::Initialize(i2c::Port_5);

View file

@ -33,6 +33,9 @@ namespace ams::clkrst {
void EnableCldvfsClock();
void EnableTzramClock();
void EnableCache2Clock();
void EnableCram2Clock();
void EnableHost1xClock();
void EnableTsecClock();
void EnableSorSafeClock();

View file

@ -96,6 +96,9 @@ namespace ams::clkrst {
DEFINE_CLOCK_PARAMETERS_WITHOUT_CLKDIV(Sor0Clock, X, SOR0);
DEFINE_CLOCK_PARAMETERS_WITHOUT_CLKDIV(KfuseClock, H, KFUSE);
DEFINE_CLOCK_PARAMETERS_WITHOUT_CLKDIV(Cache2Clock, L, CACHE2);
DEFINE_CLOCK_PARAMETERS_WITHOUT_CLKDIV(Cram2Clock, U, CRAM2);
}
void SetRegisterAddress(uintptr_t address) {
@ -145,6 +148,14 @@ namespace ams::clkrst {
EnableClock(TzramClock);
}
void EnableCache2Clock() {
EnableClock(Cache2Clock);
}
void EnableCram2Clock() {
EnableClock(Cram2Clock);
}
void EnableHost1xClock() {
EnableClock(Host1xClock);
}

View file

@ -1,110 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#pragma once
#define AVP_CACHE_CONFIG (0x000)
#define AVP_CACHE_LOCK (0x004)
#define AVP_CACHE_SIZE (0x00C)
#define AVP_CACHE_LFSR (0x010)
#define AVP_CACHE_TAG_STATUS (0x014)
#define AVP_CACHE_CLKEN_OVERRIDE (0x018)
#define AVP_CACHE_MAINT_0 (0x020)
#define AVP_CACHE_MAINT_1 (0x024)
#define AVP_CACHE_MAINT_2 (0x028)
#define AVP_CACHE_INT_MASK (0x040)
#define AVP_CACHE_INT_CLEAR (0x044)
#define AVP_CACHE_INT_RAW_EVENT (0x048)
#define AVP_CACHE_INT_STATUS (0x04C)
#define AVP_CACHE_RB_CFG (0x080)
#define AVP_CACHE_WB_CFG (0x084)
#define AVP_CACHE_MMU_FALLBACK_ENTRY (0x0A0)
#define AVP_CACHE_MMU_SHADOW_COPY_MASK_0 (0x0A4)
#define AVP_CACHE_MMU_CFG (0x0AC)
#define AVP_CACHE_MMU_CMD (0x0B0)
#define AVP_CACHE_MMU_ABORT_STAT (0x0B4)
#define AVP_CACHE_MMU_ABORT_ADDR (0x0B8)
#define AVP_CACHE_MMU_ACTIVE_ENTRIES (0x0BC)
#define AVP_CACHE_MMU_SHADOW_ENTRY_0_MIN_ADDR (0x400)
#define AVP_CACHE_MMU_SHADOW_ENTRY_0_MAX_ADDR (0x404)
#define AVP_CACHE_MMU_SHADOW_ENTRY_0_CFG (0x408)
#define AVP_CACHE_MMU_SHADOW_ENTRY_1_MIN_ADDR (0x410)
#define AVP_CACHE_MMU_SHADOW_ENTRY_1_MAX_ADDR (0x414)
#define AVP_CACHE_MMU_SHADOW_ENTRY_1_CFG (0x418)
#define AVP_CACHE_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (AVP_CACHE, NAME)
#define AVP_CACHE_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (AVP_CACHE, NAME, VALUE)
#define AVP_CACHE_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (AVP_CACHE, NAME, ENUM)
#define AVP_CACHE_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(AVP_CACHE, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
#define DEFINE_AVP_CACHE_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (AVP_CACHE, NAME, __OFFSET__, __WIDTH__)
#define DEFINE_AVP_CACHE_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (AVP_CACHE, NAME, __OFFSET__, ZERO, ONE)
#define DEFINE_AVP_CACHE_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (AVP_CACHE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
#define DEFINE_AVP_CACHE_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(AVP_CACHE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_AVP_CACHE_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (AVP_CACHE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
DEFINE_AVP_CACHE_REG_BIT_ENUM(CONFIG_ENABLE_CACHE, 0, FALSE, TRUE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(CONFIG_FORCE_WRITE_THROUGH, 3, FALSE, TRUE);
DEFINE_AVP_CACHE_REG_TWO_BIT_ENUM(CONFIG_MMU_TAG_MODE, 8, PARALLEL, TAG_FIRST, MMU_FIRST, RSVD3);
DEFINE_AVP_CACHE_REG_BIT_ENUM(CONFIG_TAG_CHECK_ABORT_ON_ERROR, 14, FALSE, TRUE);
DEFINE_AVP_CACHE_REG(MAINT_2_OPCODE, 0, 8);
DEFINE_AVP_CACHE_REG(MAINT_2_WAY_BITMAP, 8, 4);
enum AVP_CACHE_MAINT_OPCODE : u32 {
AVP_CACHE_MAINT_OPCODE_NOP = 0,
AVP_CACHE_MAINT_OPCODE_CLEAN_PHY = 1,
AVP_CACHE_MAINT_OPCODE_INVALID_PHY = 2,
AVP_CACHE_MAINT_OPCODE_CLEAN_INVALID_PHY = 3,
AVP_CACHE_MAINT_OPCODE_CLEAN_LINE = 9,
AVP_CACHE_MAINT_OPCODE_INVALID_LINE = 10,
AVP_CACHE_MAINT_OPCODE_CLEAN_INVALID_LINE = 11,
AVP_CACHE_MAINT_OPCODE_CLEAN_WAY = 17,
AVP_CACHE_MAINT_OPCODE_INVALID_WAY = 18,
AVP_CACHE_MAINT_OPCODE_CLEAN_INVALID_WAY = 19,
};
DEFINE_AVP_CACHE_REG_BIT_ENUM(INT_CLEAR_MAINTENANCE_DONE, 0, FALSE, TRUE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(INT_RAW_EVENT_MAINTENANCE_DONE, 0, FALSE, TRUE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(INT_STATUS_MAINTENANCE_DONE, 0, FALSE, TRUE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_FALLBACK_ENTRY_CACHED, 0, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_FALLBACK_ENTRY_EXE_ENA, 1, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_FALLBACK_ENTRY_RD_ENA, 2, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_FALLBACK_ENTRY_WR_ENA, 3, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_CFG_BLOCK_MAIN_ENTRY_WR, 0, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_CFG_SEQ_ENA, 1, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_CFG_TLB_ENA, 2, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_CFG_SEQ_CHECK_ALL_ENTRIES, 3, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_CFG_ABORT_MODE, 4, STORE_FIRST, STORE_LAST);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_CFG_CLR_ABORT, 5, NOP, CLEAN);
DEFINE_AVP_CACHE_REG_TWO_BIT_ENUM(MMU_CMD_CMD, 0, NOP, INIT, COPY_SHADOW, RSVD3);
DEFINE_AVP_CACHE_REG_BIT_ENUM(SHADOW_ENTRY_CFG_CACHED, 0, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(SHADOW_ENTRY_CFG_EXE_ENA, 1, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(SHADOW_ENTRY_CFG_RD_ENA, 2, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(SHADOW_ENTRY_CFG_WR_ENA, 3, DISABLE, ENABLE);

View file

@ -14,7 +14,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <exosphere.hpp>
#include "avp_cache_registers.hpp"
namespace ams::hw::arch::arm {
@ -22,7 +21,7 @@ namespace ams::hw::arch::arm {
namespace {
constexpr inline uintptr_t AVP_CACHE = 0x50040000;
constexpr inline uintptr_t AVP_CACHE = AVP_CACHE_ADDR(0);
ALWAYS_INLINE bool IsLargeBuffer(size_t size) {
/* From TRM: For very large physical buffers or when the full cache needs to be cleared, */
@ -122,6 +121,8 @@ namespace ams::hw::arch::arm {
AVP_CACHE_REG_BITS_ENUM(SHADOW_ENTRY_CFG_RD_ENA, ENABLE),
AVP_CACHE_REG_BITS_ENUM(SHADOW_ENTRY_CFG_EXE_ENA, ENABLE),
AVP_CACHE_REG_BITS_ENUM(SHADOW_ENTRY_CFG_CACHED, ENABLE));
reg::SetBits(AVP_CACHE + AVP_CACHE_MMU_SHADOW_COPY_MASK_0, (1 << 0));
}
/* Add IRAM as index 1, RWX/Cached. */
@ -133,10 +134,9 @@ namespace ams::hw::arch::arm {
AVP_CACHE_REG_BITS_ENUM(SHADOW_ENTRY_CFG_RD_ENA, ENABLE),
AVP_CACHE_REG_BITS_ENUM(SHADOW_ENTRY_CFG_EXE_ENA, ENABLE),
AVP_CACHE_REG_BITS_ENUM(SHADOW_ENTRY_CFG_CACHED, ENABLE));
}
/* Set index 0/1 in shadow copy mask. */
reg::Write(AVP_CACHE + AVP_CACHE_MMU_SHADOW_COPY_MASK_0, 0b11);
reg::SetBits(AVP_CACHE + AVP_CACHE_MMU_SHADOW_COPY_MASK_0, (1 << 1));
}
/* Issue copy shadow mmu command. */
reg::Write(AVP_CACHE + AVP_CACHE_MMU_CMD, AVP_CACHE_REG_BITS_ENUM(MMU_CMD_CMD, COPY_SHADOW));

View file

@ -21,9 +21,38 @@
#include <vapours/results.hpp>
#include <vapours/reg.hpp>
#define AVP_CACHE_ADDRESS(x) (0x50040000 + x)
#define AVP_CACHE_ADDR(n) (0x50040000 + n)
#define AVP_CACHE_CONFIG (0x000)
#define AVP_CACHE_CONFIG (0x000)
#define AVP_CACHE_LOCK (0x004)
#define AVP_CACHE_SIZE (0x00C)
#define AVP_CACHE_LFSR (0x010)
#define AVP_CACHE_TAG_STATUS (0x014)
#define AVP_CACHE_CLKEN_OVERRIDE (0x018)
#define AVP_CACHE_MAINT_0 (0x020)
#define AVP_CACHE_MAINT_1 (0x024)
#define AVP_CACHE_MAINT_2 (0x028)
#define AVP_CACHE_INT_MASK (0x040)
#define AVP_CACHE_INT_CLEAR (0x044)
#define AVP_CACHE_INT_RAW_EVENT (0x048)
#define AVP_CACHE_INT_STATUS (0x04C)
#define AVP_CACHE_RB_CFG (0x080)
#define AVP_CACHE_WB_CFG (0x084)
#define AVP_CACHE_MMU_FALLBACK_ENTRY (0x0A0)
#define AVP_CACHE_MMU_SHADOW_COPY_MASK_0 (0x0A4)
#define AVP_CACHE_MMU_CFG (0x0AC)
#define AVP_CACHE_MMU_CMD (0x0B0)
#define AVP_CACHE_MMU_ABORT_STAT (0x0B4)
#define AVP_CACHE_MMU_ABORT_ADDR (0x0B8)
#define AVP_CACHE_MMU_ACTIVE_ENTRIES (0x0BC)
#define AVP_CACHE_MMU_SHADOW_ENTRY_0_MIN_ADDR (0x400)
#define AVP_CACHE_MMU_SHADOW_ENTRY_0_MAX_ADDR (0x404)
#define AVP_CACHE_MMU_SHADOW_ENTRY_0_CFG (0x408)
#define AVP_CACHE_MMU_SHADOW_ENTRY_1_MIN_ADDR (0x410)
#define AVP_CACHE_MMU_SHADOW_ENTRY_1_MAX_ADDR (0x414)
#define AVP_CACHE_MMU_SHADOW_ENTRY_1_CFG (0x418)
#define AVP_CACHE_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (AVP_CACHE, NAME)
#define AVP_CACHE_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (AVP_CACHE, NAME, VALUE)
@ -36,5 +65,53 @@
#define DEFINE_AVP_CACHE_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(AVP_CACHE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
#define DEFINE_AVP_CACHE_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (AVP_CACHE, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
DEFINE_AVP_CACHE_REG_BIT_ENUM(DISABLE_WB, 10, FALSE, TRUE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(DISABLE_RB, 11, FALSE, TRUE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(CONFIG_ENABLE_CACHE, 0, FALSE, TRUE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(CONFIG_FORCE_WRITE_THROUGH, 3, FALSE, TRUE);
DEFINE_AVP_CACHE_REG_TWO_BIT_ENUM(CONFIG_MMU_TAG_MODE, 8, PARALLEL, TAG_FIRST, MMU_FIRST, RSVD3);
DEFINE_AVP_CACHE_REG_BIT_ENUM(CONFIG_DISABLE_WB, 10, FALSE, TRUE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(CONFIG_DISABLE_RB, 11, FALSE, TRUE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(CONFIG_TAG_CHECK_ABORT_ON_ERROR, 14, FALSE, TRUE);
DEFINE_AVP_CACHE_REG(MAINT_2_OPCODE, 0, 8);
DEFINE_AVP_CACHE_REG(MAINT_2_WAY_BITMAP, 8, 4);
enum AVP_CACHE_MAINT_OPCODE : u32 {
AVP_CACHE_MAINT_OPCODE_NOP = 0,
AVP_CACHE_MAINT_OPCODE_CLEAN_PHY = 1,
AVP_CACHE_MAINT_OPCODE_INVALID_PHY = 2,
AVP_CACHE_MAINT_OPCODE_CLEAN_INVALID_PHY = 3,
AVP_CACHE_MAINT_OPCODE_CLEAN_LINE = 9,
AVP_CACHE_MAINT_OPCODE_INVALID_LINE = 10,
AVP_CACHE_MAINT_OPCODE_CLEAN_INVALID_LINE = 11,
AVP_CACHE_MAINT_OPCODE_CLEAN_WAY = 17,
AVP_CACHE_MAINT_OPCODE_INVALID_WAY = 18,
AVP_CACHE_MAINT_OPCODE_CLEAN_INVALID_WAY = 19,
};
DEFINE_AVP_CACHE_REG_BIT_ENUM(INT_CLEAR_MAINTENANCE_DONE, 0, FALSE, TRUE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(INT_RAW_EVENT_MAINTENANCE_DONE, 0, FALSE, TRUE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(INT_STATUS_MAINTENANCE_DONE, 0, FALSE, TRUE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_FALLBACK_ENTRY_CACHED, 0, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_FALLBACK_ENTRY_EXE_ENA, 1, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_FALLBACK_ENTRY_RD_ENA, 2, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_FALLBACK_ENTRY_WR_ENA, 3, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_CFG_BLOCK_MAIN_ENTRY_WR, 0, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_CFG_SEQ_ENA, 1, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_CFG_TLB_ENA, 2, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_CFG_SEQ_CHECK_ALL_ENTRIES, 3, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_CFG_ABORT_MODE, 4, STORE_FIRST, STORE_LAST);
DEFINE_AVP_CACHE_REG_BIT_ENUM(MMU_CFG_CLR_ABORT, 5, NOP, CLEAN);
DEFINE_AVP_CACHE_REG_TWO_BIT_ENUM(MMU_CMD_CMD, 0, NOP, INIT, COPY_SHADOW, RSVD3);
DEFINE_AVP_CACHE_REG_BIT_ENUM(SHADOW_ENTRY_CFG_CACHED, 0, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(SHADOW_ENTRY_CFG_EXE_ENA, 1, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(SHADOW_ENTRY_CFG_RD_ENA, 2, DISABLE, ENABLE);
DEFINE_AVP_CACHE_REG_BIT_ENUM(SHADOW_ENTRY_CFG_WR_ENA, 3, DISABLE, ENABLE);

View file

@ -259,6 +259,9 @@ DEFINE_CLK_RST_REG_BIT_ENUM(PLLMB_BASE_PLLMB_ENABLE, 30, DISABLE, ENABLE);
#define CLK_RST_CONTROLLER_CLK_ENB_TZRAM_INDEX (0x1E)
#define CLK_RST_CONTROLLER_CLK_ENB_CACHE2_INDEX (0x1F)
#define CLK_RST_CONTROLLER_CLK_ENB_CRAM2_INDEX (0x18)
#define CLK_RST_CONTROLLER_CLK_ENB_SE_INDEX (0x1F)
#define CLK_RST_CONTROLLER_CLK_ENB_HOST1X_INDEX (0x1C)