mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-23 02:42:09 +00:00
Implement dbg log interface
This commit is contained in:
parent
a65d380889
commit
d2b1febb43
11 changed files with 471 additions and 1 deletions
291
exosphere/src/dbg/fmt.c
Normal file
291
exosphere/src/dbg/fmt.c
Normal file
|
@ -0,0 +1,291 @@
|
|||
/* File : barebones/ee_printf.c
|
||||
This file contains an implementation of ee_printf that only requires a method to output a char to a UART without pulling in library code.
|
||||
This code is based on a file that contains the following:
|
||||
Copyright (C) 2002 Michael Ringgaard. All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the project nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
//TuxSH's changes: add support for 64-bit numbers, remove floating-point code
|
||||
// (C) AuroraWright, TuxSH
|
||||
|
||||
#include "../utils.h"
|
||||
#include <string.h>
|
||||
|
||||
#include "fmt.h"
|
||||
|
||||
#define ZEROPAD (1<<0) //Pad with zero
|
||||
#define SIGN (1<<1) //Unsigned/signed long
|
||||
#define PLUS (1<<2) //Show plus
|
||||
#define SPACE (1<<3) //Spacer
|
||||
#define LEFT (1<<4) //Left justified
|
||||
#define HEX_PREP (1<<5) //0x
|
||||
#define UPPERCASE (1<<6) //'ABCDEF'
|
||||
|
||||
#define IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
|
||||
|
||||
static int32_t skipAtoi(const char **s)
|
||||
{
|
||||
int32_t i = 0;
|
||||
|
||||
while(IS_DIGIT(**s)) i = i * 10 + *((*s)++) - '0';
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static char *processNumber(char *str, int64_t num, bool isHex, int32_t size, int32_t precision, uint32_t type)
|
||||
{
|
||||
char sign = 0;
|
||||
|
||||
if(type & SIGN)
|
||||
{
|
||||
if(num < 0)
|
||||
{
|
||||
sign = '-';
|
||||
num = -num;
|
||||
size--;
|
||||
}
|
||||
else if(type & PLUS)
|
||||
{
|
||||
sign = '+';
|
||||
size--;
|
||||
}
|
||||
else if(type & SPACE)
|
||||
{
|
||||
sign = ' ';
|
||||
size--;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *lowerDigits = "0123456789abcdef",
|
||||
*upperDigits = "0123456789ABCDEF";
|
||||
|
||||
int32_t i = 0;
|
||||
char tmp[20];
|
||||
const char *dig = (type & UPPERCASE) ? upperDigits : lowerDigits;
|
||||
|
||||
if(num == 0)
|
||||
{
|
||||
if(precision != 0) tmp[i++] = '0';
|
||||
type &= ~HEX_PREP;
|
||||
}
|
||||
else
|
||||
{
|
||||
while(num != 0)
|
||||
{
|
||||
uint64_t base = isHex ? 16ULL : 10ULL;
|
||||
tmp[i++] = dig[(uint64_t)num % base];
|
||||
num = (int64_t)((uint64_t)num / base);
|
||||
}
|
||||
}
|
||||
|
||||
if(type & LEFT || precision != -1) type &= ~ZEROPAD;
|
||||
if(type & HEX_PREP && isHex) size -= 2;
|
||||
if(i > precision) precision = i;
|
||||
size -= precision;
|
||||
if(!(type & (ZEROPAD | LEFT))) while(size-- > 0) *str++ = ' ';
|
||||
if(sign) *str++ = sign;
|
||||
|
||||
if(type & HEX_PREP && isHex)
|
||||
{
|
||||
*str++ = '0';
|
||||
*str++ = 'x';
|
||||
}
|
||||
|
||||
if(type & ZEROPAD) while(size-- > 0) *str++ = '0';
|
||||
while(i < precision--) *str++ = '0';
|
||||
while(i-- > 0) *str++ = tmp[i];
|
||||
while(size-- > 0) *str++ = ' ';
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
int visprintf(char *buf, const char *fmt, va_list args)
|
||||
{
|
||||
char *str;
|
||||
|
||||
for(str = buf; *fmt; fmt++)
|
||||
{
|
||||
if(*fmt != '%')
|
||||
{
|
||||
*str++ = *fmt;
|
||||
continue;
|
||||
}
|
||||
|
||||
//Process flags
|
||||
uint32_t flags = 0; //Flags to number()
|
||||
bool loop = true;
|
||||
|
||||
while(loop)
|
||||
{
|
||||
switch(*++fmt)
|
||||
{
|
||||
case '-': flags |= LEFT; break;
|
||||
case '+': flags |= PLUS; break;
|
||||
case ' ': flags |= SPACE; break;
|
||||
case '#': flags |= HEX_PREP; break;
|
||||
case '0': flags |= ZEROPAD; break;
|
||||
default: loop = false; break;
|
||||
}
|
||||
}
|
||||
|
||||
//Get field width
|
||||
int32_t fieldWidth = -1; //Width of output field
|
||||
if(IS_DIGIT(*fmt)) fieldWidth = skipAtoi(&fmt);
|
||||
else if(*fmt == '*')
|
||||
{
|
||||
fmt++;
|
||||
|
||||
fieldWidth = va_arg(args, int32_t);
|
||||
|
||||
if(fieldWidth < 0)
|
||||
{
|
||||
fieldWidth = -fieldWidth;
|
||||
flags |= LEFT;
|
||||
}
|
||||
}
|
||||
|
||||
//Get the precision
|
||||
int32_t precision = -1; //Min. # of digits for integers; max number of chars for from string
|
||||
if(*fmt == '.')
|
||||
{
|
||||
fmt++;
|
||||
|
||||
if(IS_DIGIT(*fmt)) precision = skipAtoi(&fmt);
|
||||
else if(*fmt == '*')
|
||||
{
|
||||
fmt++;
|
||||
precision = va_arg(args, int32_t);
|
||||
}
|
||||
|
||||
if(precision < 0) precision = 0;
|
||||
}
|
||||
|
||||
//Get the conversion qualifier
|
||||
uint32_t integerType = 0;
|
||||
if(*fmt == 'l')
|
||||
{
|
||||
if(*++fmt == 'l')
|
||||
{
|
||||
fmt++;
|
||||
integerType = 1;
|
||||
}
|
||||
|
||||
}
|
||||
else if(*fmt == 'h')
|
||||
{
|
||||
if(*++fmt == 'h')
|
||||
{
|
||||
fmt++;
|
||||
integerType = 3;
|
||||
}
|
||||
else integerType = 2;
|
||||
}
|
||||
|
||||
bool isHex;
|
||||
|
||||
switch(*fmt)
|
||||
{
|
||||
case 'c':
|
||||
if(!(flags & LEFT)) while(--fieldWidth > 0) *str++ = ' ';
|
||||
*str++ = (uint8_t)va_arg(args, int32_t);
|
||||
while(--fieldWidth > 0) *str++ = ' ';
|
||||
continue;
|
||||
|
||||
case 's':
|
||||
{
|
||||
char *s = va_arg(args, char *);
|
||||
if(!s) s = "<NULL>";
|
||||
uint32_t len = (precision != -1) ? strnlen(s, precision) : strlen(s);
|
||||
if(!(flags & LEFT)) while((int32_t)len < fieldWidth--) *str++ = ' ';
|
||||
for(uint32_t i = 0; i < len; i++) *str++ = *s++;
|
||||
while((int32_t)len < fieldWidth--) *str++ = ' ';
|
||||
continue;
|
||||
}
|
||||
|
||||
case 'p':
|
||||
if(fieldWidth == -1)
|
||||
{
|
||||
fieldWidth = 8;
|
||||
flags |= ZEROPAD;
|
||||
}
|
||||
str = processNumber(str, va_arg(args, uint32_t), true, fieldWidth, precision, flags);
|
||||
continue;
|
||||
|
||||
//Integer number formats - set up the flags and "break"
|
||||
case 'X':
|
||||
flags |= UPPERCASE;
|
||||
//Falls through
|
||||
case 'x':
|
||||
isHex = true;
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
case 'i':
|
||||
flags |= SIGN;
|
||||
//Falls through
|
||||
case 'u':
|
||||
isHex = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
if(*fmt != '%') *str++ = '%';
|
||||
if(*fmt) *str++ = *fmt;
|
||||
else fmt--;
|
||||
continue;
|
||||
}
|
||||
|
||||
int64_t num;
|
||||
|
||||
if(flags & SIGN)
|
||||
{
|
||||
if(integerType == 1) num = va_arg(args, int64_t);
|
||||
else num = va_arg(args, int32_t);
|
||||
|
||||
if(integerType == 2) num = (int16_t)num;
|
||||
else if(integerType == 3) num = (int8_t)num;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(integerType == 1) num = va_arg(args, uint64_t);
|
||||
else num = va_arg(args, uint32_t);
|
||||
|
||||
if(integerType == 2) num = (uint16_t)num;
|
||||
else if(integerType == 3) num = (uint8_t)num;
|
||||
}
|
||||
|
||||
str = processNumber(str, num, isHex, fieldWidth, precision, flags);
|
||||
}
|
||||
|
||||
*str = 0;
|
||||
return str - buf;
|
||||
}
|
||||
|
||||
int isprintf(char *buf, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int res = visprintf(buf, fmt, args);
|
||||
va_end(args);
|
||||
return res;
|
||||
}
|
9
exosphere/src/dbg/fmt.h
Normal file
9
exosphere/src/dbg/fmt.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#ifndef EXOSPHERE_DBG_FMT_H
|
||||
#define EXOSPHERE_DBG_FMT_H
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
int visprintf(char *buf, const char *fmt, va_list args);
|
||||
int isprintf(char *buf, const char *fmt, ...);
|
||||
|
||||
#endif
|
47
exosphere/src/dbg/log.c
Normal file
47
exosphere/src/dbg/log.c
Normal file
|
@ -0,0 +1,47 @@
|
|||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include "log.h"
|
||||
#include "log_device_null.h"
|
||||
#include "log_device_uart.h"
|
||||
#include "fmt.h"
|
||||
#include "../synchronization.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
static atomic_flag g_log_lock = ATOMIC_FLAG_INIT;
|
||||
static debug_log_device_t *dev;
|
||||
#endif
|
||||
|
||||
void dbg_log_initialize(DebugLogDevice device) {
|
||||
#ifndef NDEBUG
|
||||
static debug_log_device_t *const devs[] = {&g_debug_log_device_null.super, &g_debug_log_device_uart.super};
|
||||
dev = device >= DEBUGLOGDEVICE_MAX ? &g_debug_log_device_null.super : devs[device];
|
||||
#else
|
||||
(void)device;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* NOTE: no bound checks are done */
|
||||
void dbg_log_write(const char *fmt, ...) {
|
||||
#ifndef NDEBUG
|
||||
char buf[DBG_LOG_BUF_SIZE];
|
||||
int len;
|
||||
va_list args;
|
||||
lock_acquire(&g_log_lock);
|
||||
|
||||
va_start(args, fmt);
|
||||
len = visprintf(buf, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
dev->write_string(dev, buf, len);
|
||||
lock_release(&g_log_lock);
|
||||
#else
|
||||
(void)fmt;
|
||||
#endif
|
||||
}
|
||||
|
||||
void dbg_log_finalize(void) {
|
||||
#ifndef NDEBUG
|
||||
dev->finalize(dev);
|
||||
dev = NULL;
|
||||
#endif
|
||||
}
|
25
exosphere/src/dbg/log.h
Normal file
25
exosphere/src/dbg/log.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef EXOSPHERE_DBG_LOG_H
|
||||
#define EXOSPHERE_DBG_LOG_H
|
||||
|
||||
#include "../utils.h"
|
||||
|
||||
#define DBG_LOG_BUF_SIZE 256
|
||||
|
||||
typedef enum {
|
||||
DEBUGLOGDEVICE_NULL = 0,
|
||||
DEBUGLOGDEVICE_UART = 1,
|
||||
|
||||
DEBUGLOGDEVICE_MAX = 2,
|
||||
} DebugLogDevice;
|
||||
|
||||
typedef struct debug_log_device_t {
|
||||
void (*initialize)(struct debug_log_device_t *this, ...);
|
||||
void (*write_string)(struct debug_log_device_t *this, const char *str, size_t len);
|
||||
void (*finalize)(struct debug_log_device_t *this);
|
||||
} debug_log_device_t;
|
||||
|
||||
void dbg_log_initialize(DebugLogDevice device);
|
||||
void dbg_log_write(const char *fmt, ...);
|
||||
void dbg_log_finalize(void);
|
||||
|
||||
#endif
|
26
exosphere/src/dbg/log_device_null.c
Normal file
26
exosphere/src/dbg/log_device_null.c
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include "log_device_null.h"
|
||||
|
||||
static void initialize(debug_log_device_null_t *this) {
|
||||
(void)this;
|
||||
/* Do nothing */
|
||||
}
|
||||
|
||||
static void write_string(debug_log_device_null_t *this, const char *str, size_t len) {
|
||||
(void)this;
|
||||
(void)str;
|
||||
(void)len;
|
||||
/* Do nothing */
|
||||
}
|
||||
|
||||
static void finalize(debug_log_device_null_t *this) {
|
||||
(void)this;
|
||||
/* Do nothing */
|
||||
}
|
||||
|
||||
debug_log_device_null_t g_debug_log_device_null = {
|
||||
.super = {
|
||||
.initialize = (void (*)(debug_log_device_t *, ...))initialize,
|
||||
.write_string = (void (*)(debug_log_device_t *, const char *, size_t))write_string,
|
||||
.finalize = (void (*)(debug_log_device_t *))finalize,
|
||||
},
|
||||
};
|
13
exosphere/src/dbg/log_device_null.h
Normal file
13
exosphere/src/dbg/log_device_null.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef EXOSPHERE_DBG_LOG_DEVICE_NULL_H
|
||||
#define EXOSPHERE_DBG_LOG_DEVICE_NULL_H
|
||||
|
||||
#include "log.h"
|
||||
|
||||
typedef struct {
|
||||
debug_log_device_t super;
|
||||
/* Additonnal attributes go here */
|
||||
} debug_log_device_null_t;
|
||||
|
||||
extern debug_log_device_null_t g_debug_log_device_null;
|
||||
|
||||
#endif
|
30
exosphere/src/dbg/log_device_uart.c
Normal file
30
exosphere/src/dbg/log_device_uart.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include "log_device_uart.h"
|
||||
#include "../car.h"
|
||||
#include "../uart.h"
|
||||
|
||||
static void initialize(debug_log_device_uart_t *this) {
|
||||
if (!this->is_initialized) {
|
||||
uart_select(0); /* UART-A */
|
||||
clkrst_enable(CARDEVICE_UARTA);
|
||||
uart_initialize(0 /* I don't know */);
|
||||
this->is_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_string(debug_log_device_uart_t *this, const char *str, size_t len) {
|
||||
(void)this;
|
||||
(void)len;
|
||||
uart_transmit_str(str);
|
||||
}
|
||||
|
||||
static void finalize(debug_log_device_uart_t *this) {
|
||||
clkrst_disable(CARDEVICE_UARTA);
|
||||
}
|
||||
|
||||
debug_log_device_uart_t g_debug_log_device_uart = {
|
||||
.super = {
|
||||
.initialize = (void (*)(debug_log_device_t *, ...))initialize,
|
||||
.write_string = (void (*)(debug_log_device_t *, const char *, size_t))write_string,
|
||||
.finalize = (void (*)(debug_log_device_t *))finalize,
|
||||
},
|
||||
};
|
13
exosphere/src/dbg/log_device_uart.h
Normal file
13
exosphere/src/dbg/log_device_uart.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef EXOSPHERE_DBG_LOG_DEVICE_UART_H
|
||||
#define EXOSPHERE_DBG_LOG_DEVICE_UART_H
|
||||
|
||||
#include "log.h"
|
||||
|
||||
typedef struct {
|
||||
debug_log_device_t super;
|
||||
bool is_initialized;
|
||||
} debug_log_device_uart_t;
|
||||
|
||||
extern debug_log_device_uart_t g_debug_log_device_uart;
|
||||
|
||||
#endif
|
|
@ -19,4 +19,9 @@
|
|||
#define PINMUX_AUX_GEN1_I2C_SCL_0 MAKE_MISC_REG(0x30BC)
|
||||
#define PINMUX_AUX_GEN1_I2C_SDA_0 MAKE_MISC_REG(0x30C0)
|
||||
|
||||
#define PINMUX_AUX_UARTn_TX_0(n) MAKE_MISC_REG(0x30E4 + 0x10 * (n))
|
||||
#define PINMUX_AUX_UARTn_RX_0(n) MAKE_MISC_REG(0x30E8 + 0x10 * (n))
|
||||
#define PINMUX_AUX_UARTn_RTS_0(n) MAKE_MISC_REG(0x30EC + 0x10 * (n))
|
||||
#define PINMUX_AUX_UARTn_CTS_0(n) MAKE_MISC_REG(0x30F0 + 0x10 * (n))
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,14 @@
|
|||
#include "uart.h"
|
||||
#include "misc.h"
|
||||
|
||||
void uart_select(unsigned int id) {
|
||||
/* This confirmation is valid for UART-A, I don't know about the other UARTs. */
|
||||
/* Official Nintendo code (for UART-A, at least) */
|
||||
PINMUX_AUX_UARTn_TX_0(id) = 0; /* UART */
|
||||
PINMUX_AUX_UARTn_RX_0(id) = 0x48; /* UART, enable, pull up */
|
||||
PINMUX_AUX_UARTn_RTS_0(id) = 0; /* UART */
|
||||
PINMUX_AUX_UARTn_CTS_0(id) = 0x44; /* UART, enable, pull down */
|
||||
}
|
||||
|
||||
void uart_initialize(uint16_t divider) {
|
||||
/* Setup UART in 16450 mode. We assume the relevant UART clock has been enabled. */
|
||||
|
@ -33,4 +43,4 @@ void uart_transmit_hex(uint32_t value) {
|
|||
uint32_t nibble = (value >> (28 - i * 4)) & 0xF;
|
||||
uart_transmit_char("0123456789ABCDEF"[nibble]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ static inline uintptr_t get_uarta_base(void) {
|
|||
#define UART_LCR_0 MAKE_REG32(UARTA_BASE + 0xC)
|
||||
#define UART_LSR_0 MAKE_REG32(UARTA_BASE + 0x14)
|
||||
|
||||
void uart_select(unsigned int id);
|
||||
void uart_initialize(uint16_t divider);
|
||||
void uart_transmit_char(char ch);
|
||||
void uart_transmit_str(const char *str);
|
||||
|
|
Loading…
Reference in a new issue