You've already forked HackerSM64
mirror of
https://github.com/HackerN64/HackerSM64.git
synced 2026-01-21 10:35:32 -08:00
Add memory typedefs
This commit is contained in:
@@ -69,8 +69,19 @@ struct Controller {
|
||||
#endif
|
||||
};
|
||||
|
||||
// -- Booleans --
|
||||
// -- Memory --
|
||||
typedef u8 Byte;
|
||||
typedef u16 Halfword;
|
||||
typedef u32 Word;
|
||||
typedef u64 Doubleword;
|
||||
|
||||
typedef uintptr_t Address; //! TODO: 64 bit addressing mode.
|
||||
typedef u64 Register; //! TODO: 32 bit mode.
|
||||
|
||||
// -- String --
|
||||
typedef unsigned char* String;
|
||||
|
||||
// -- Booleans --
|
||||
typedef u8 Bool8;
|
||||
typedef u16 Bool16;
|
||||
typedef u32 Bool32;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
|
||||
_Bool gAddressSelectMenuOpen = FALSE;
|
||||
static uintptr_t sAddressSelectTarget = 0x00000000;
|
||||
static Address sAddressSelectTarget = 0x00000000;
|
||||
static s8 sAddressSelecCharIndex = 2;
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ void draw_address_select(void) {
|
||||
crash_screen_print((SCREEN_CENTER_X - TEXT_WIDTH(8 / 2) - TEXT_WIDTH(2)), (JUMP_MENU_Y1 + TEXT_HEIGHT(2)), (STR_HEX_PREFIX STR_HEX_WORD), sAddressSelectTarget);
|
||||
|
||||
#ifdef INCLUDE_DEBUG_MAP
|
||||
uintptr_t checkAddr = sAddressSelectTarget;
|
||||
Address checkAddr = sAddressSelectTarget;
|
||||
const char* fname = parse_map(&checkAddr);
|
||||
if (fname != NULL) {
|
||||
// "[function name]"
|
||||
@@ -62,10 +62,10 @@ void crash_screen_select_address(void) {
|
||||
sAddressSelecCharIndex = ((sAddressSelecCharIndex + 1) & 0x7); // % 8
|
||||
}
|
||||
|
||||
uintptr_t nextSelectedAddress = sAddressSelectTarget;
|
||||
Address nextSelectedAddress = sAddressSelectTarget;
|
||||
u32 shift = ((32 - 4) - (sAddressSelecCharIndex * 4));
|
||||
u8 digit = GET_HEX_DIGIT(sAddressSelectTarget, shift);
|
||||
s8 new = digit;
|
||||
u8 new = digit;
|
||||
|
||||
if (gCSDirectionFlags.pressed.up) {
|
||||
// Increment the selected digit.
|
||||
@@ -129,7 +129,7 @@ void crash_screen_select_address(void) {
|
||||
}
|
||||
|
||||
// Open the jump to address popup.
|
||||
void open_address_select(uintptr_t dest) {
|
||||
void open_address_select(Address dest) {
|
||||
gAddressSelectMenuOpen = TRUE;
|
||||
sAddressSelectTarget = dest;
|
||||
}
|
||||
|
||||
@@ -31,4 +31,4 @@ extern _Bool gAddressSelectMenuOpen;
|
||||
|
||||
void draw_address_select(void);
|
||||
void crash_screen_select_address(void);
|
||||
void open_address_select(uintptr_t dest);
|
||||
void open_address_select(Address dest);
|
||||
|
||||
@@ -29,9 +29,9 @@ static _Bool glyph_to_hex(char* dest, unsigned char glyph) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static _Bool read_str_to_bytes(u8 dest[], const char* buf, u32 index, size_t numBytes) {
|
||||
static _Bool read_str_to_bytes(Byte dest[], const char* buf, u32 index, size_t numBytes) {
|
||||
for (u32 byteIndex = 0; byteIndex < numBytes; byteIndex++) {
|
||||
u8 retByte = 0x00;
|
||||
Byte retByte = 0x00;
|
||||
|
||||
for (int digit = 0; digit < 2; digit++) {
|
||||
unsigned char glyph = buf[index];
|
||||
@@ -57,10 +57,10 @@ static _Bool read_str_to_bytes(u8 dest[], const char* buf, u32 index, size_t num
|
||||
|
||||
static _Bool is_special_char(unsigned char glyph) {
|
||||
return (
|
||||
glyph == CHAR_ESCAPE ||
|
||||
glyph == CHAR_NEWLINE ||
|
||||
glyph == CHAR_RETURN ||
|
||||
glyph == CHAR_COLOR
|
||||
(glyph == CHAR_ESCAPE ) ||
|
||||
(glyph == CHAR_NEWLINE) ||
|
||||
(glyph == CHAR_RETURN ) ||
|
||||
(glyph == CHAR_COLOR )
|
||||
);
|
||||
}
|
||||
|
||||
@@ -135,10 +135,10 @@ static u32 get_next_word_length(PrintBuffer* buf, u32 index, size_t size) {
|
||||
while (index < size) {
|
||||
unsigned char glyph = buf[index].glyph;
|
||||
if (
|
||||
glyph == CHAR_NULL ||
|
||||
glyph == CHAR_SPACE ||
|
||||
glyph == CHAR_NEWLINE ||
|
||||
glyph == CHAR_RETURN
|
||||
(glyph == CHAR_NULL ) ||
|
||||
(glyph == CHAR_SPACE ) ||
|
||||
(glyph == CHAR_NEWLINE) ||
|
||||
(glyph == CHAR_RETURN )
|
||||
) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -48,8 +48,8 @@ static _Bool sFirstCrash = TRUE;
|
||||
struct CSThreadInfo* gActiveCSThreadInfo = NULL;
|
||||
OSThread* gCrashedThread = NULL;
|
||||
|
||||
uintptr_t gCrashAddress = 0x00000000; // Crashed thread PC.
|
||||
uintptr_t gSelectedAddress = 0x00000000; // Selected address for ram viewer and disasm pages.
|
||||
Address gCrashAddress = 0x00000000; // Crashed thread PC.
|
||||
Address gSelectedAddress = 0x00000000; // Selected address for ram viewer and disasm pages.
|
||||
|
||||
|
||||
void crash_screen_reinitialize(void) {
|
||||
|
||||
@@ -10,15 +10,14 @@
|
||||
#include "crash_print.h"
|
||||
#include "insn_disasm.h"
|
||||
#include "map_parser.h"
|
||||
#include "address_select.h"
|
||||
|
||||
|
||||
enum CrashScreenMessageIDs {
|
||||
CRASH_SCREEN_MSG_NONE,
|
||||
CRASH_SCREEN_MSG_CPU_BREAK,
|
||||
CRASH_SCREEN_MSG_SP_BREAK,
|
||||
CRASH_SCREEN_MSG_FAULT,
|
||||
CRASH_SCREEN_MSG_VI_VBLANK,
|
||||
CRASH_SCREEN_MSG_CPU_BREAK = OS_EVENT_CPU_BREAK,
|
||||
CRASH_SCREEN_MSG_SP_BREAK = OS_EVENT_SP_BREAK ,
|
||||
CRASH_SCREEN_MSG_FAULT = OS_EVENT_FAULT ,
|
||||
};
|
||||
|
||||
enum CrashScreenPages {
|
||||
@@ -42,8 +41,8 @@ extern enum CrashScreenPages gCSPageID;
|
||||
extern struct CSThreadInfo* gActiveCSThreadInfo;
|
||||
extern OSThread* gCrashedThread;
|
||||
|
||||
extern uintptr_t gCrashAddress;
|
||||
extern uintptr_t gSelectedAddress;
|
||||
extern Address gCrashAddress;
|
||||
extern Address gSelectedAddress;
|
||||
|
||||
|
||||
void create_crash_screen_thread(void);
|
||||
|
||||
@@ -15,12 +15,13 @@ typedef u32 CSFontRow;
|
||||
#define VALID_RAM_END RAM_END
|
||||
#define VALID_RAM_SIZE (u64)(VALID_RAM_END - VALID_RAM_START)
|
||||
|
||||
|
||||
#define NUM_CRASH_SCREEN_BUFFERS 3
|
||||
|
||||
|
||||
struct CSThreadInfo {
|
||||
/*0x000*/ OSThread thread; /*0x1B0*/
|
||||
/*0x1B0*/ u64 stack[THREAD2_STACK / sizeof(u64)]; /*0x400*/
|
||||
/*0x1B0*/ Register stack[THREAD2_STACK / sizeof(Register)]; /*0x400*/
|
||||
/*0x4B0*/ OSMesgQueue mesgQueue; /*0x18*/
|
||||
/*0x4C8*/ OSMesg mesg; /*0x04*/
|
||||
}; /*0x4CC*/
|
||||
@@ -42,9 +43,9 @@ struct CSPage {
|
||||
|
||||
// Time conversion macros
|
||||
#define FPS_COUNT 30
|
||||
#define FRAMES_TO_NESC(f) (((u64)(f) * 1000000000LL) / FPS_COUNT)
|
||||
#define FRAMES_TO_UESC(f) (((u64)(f) * 1000000LL) / FPS_COUNT)
|
||||
#define FRAMES_TO_CYCLES(f) (((u64)(f) * OS_CPU_COUNTER) / FPS_COUNT)
|
||||
#define NSEC_TO_FRAMES(n) (((u64)(n) * FPS_COUNT) / 1000000000LL)
|
||||
#define USEC_TO_FRAMES(n) (((u64)(n) * FPS_COUNT) / 1000000LL)
|
||||
#define CYCLES_TO_FRAMES(c) (((u64)(c) * FPS_COUNT) / OS_CPU_COUNTER)
|
||||
#define FRAMES_TO_NESC(f) (((OSTime)(f) * 1000000000LL) / FPS_COUNT)
|
||||
#define FRAMES_TO_UESC(f) (((OSTime)(f) * 1000000LL) / FPS_COUNT)
|
||||
#define FRAMES_TO_CYCLES(f) (((OSTime)(f) * OS_CPU_COUNTER) / FPS_COUNT)
|
||||
#define NSEC_TO_FRAMES(n) (((OSTime)(n) * FPS_COUNT) / 1000000000LL)
|
||||
#define USEC_TO_FRAMES(n) (((OSTime)(n) * FPS_COUNT) / 1000000LL)
|
||||
#define CYCLES_TO_FRAMES(c) (((OSTime)(c) * FPS_COUNT) / OS_CPU_COUNTER)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
/**
|
||||
* How to find instructions:
|
||||
*
|
||||
*
|
||||
* - If first 4 bits (cop_opcode) == 0100 (COP_OPCODE):
|
||||
* - The next 2 bits (cop_num) are coprocessor number, so use insn_db_cop(z)
|
||||
* - If the next bit (cop_bit) is set:
|
||||
@@ -331,7 +331,7 @@ static _Bool check_pseudo_instructions(const InsnTemplate** type, InsnData insn)
|
||||
static enum InsnType get_insn_type_and_list(InsnData insn, const InsnTemplate** checkInsn) {
|
||||
enum InsnType insnType = INSN_TYPE_OPCODE;
|
||||
*checkInsn = insn_db;
|
||||
|
||||
|
||||
if (insn.cop_opcode == COP_OPCODE) { // COPz
|
||||
if (insn.cop_num < ARRAY_COUNT(insn_db_cop_lists)) {
|
||||
*checkInsn = insn_db_cop_lists[insn.cop_num];
|
||||
@@ -466,21 +466,21 @@ s16 check_for_branch_offset(InsnData insn) {
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
uintptr_t get_branch_target_from_addr(uintptr_t addr) {
|
||||
Address get_branch_target_from_addr(Address addr) {
|
||||
if (!is_in_code_segment(addr)) {
|
||||
return addr;
|
||||
}
|
||||
|
||||
InsnData insn = { .raw = *(uintptr_t*)addr };
|
||||
InsnData insn = { .raw = *(Word*)addr };
|
||||
|
||||
if (insn.opcode == OPC_J || insn.opcode == OPC_JAL) {
|
||||
return PHYSICAL_TO_VIRTUAL(insn.instr_index * sizeof(uintptr_t));
|
||||
return PHYSICAL_TO_VIRTUAL(insn.instr_index * sizeof(InsnData));
|
||||
}
|
||||
|
||||
s16 branchOffset = check_for_branch_offset(insn);
|
||||
|
||||
if (branchOffset) {
|
||||
return (addr + (((s16)branchOffset + 1) * sizeof(uintptr_t)));
|
||||
return (addr + (((s16)branchOffset + 1) * sizeof(InsnData)));
|
||||
}
|
||||
|
||||
return addr;
|
||||
@@ -515,12 +515,12 @@ static char insn_name[INSN_NAME_DISPLAY_WIDTH] = "";
|
||||
#define STR_INSN_NAME "%-"TO_STRING2(INSN_NAME_DISPLAY_WIDTH)"s"
|
||||
#define STR_INSN_NAME_FORMAT STR_INSN_NAME_BASE"."STR_FORMAT
|
||||
|
||||
#define STR_IREG "%s" // Register
|
||||
#define STR_IMMEDIATE STR_HEX_PREFIX STR_HEX_HALFWORD // 0xI
|
||||
#define STR_OFFSET "%c"STR_IMMEDIATE // ±Offset
|
||||
#define STR_FUNCTION STR_HEX_PREFIX STR_HEX_WORD // Function address
|
||||
#define STR_IREG_BASE "("STR_IREG")" // Base register
|
||||
#define STR_FREG "F%02d" // Float Register
|
||||
#define STR_IREG "%s" // Register
|
||||
#define STR_IMMEDIATE STR_HEX_PREFIX STR_HEX_HALFWORD // 0xI
|
||||
#define STR_OFFSET "%c"STR_IMMEDIATE // ±Offset
|
||||
#define STR_FUNCTION STR_HEX_PREFIX STR_HEX_WORD // Function address
|
||||
#define STR_IREG_BASE "("STR_IREG")" // Base register
|
||||
#define STR_FREG "F%02d" // Float Register
|
||||
|
||||
char* insn_disasm(InsnData insn, const char** fname, _Bool showDestNames) {
|
||||
char* strp = &insn_as_string[0];
|
||||
@@ -619,10 +619,10 @@ char* insn_disasm(InsnData insn, const char** fname, _Bool showDestNames) {
|
||||
break;
|
||||
case CHAR_P_FUNC:
|
||||
check_color_change(&strp, &color, COLOR_RGBA32_CRASH_FUNCTION_NAME);
|
||||
uintptr_t target = PHYSICAL_TO_VIRTUAL(insn.instr_index * sizeof(uintptr_t));
|
||||
Address target = PHYSICAL_TO_VIRTUAL(insn.instr_index * sizeof(InsnData));
|
||||
#ifdef INCLUDE_DEBUG_MAP
|
||||
if (showDestNames && is_in_code_segment(target)) {
|
||||
uintptr_t tempTarget = target;
|
||||
Address tempTarget = target;
|
||||
*fname = parse_map(&tempTarget);
|
||||
// Only print as the function name if it's the exact starting address of the function.
|
||||
if (target != tempTarget) {
|
||||
|
||||
@@ -434,5 +434,5 @@ typedef struct PACKED {
|
||||
|
||||
|
||||
s16 check_for_branch_offset(InsnData insn);
|
||||
uintptr_t get_branch_target_from_addr(uintptr_t addr);
|
||||
Address get_branch_target_from_addr(Address addr);
|
||||
char* insn_disasm(InsnData insn, const char** fname, _Bool showDestNames);
|
||||
|
||||
@@ -48,11 +48,11 @@ TEXT_REGION_GROUP(common1)
|
||||
size_t gNumMapEntries = 0;
|
||||
|
||||
|
||||
static void headless_dma(uintptr_t devAddr, void* dramAddr, size_t size) {
|
||||
static void headless_dma(Address devAddr, void* dramAddr, size_t size) {
|
||||
while (IO_READ(PI_STATUS_REG) & (PI_STATUS_IO_BUSY | PI_STATUS_DMA_BUSY));
|
||||
|
||||
IO_WRITE(PI_DRAM_ADDR_REG, K0_TO_PHYS(dramAddr));
|
||||
IO_WRITE(PI_CART_ADDR_REG, K1_TO_PHYS((uintptr_t)osRomBase | devAddr));
|
||||
IO_WRITE(PI_CART_ADDR_REG, K1_TO_PHYS((Address)osRomBase | devAddr));
|
||||
IO_WRITE(PI_WR_LEN_REG, (size - 1));
|
||||
|
||||
while (IO_READ(PI_STATUS_REG) & (PI_STATUS_DMA_BUSY | PI_STATUS_ERROR));
|
||||
@@ -61,15 +61,15 @@ static void headless_dma(uintptr_t devAddr, void* dramAddr, size_t size) {
|
||||
void map_data_init(void) {
|
||||
gNumMapEntries = (gMapEntryEnd - gMapEntries);
|
||||
|
||||
uintptr_t start = (uintptr_t)_mapDataSegmentRomStart;
|
||||
uintptr_t end = (uintptr_t)_mapDataSegmentRomEnd;
|
||||
Address start = (Address)_mapDataSegmentRomStart;
|
||||
Address end = (Address)_mapDataSegmentRomEnd;
|
||||
|
||||
headless_dma((uintptr_t)_mapDataSegmentRomStart, (size_t*)(RAM_END - RAM_1MB), (end - start));
|
||||
headless_dma((Address)_mapDataSegmentRomStart, (size_t*)(RAM_END - RAM_1MB), (end - start));
|
||||
}
|
||||
|
||||
// Check whether the address is in a .text segment.
|
||||
//! TODO: do INCLUDE_DEBUG_MAP inside this instead of on this whole file.
|
||||
_Bool is_in_code_segment(uintptr_t addr) {
|
||||
_Bool is_in_code_segment(Address addr) {
|
||||
//! TODO: Allow reading .text memory outside 0x80000000-0x80800000.
|
||||
if (!IS_IN_RDRAM(addr)) {
|
||||
return FALSE;
|
||||
@@ -85,16 +85,17 @@ _Bool is_in_code_segment(uintptr_t addr) {
|
||||
}
|
||||
|
||||
const char* get_map_entry_name(const struct MapEntry* entry) {
|
||||
return (const char*)((uintptr_t)gMapStrings + entry->name_offset);
|
||||
return (const char*)((u32)gMapStrings + entry->name_offset);
|
||||
}
|
||||
|
||||
s32 get_map_entry_index(uintptr_t addr) {
|
||||
s32 get_map_entry_index(Address addr) {
|
||||
const struct MapEntry* entry = &gMapEntries[0];
|
||||
|
||||
for (size_t i = 0; i < gNumMapEntries; i++) {
|
||||
if ((addr >= entry->addr) && (addr < (entry->addr + entry->size))) {
|
||||
return i;
|
||||
}
|
||||
|
||||
entry++;
|
||||
}
|
||||
|
||||
@@ -102,11 +103,11 @@ s32 get_map_entry_index(uintptr_t addr) {
|
||||
}
|
||||
|
||||
// Changes 'addr' to the starting address of the function it's in and returns a pointer to the function name.
|
||||
const char* parse_map(uintptr_t* addr) {
|
||||
const char* parse_map(Address* addr) {
|
||||
#ifndef INCLUDE_DEBUG_MAP
|
||||
return NULL;
|
||||
#endif
|
||||
*addr = ALIGNFLOOR(*addr, sizeof(uintptr_t));
|
||||
*addr = ALIGNFLOOR(*addr, sizeof(Word));
|
||||
|
||||
if (!is_in_code_segment(*addr)) {
|
||||
return NULL;
|
||||
@@ -124,20 +125,20 @@ const char* parse_map(uintptr_t* addr) {
|
||||
}
|
||||
|
||||
// Check whether two addresses share the same function.
|
||||
_Bool is_in_same_function(uintptr_t oldPos, uintptr_t newPos) {
|
||||
if (oldPos == newPos) {
|
||||
_Bool is_in_same_function(Address addr1, Address addr2) {
|
||||
if (addr1 == addr2) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
oldPos = ALIGNFLOOR(oldPos, sizeof(uintptr_t));
|
||||
newPos = ALIGNFLOOR(newPos, sizeof(uintptr_t));
|
||||
addr1 = ALIGNFLOOR(addr1, sizeof(Word));
|
||||
addr2 = ALIGNFLOOR(addr2, sizeof(Word));
|
||||
|
||||
if (oldPos == newPos) {
|
||||
if (addr1 == addr2) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
parse_map(&oldPos);
|
||||
parse_map(&newPos);
|
||||
parse_map(&addr1);
|
||||
parse_map(&addr2);
|
||||
|
||||
return (oldPos == newPos);
|
||||
return (addr1 == addr2);
|
||||
}
|
||||
|
||||
@@ -6,19 +6,19 @@
|
||||
|
||||
|
||||
struct MapEntry {
|
||||
/*0x00*/ uintptr_t addr;
|
||||
/*0x00*/ Address addr;
|
||||
/*0x04*/ size_t size;
|
||||
/*0x08*/ uintptr_t name_offset;
|
||||
/*0x08*/ size_t name_offset;
|
||||
/*0x0C*/ size_t name_len;
|
||||
}; /*0x10*/
|
||||
|
||||
typedef struct {
|
||||
/*0x00*/ const uintptr_t start;
|
||||
/*0x04*/ const uintptr_t end;
|
||||
/*0x00*/ const Address start;
|
||||
/*0x04*/ const Address end;
|
||||
} MemoryRegion; /*0x08*/
|
||||
|
||||
#define EXTERN_TEXT_SYMBOL(name, side) \
|
||||
extern const u8 _##name##SegmentText##side[];
|
||||
extern const Byte _##name##SegmentText##side[];
|
||||
|
||||
#define EXTERN_TEXT_REGION(name) \
|
||||
EXTERN_TEXT_SYMBOL(name, Start) \
|
||||
@@ -73,7 +73,7 @@ EXTERN_GROUP_TEXT(common1)
|
||||
#undef DEFINE_LEVEL
|
||||
|
||||
#define MEMORY_REGION(start, end) \
|
||||
{ (const uintptr_t)(start), (const uintptr_t)(end) },
|
||||
{ (const Address)(start), (const Address)(end) },
|
||||
|
||||
#define TEXT_REGION(name) \
|
||||
MEMORY_REGION(_##name##SegmentTextStart, _##name##SegmentTextEnd)
|
||||
@@ -94,18 +94,18 @@ EXTERN_GROUP_TEXT(common1)
|
||||
|
||||
extern const struct MapEntry gMapEntries[];
|
||||
extern const struct MapEntry gMapEntryEnd[];
|
||||
extern const u8 gMapStrings[];
|
||||
extern const u8 gMapStringEnd[];
|
||||
extern const u8 _mapDataSegmentRomStart[];
|
||||
extern const u8 _mapDataSegmentRomEnd[];
|
||||
extern const Byte gMapStrings[];
|
||||
extern const Byte gMapStringEnd[];
|
||||
extern const Byte _mapDataSegmentRomStart[];
|
||||
extern const Byte _mapDataSegmentRomEnd[];
|
||||
|
||||
|
||||
extern size_t gNumMapEntries;
|
||||
|
||||
|
||||
void map_data_init(void);
|
||||
_Bool is_in_code_segment(uintptr_t addr);
|
||||
_Bool is_in_code_segment(Address addr);
|
||||
const char* get_map_entry_name(const struct MapEntry* entry);
|
||||
s32 get_map_entry_index(uintptr_t addr);
|
||||
const char* parse_map(uintptr_t* addr);
|
||||
_Bool is_in_same_function(uintptr_t oldPos, uintptr_t newPos);
|
||||
s32 get_map_entry_index(Address addr);
|
||||
const char* parse_map(Address* addr);
|
||||
_Bool is_in_same_function(Address addr1, Address addr2);
|
||||
|
||||
@@ -29,7 +29,7 @@ void assert_draw(void) { //! TODO: Make this scrollable if the message long enou
|
||||
// "No failed assert to report."
|
||||
crash_screen_print(TEXT_X(0), TEXT_Y(line), "No failed assert to report.");
|
||||
}
|
||||
|
||||
|
||||
gCSWordWrap = FALSE;
|
||||
|
||||
osWritebackDCacheAll();
|
||||
|
||||
@@ -50,7 +50,7 @@ static const char* sRegNames[29] = { //! TODO: Combine this with sCPURegisterNam
|
||||
|
||||
|
||||
// Print a fixed-point register.
|
||||
void crash_screen_print_reg(u32 x, u32 y, const char* name, uintptr_t addr) {
|
||||
void crash_screen_print_reg(u32 x, u32 y, const char* name, Address addr) {
|
||||
// "[register name]: [XXXXXXXX]"
|
||||
crash_screen_print(x, y,
|
||||
" "STR_COLOR_PREFIX"%s: "STR_COLOR_PREFIX STR_HEX_WORD,
|
||||
@@ -62,14 +62,14 @@ void crash_screen_print_reg(u32 x, u32 y, const char* name, uintptr_t addr) {
|
||||
// Print important fixed-point registers.
|
||||
void crash_screen_print_registers(__OSThreadContext* tc) {
|
||||
u32 regNum = 0;
|
||||
u64* reg = &tc->at;
|
||||
Register* reg = &tc->at;
|
||||
|
||||
crash_screen_print_reg(TEXT_X(0 * 15), TEXT_Y( 3), "PC", tc->pc);
|
||||
crash_screen_print_reg(TEXT_X(1 * 15), TEXT_Y( 3), "SR", tc->sr);
|
||||
crash_screen_print_reg(TEXT_X(2 * 15), TEXT_Y( 3), "VA", tc->badvaddr);
|
||||
crash_screen_print_reg(TEXT_X(0 * 15), TEXT_Y(3), "PC", tc->pc);
|
||||
crash_screen_print_reg(TEXT_X(1 * 15), TEXT_Y(3), "SR", tc->sr);
|
||||
crash_screen_print_reg(TEXT_X(2 * 15), TEXT_Y(3), "VA", tc->badvaddr);
|
||||
|
||||
if (IS_IN_RDRAM(tc->pc)) {
|
||||
crash_screen_print_reg(TEXT_X(2 * 15), TEXT_Y(13), "MM", *(uintptr_t*)tc->pc); // The raw data of the asm code that crashed.
|
||||
crash_screen_print_reg(TEXT_X(2 * 15), TEXT_Y(13), "MM", *(Word*)tc->pc); // The raw data of the asm code that crashed.
|
||||
}
|
||||
|
||||
osWritebackDCacheAll();
|
||||
@@ -110,12 +110,12 @@ void crash_screen_print_fpcsr(u32 x, u32 y, u32 fpcsr) {
|
||||
}
|
||||
|
||||
// Print a floating-point register.
|
||||
void crash_screen_print_float_reg(u32 x, u32 y, u32 regNum, f32* addr) {
|
||||
void crash_screen_print_float_reg(u32 x, u32 y, u32 regNum, f32* data) {
|
||||
// "[register name]:"
|
||||
size_t charX = crash_screen_print(x, y, STR_COLOR_PREFIX"F%02d:", COLOR_RGBA32_CRASH_REGISTER, regNum);
|
||||
x += TEXT_WIDTH(charX);
|
||||
|
||||
IEEE754_f32 val = { .asF32 = *addr };
|
||||
IEEE754_f32 val = { .asF32 = *data };
|
||||
|
||||
char prefix = '\0';
|
||||
|
||||
@@ -161,7 +161,7 @@ void crash_screen_print_float_registers(__OSThreadContext* tc) {
|
||||
void crash_context_draw(void) {
|
||||
__OSThreadContext* tc = &gCrashedThread->context;
|
||||
|
||||
s32 cause = ((tc->cause >> CAUSE_EXCSHIFT) & BITMASK(5));
|
||||
u32 cause = ((tc->cause >> CAUSE_EXCSHIFT) & BITMASK(5));
|
||||
// Make the last two cause case indexes sequential for array access.
|
||||
if (cause == (EXC_WATCH >> CAUSE_EXCSHIFT)) cause = 16;
|
||||
if (cause == (EXC_VCED >> CAUSE_EXCSHIFT)) cause = 17;
|
||||
@@ -180,11 +180,11 @@ void crash_context_draw(void) {
|
||||
osWritebackDCacheAll();
|
||||
|
||||
#ifdef INCLUDE_DEBUG_MAP
|
||||
uintptr_t pc = tc->pc;
|
||||
Address pc = tc->pc;
|
||||
const char* fname = parse_map(&pc);
|
||||
// "CRASH AT:"
|
||||
// "CRASH IN:"
|
||||
size_t charX = crash_screen_print(TEXT_X(0), TEXT_Y(line),
|
||||
STR_COLOR_PREFIX"CRASH AT: ",
|
||||
STR_COLOR_PREFIX"CRASH IN: ",
|
||||
COLOR_RGBA32_CRASH_AT
|
||||
);
|
||||
if (fname == NULL) {
|
||||
|
||||
@@ -30,10 +30,10 @@ static _Bool sContinueFillBranchBuffer = FALSE;
|
||||
ALIGNED16 static struct BranchArrow sBranchArrows[DISASM_BRANCH_BUFFER_SIZE];
|
||||
static u32 sNumBranchArrows = 0;
|
||||
|
||||
static uintptr_t sBranchBufferCurrAddr = 0x00000000;
|
||||
static Address sBranchBufferCurrAddr = 0x00000000;
|
||||
|
||||
|
||||
void reset_branch_buffer(uintptr_t funcAddr) {
|
||||
void reset_branch_buffer(Address funcAddr) {
|
||||
bzero(sBranchArrows, sizeof(sBranchArrows));
|
||||
sNumBranchArrows = 0;
|
||||
|
||||
@@ -42,7 +42,7 @@ void reset_branch_buffer(uintptr_t funcAddr) {
|
||||
|
||||
//! TODO: Optimize this as much as possible
|
||||
//! TODO: Version that works without INCLUDE_DEBUG_MAP (check for branches relative to viewport)
|
||||
_Bool crash_screen_fill_branch_buffer(const char* fname, uintptr_t funcAddr) {
|
||||
_Bool crash_screen_fill_branch_buffer(const char* fname, Address funcAddr) {
|
||||
if (fname == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
@@ -60,6 +60,7 @@ _Bool crash_screen_fill_branch_buffer(const char* fname, uintptr_t funcAddr) {
|
||||
curBranchX = sBranchArrows[sNumBranchArrows - 1].xPos;
|
||||
}
|
||||
|
||||
// Pick up where we left off.
|
||||
struct BranchArrow* currArrow = &sBranchArrows[sNumBranchArrows];
|
||||
|
||||
OSTime startTime = osGetTime();
|
||||
@@ -75,7 +76,7 @@ _Bool crash_screen_fill_branch_buffer(const char* fname, uintptr_t funcAddr) {
|
||||
}
|
||||
|
||||
// Left the function.
|
||||
uintptr_t checkAddr = sBranchBufferCurrAddr;
|
||||
Address checkAddr = sBranchBufferCurrAddr;
|
||||
if (!is_in_code_segment(checkAddr)) {
|
||||
return FALSE;
|
||||
}
|
||||
@@ -85,10 +86,10 @@ _Bool crash_screen_fill_branch_buffer(const char* fname, uintptr_t funcAddr) {
|
||||
}
|
||||
|
||||
// Get the offset for the current function;
|
||||
InsnData insn = (InsnData){ .raw = *(uintptr_t*)sBranchBufferCurrAddr };
|
||||
InsnData insn = (InsnData){ .raw = *(Word*)sBranchBufferCurrAddr };
|
||||
s16 branchOffset = check_for_branch_offset(insn);
|
||||
|
||||
if (branchOffset != 0) { //! TODO: Verify ordering:
|
||||
if (branchOffset != 0x0000) { //! TODO: Verify ordering:
|
||||
curBranchX += DISASM_BRANCH_ARROW_SPACING;
|
||||
curBranchColorIndex = ((curBranchColorIndex + 1) % ARRAY_COUNT(sBranchColors));
|
||||
|
||||
@@ -125,7 +126,7 @@ void disasm_init(void) {
|
||||
sDisasmShowDataAsBinary = FALSE;
|
||||
gFillBranchBuffer = FALSE;
|
||||
sContinueFillBranchBuffer = FALSE;
|
||||
reset_branch_buffer((uintptr_t)NULL);
|
||||
reset_branch_buffer((Address)NULL);
|
||||
}
|
||||
|
||||
void draw_branch_arrow(s32 startLine, s32 endLine, s32 dist, RGBA32 color, u32 printLine) {
|
||||
@@ -180,7 +181,7 @@ void disasm_draw_branch_arrows(u32 printLine) {
|
||||
osWritebackDCacheAll();
|
||||
}
|
||||
|
||||
static void print_as_insn(const u32 charX, const u32 charY, const uintptr_t data) {
|
||||
static void print_as_insn(const u32 charX, const u32 charY, const Word data) {
|
||||
InsnData insn = { .raw = data };
|
||||
#ifndef INCLUDE_DEBUG_MAP
|
||||
s16 branchOffset = check_for_branch_offset(insn);
|
||||
@@ -197,7 +198,8 @@ static void print_as_insn(const u32 charX, const u32 charY, const uintptr_t data
|
||||
#ifdef INCLUDE_DEBUG_MAP
|
||||
if (sDisasmShowDestFunctionNames && destFname != NULL) {
|
||||
// "[function name]"
|
||||
crash_screen_print_scroll((charX + TEXT_WIDTH(INSN_NAME_DISPLAY_WIDTH)), charY, (CRASH_SCREEN_NUM_CHARS_X - (INSN_NAME_DISPLAY_WIDTH)),
|
||||
crash_screen_print_scroll((charX + TEXT_WIDTH(INSN_NAME_DISPLAY_WIDTH)), charY,
|
||||
(CRASH_SCREEN_NUM_CHARS_X - (INSN_NAME_DISPLAY_WIDTH)),
|
||||
STR_COLOR_PREFIX"%s",
|
||||
COLOR_RGBA32_CRASH_FUNCTION_NAME, destFname
|
||||
);
|
||||
@@ -205,7 +207,7 @@ static void print_as_insn(const u32 charX, const u32 charY, const uintptr_t data
|
||||
#endif
|
||||
}
|
||||
|
||||
static void print_as_binary(const u32 charX, const u32 charY, const uintptr_t data) { //! TODO: make this a formatting char, maybe \%b?
|
||||
static void print_as_binary(const u32 charX, const u32 charY, const Word data) { //! TODO: make this a formatting char?, maybe \%b?
|
||||
u32 bitX = charX;
|
||||
|
||||
for (u32 c = 0; c < 32; c++) {
|
||||
@@ -219,14 +221,14 @@ static void print_as_binary(const u32 charX, const u32 charY, const uintptr_t da
|
||||
}
|
||||
}
|
||||
|
||||
void disasm_draw_asm_entries(u32 line, u32 numLines, uintptr_t selectedAddr, uintptr_t pc) {
|
||||
static void disasm_draw_asm_entries(u32 line, u32 numLines, Address selectedAddr, Address pc) {
|
||||
u32 charX = TEXT_X(0);
|
||||
u32 charY = TEXT_Y(line);
|
||||
|
||||
sDisasmViewportIndex = clamp_view_to_selection(sDisasmViewportIndex, gSelectedAddress, numLines, DISASM_STEP);
|
||||
|
||||
for (u32 y = 0; y < numLines; y++) {
|
||||
uintptr_t addr = (sDisasmViewportIndex + (y * DISASM_STEP));
|
||||
Address addr = (sDisasmViewportIndex + (y * DISASM_STEP));
|
||||
charY = TEXT_Y(line + y);
|
||||
|
||||
// Draw crash and selection rectangles:
|
||||
@@ -240,7 +242,7 @@ void disasm_draw_asm_entries(u32 line, u32 numLines, uintptr_t selectedAddr, uin
|
||||
crash_screen_draw_rect((charX - 1), (charY - 2), (CRASH_SCREEN_TEXT_W + 1), (TEXT_HEIGHT(1) + 1), COLOR_RGBA32_CRASH_SELECT);
|
||||
}
|
||||
|
||||
uintptr_t data = *(uintptr_t*)addr;
|
||||
Word data = *(Word*)addr;
|
||||
|
||||
if (is_in_code_segment(addr)) {
|
||||
print_as_insn(charX, charY, data);
|
||||
@@ -258,16 +260,16 @@ void disasm_draw_asm_entries(u32 line, u32 numLines, uintptr_t selectedAddr, uin
|
||||
osWritebackDCacheAll();
|
||||
}
|
||||
|
||||
//! TODO: automatically check page change:
|
||||
// uintptr_t sCurrFuncAddr = 0x00000000;
|
||||
//! TODO: automatically check page/address change:
|
||||
// Address sCurrFuncAddr = 0x00000000;
|
||||
// const char* sCurrFuncName = NULL;
|
||||
|
||||
void disasm_draw(void) {
|
||||
__OSThreadContext* tc = &gCrashedThread->context;
|
||||
const char* fname = NULL;
|
||||
uintptr_t alignedSelectedAddr = ALIGNFLOOR(gSelectedAddress, DISASM_STEP);
|
||||
Address alignedSelectedAddr = ALIGNFLOOR(gSelectedAddress, DISASM_STEP);
|
||||
#ifdef INCLUDE_DEBUG_MAP
|
||||
uintptr_t funcAddr = alignedSelectedAddr;
|
||||
Address funcAddr = alignedSelectedAddr;
|
||||
fname = parse_map(&funcAddr);
|
||||
#endif
|
||||
|
||||
@@ -330,7 +332,7 @@ const enum ControlTypes disasmContList[] = {
|
||||
|
||||
void disasm_input(void) {
|
||||
#ifdef INCLUDE_DEBUG_MAP
|
||||
uintptr_t oldPos = gSelectedAddress;
|
||||
Address oldPos = gSelectedAddress;
|
||||
#endif
|
||||
|
||||
if (gCSDirectionFlags.pressed.up) {
|
||||
@@ -361,11 +363,12 @@ void disasm_input(void) {
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_DEBUG_MAP
|
||||
//! TODO: don't reset branch buffer if switched page back into the same function.
|
||||
if (gCSSwitchedPage || !is_in_same_function(oldPos, gSelectedAddress)) {
|
||||
gFillBranchBuffer = TRUE;
|
||||
}
|
||||
|
||||
uintptr_t funcAddr = ALIGNFLOOR(gSelectedAddress, DISASM_STEP);
|
||||
Address funcAddr = ALIGNFLOOR(gSelectedAddress, DISASM_STEP);
|
||||
const char* fname = parse_map(&funcAddr);
|
||||
|
||||
if (gFillBranchBuffer) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
|
||||
struct BranchArrow {
|
||||
/*0x00*/ uintptr_t startAddr;
|
||||
/*0x00*/ Address startAddr;
|
||||
/*0x02*/ s16 branchOffset;
|
||||
/*0x04*/ s16 colorIndex;
|
||||
/*0x08*/ s32 xPos;
|
||||
|
||||
@@ -7,11 +7,22 @@
|
||||
#include "game/game_input.h"
|
||||
|
||||
|
||||
static u32 sRamViewViewportIndex = 0x00000000;
|
||||
static Address sRamViewViewportIndex = 0x00000000;
|
||||
|
||||
static _Bool sRamViewShowAsAscii = FALSE;
|
||||
|
||||
|
||||
const enum ControlTypes ramViewerContList[] = {
|
||||
CONT_DESC_SWITCH_PAGE,
|
||||
CONT_DESC_SHOW_CONTROLS,
|
||||
CONT_DESC_CYCLE_DRAW,
|
||||
CONT_DESC_CURSOR,
|
||||
CONT_DESC_JUMP_TO_ADDRESS,
|
||||
CONT_DESC_TOGGLE_ASCII,
|
||||
CONT_DESC_LIST_END,
|
||||
};
|
||||
|
||||
|
||||
void ram_viewer_init(void) {
|
||||
sRamViewViewportIndex = gSelectedAddress;
|
||||
sRamViewShowAsAscii = FALSE;
|
||||
@@ -19,7 +30,7 @@ void ram_viewer_init(void) {
|
||||
|
||||
static const char gHex[0x10] = "0123456789ABCDEF";
|
||||
|
||||
static void print_byte(u32 x, u32 y, u8 byte, RGBA32 color) {
|
||||
static void print_byte(u32 x, u32 y, Byte byte, RGBA32 color) {
|
||||
// "XX"
|
||||
if (sRamViewShowAsAscii) {
|
||||
crash_screen_draw_glyph((x + TEXT_WIDTH(1)), y, byte, color);
|
||||
@@ -35,8 +46,7 @@ void ram_viewer_draw(void) {
|
||||
|
||||
sRamViewViewportIndex = clamp_view_to_selection(sRamViewViewportIndex, gSelectedAddress, RAM_VIEWER_NUM_ROWS, RAM_VIEWER_STEP);
|
||||
|
||||
uintptr_t startAddr = sRamViewViewportIndex;
|
||||
u32 charX, charY;
|
||||
Address startAddr = sRamViewViewportIndex;
|
||||
u32 line = 1;
|
||||
|
||||
// "[XXXXXXXX] in [XXXXXXXX]-[XXXXXXXX]"
|
||||
@@ -47,8 +57,9 @@ void ram_viewer_draw(void) {
|
||||
|
||||
line++;
|
||||
|
||||
charX = (TEXT_X(8) + 3);
|
||||
u32 charX = (TEXT_X(8) + 3);
|
||||
|
||||
// Print column headers:
|
||||
for (u32 i = 0; i < 16; i++) {
|
||||
if ((i % 4) == 0) {
|
||||
charX += 2;
|
||||
@@ -70,10 +81,10 @@ void ram_viewer_draw(void) {
|
||||
line++;
|
||||
|
||||
charX = (TEXT_X(8) + 3);
|
||||
charY = TEXT_Y(line);
|
||||
u32 charY = TEXT_Y(line);
|
||||
|
||||
for (u32 y = 0; y < RAM_VIEWER_NUM_ROWS; y++) {
|
||||
uintptr_t rowAddr = (startAddr + (y * RAM_VIEWER_STEP));
|
||||
Address rowAddr = (startAddr + (y * RAM_VIEWER_STEP));
|
||||
|
||||
// "[XXXXXXXX]"
|
||||
crash_screen_print(TEXT_X(0), TEXT_Y(line + y), (STR_COLOR_PREFIX STR_HEX_WORD),
|
||||
@@ -83,7 +94,7 @@ void ram_viewer_draw(void) {
|
||||
charX = (TEXT_X(8) + 3);
|
||||
charY = TEXT_Y(line + y);
|
||||
for (u32 x = 0; x < 16; x++) {
|
||||
uintptr_t currAddr = (rowAddr + x);
|
||||
Address currAddr = (rowAddr + x);
|
||||
|
||||
if ((x % 4) == 0) {
|
||||
charX += 2;
|
||||
@@ -103,7 +114,7 @@ void ram_viewer_draw(void) {
|
||||
crash_screen_draw_rect((charX - 1), (charY - 1), (TEXT_WIDTH(2) + 1), (TEXT_WIDTH(1) + 3), selectColor);
|
||||
}
|
||||
|
||||
print_byte(charX, charY, *(vu8*)currAddr, textColor);
|
||||
print_byte(charX, charY, *(Byte*)currAddr, textColor);
|
||||
|
||||
charX += (TEXT_WIDTH(2) + 1);
|
||||
}
|
||||
@@ -119,18 +130,6 @@ void ram_viewer_draw(void) {
|
||||
osWritebackDCacheAll();
|
||||
}
|
||||
|
||||
|
||||
const enum ControlTypes ramViewerContList[] = {
|
||||
CONT_DESC_SWITCH_PAGE,
|
||||
CONT_DESC_SHOW_CONTROLS,
|
||||
CONT_DESC_CYCLE_DRAW,
|
||||
CONT_DESC_CURSOR,
|
||||
CONT_DESC_JUMP_TO_ADDRESS,
|
||||
CONT_DESC_TOGGLE_ASCII,
|
||||
CONT_DESC_LIST_END,
|
||||
};
|
||||
|
||||
|
||||
void ram_viewer_input(void) {
|
||||
if (gCSDirectionFlags.pressed.up) {
|
||||
// Scroll up.
|
||||
@@ -147,14 +146,14 @@ void ram_viewer_input(void) {
|
||||
}
|
||||
|
||||
if (gCSDirectionFlags.pressed.left) {
|
||||
// Don't wrap.
|
||||
// Prevent wrapping.
|
||||
if (((gSelectedAddress - 1) & BITMASK(4)) != 0xF) {
|
||||
gSelectedAddress--;
|
||||
}
|
||||
}
|
||||
|
||||
if (gCSDirectionFlags.pressed.right) {
|
||||
// Don't wrap.
|
||||
// Prevent wrapping.
|
||||
if (((gSelectedAddress + 1) & BITMASK(4)) != 0x0) {
|
||||
gSelectedAddress++;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
|
||||
// RAM Viewer constants
|
||||
#define RAM_VIEWER_STEP (s32)(4 * sizeof(uintptr_t))
|
||||
#define RAM_VIEWER_STEP (s32)(4 * sizeof(Word))
|
||||
|
||||
#define RAM_VIEWER_NUM_ROWS 19
|
||||
#define RAM_VIEWER_SHOWN_SECTION ((RAM_VIEWER_NUM_ROWS - 1) * RAM_VIEWER_STEP)
|
||||
|
||||
@@ -37,11 +37,11 @@ void fill_function_stack_trace(void) {
|
||||
.fname = NULL,
|
||||
};
|
||||
|
||||
u64* sp = (u64*)(uintptr_t)tc->sp; // Stack pointer is already aligned, so get the lower bits.
|
||||
Register* sp = (Register*)(Address)tc->sp; // Stack pointer is already aligned, so get the lower bits.
|
||||
|
||||
// Loop through the stack buffer and find all the addresses that point to a function.
|
||||
while ((u8*)sp < _buffersSegmentBssEnd && sNumFoundFunctions < STACK_TRACE_BUFFER_SIZE) {
|
||||
currInfo.curAddr = (uintptr_t)(*sp);
|
||||
while ((Byte*)sp < _buffersSegmentBssEnd && sNumFoundFunctions < STACK_TRACE_BUFFER_SIZE) {
|
||||
currInfo.curAddr = (Address)(*sp);
|
||||
|
||||
if (is_in_code_segment(currInfo.curAddr)) {
|
||||
currInfo.faddr = currInfo.curAddr;
|
||||
@@ -49,11 +49,11 @@ void fill_function_stack_trace(void) {
|
||||
if (currInfo.fname != NULL) {
|
||||
//! TODO: If JAL command uses a different function than the previous entry's faddr, replace it with the one in the JAL command?
|
||||
//! TODO: handle duplicate entries caused by JALR RA, V0
|
||||
currInfo.stackAddr = (uintptr_t)sp + sizeof(uintptr_t);
|
||||
currInfo.stackAddr = (Address)sp + sizeof(Address);
|
||||
sFunctionStack[sNumFoundFunctions++] = currInfo;
|
||||
}
|
||||
|
||||
if (currInfo.faddr == (uintptr_t)__osCleanupThread) {
|
||||
if (currInfo.faddr == (Address)__osCleanupThread) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -149,13 +149,13 @@ void stack_trace_draw(void) {
|
||||
u32 line = 1;
|
||||
|
||||
// "FROM: [XXXXXXXX]"
|
||||
crash_screen_print(TEXT_X(12), TEXT_Y(line), "FROM "STR_HEX_WORD, (uintptr_t)tc->sp);
|
||||
crash_screen_print(TEXT_X(12), TEXT_Y(line), "FROM "STR_HEX_WORD, (Address)tc->sp);
|
||||
|
||||
line++;
|
||||
|
||||
#ifdef INCLUDE_DEBUG_MAP
|
||||
crash_screen_print(TEXT_X(0), TEXT_Y(line), STR_COLOR_PREFIX"CURRFUNC:", COLOR_RGBA32_CRASH_AT);
|
||||
uintptr_t pc = tc->pc;
|
||||
Address pc = tc->pc;
|
||||
const char* fname = parse_map(&pc);
|
||||
if (fname == NULL) {
|
||||
// "UNKNOWN"
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
|
||||
|
||||
struct FunctionInStack {
|
||||
/*0x00*/ uintptr_t stackAddr;
|
||||
/*0x04*/ uintptr_t curAddr;
|
||||
/*0x08*/ uintptr_t faddr;
|
||||
/*0x00*/ Address stackAddr;
|
||||
/*0x04*/ Address curAddr;
|
||||
/*0x08*/ Address faddr;
|
||||
/*0x0C*/ const char* fname;
|
||||
}; /*0x10*/
|
||||
|
||||
|
||||
Reference in New Issue
Block a user