mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2024-12-28 04:23:36 +01:00
improve structure of sources and formatted printer
* Create an `src/3rdparty` folder for third-party code (to add the Grisu2B alfogithm soon). * Split the formatted printer into gint's kprint (src/kprint), its extension and interface (include/gint/kprint.h), and its use in the standard stdio functions (src/std/print.c). * Slightly improve the interface of kformat_geometry() to avoid relying on knowing format specifiers. * Add a function to register more formatters, to allow floating-point formatters without requiring them.
This commit is contained in:
parent
ee7b4f27b8
commit
023675d449
10 changed files with 229 additions and 130 deletions
|
@ -30,6 +30,7 @@ set(SOURCES_COMMON
|
|||
src/keysc/iokbd.c
|
||||
src/keysc/keycodes.c
|
||||
src/keysc/keysc.c
|
||||
src/kprint/kprint.c
|
||||
src/mmu/mmu.c
|
||||
src/render/dhline.c
|
||||
src/render/dimage.c
|
||||
|
@ -43,18 +44,18 @@ set(SOURCES_COMMON
|
|||
src/rtc/rtc.c
|
||||
src/rtc/rtc_ticks.c
|
||||
src/spu/spu.c
|
||||
src/std/tinymt32/rand.c
|
||||
src/std/tinymt32/tinymt32.c
|
||||
src/std/memcmp.s
|
||||
src/std/memcpy.s
|
||||
src/std/memmove.s
|
||||
src/std/memset.s
|
||||
src/std/stdio.c
|
||||
src/std/print.c
|
||||
src/std/string.c
|
||||
src/tmu/inth-etmu.s
|
||||
src/tmu/inth-tmu.s
|
||||
src/tmu/sleep.c
|
||||
src/tmu/tmu.c
|
||||
src/3rdparty/tinymt32/rand.c
|
||||
src/3rdparty/tinymt32/tinymt32.c
|
||||
)
|
||||
set(SOURCES_FX
|
||||
src/gray/engine.c
|
||||
|
|
|
@ -17,7 +17,7 @@ Currently, this only includes:
|
|||
|
||||
* A stripped-down version of the [TinyMT random number generator](http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/TINYMT/index.html)
|
||||
([GitHub repository](https://github.com/MersenneTwister-Lab/TinyMT)) by
|
||||
Mutsuo Saito and Makoto Matsumoto. See `src/std/tinymt32/LICENSE.txt`.
|
||||
Mutsuo Saito and Makoto Matsumoto. See `src/3rdparty/tinymt32/LICENSE.txt`.
|
||||
|
||||
## Programming interface
|
||||
|
||||
|
|
1
TODO
1
TODO
|
@ -1,4 +1,5 @@
|
|||
Extensions on existing code:
|
||||
* kernel: check if cpu_setVBR() really needs to be perma-mapped
|
||||
* stdio: support %f in printf
|
||||
* project: add license file
|
||||
* kernel: group linker script symbols in a single header file
|
||||
|
|
138
include/gint/kprint.h
Normal file
138
include/gint/kprint.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
//---
|
||||
// gint:kprint - gint's printf(3)-like formatted printer
|
||||
//---
|
||||
|
||||
#ifndef GINT_KPRINT
|
||||
#define GINT_KPRINT
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
//---
|
||||
// Formatted printing
|
||||
//---
|
||||
|
||||
/* kvsprint(): Formatted print to string
|
||||
This function is similar to vsnprintf(3), except that variadic arguments are
|
||||
passed as a pointer to va_list. For standard functions like vsnprintf(3),
|
||||
see <stdio.h>. */
|
||||
size_t kvsprint(char *output, size_t len, char const *format, va_list *args);
|
||||
|
||||
//---
|
||||
// Printer extensions
|
||||
//---
|
||||
|
||||
/* Arguments passed to formatters */
|
||||
#define KFORMAT_ARGS \
|
||||
GUNUSED int spec, /* Specifier letter */ \
|
||||
GUNUSED kprint_options_t *opt, /* Other options in format */ \
|
||||
va_list *args /* Vararg list to read from */
|
||||
|
||||
/* kprint_options_t: Standard format options */
|
||||
typedef struct {
|
||||
/* Minimal length of formatted string (padding can be added) */
|
||||
uint16_t length;
|
||||
/* How much significant characters of data, meaning varies */
|
||||
uint16_t precision;
|
||||
|
||||
/* Size specifier for integers (%o, %x, %i, %d, %u), may be one of:
|
||||
(0) char (8-bit)
|
||||
(1) short (16-bit)
|
||||
(2) int (32-bit)
|
||||
(3) long (32-bit)
|
||||
(4) long long (64-bit) */
|
||||
uint8_t size;
|
||||
|
||||
/* (#) Alternative form: base prefixes, decimal point */
|
||||
uint alternative :1;
|
||||
/* ( ) Add a blank sign before nonnegative numbers */
|
||||
uint blank_sign :1;
|
||||
/* (+) Always add a sign before a number (overrides ' ') */
|
||||
uint force_sign :1;
|
||||
|
||||
/* Alignment options: each option overrides all others before itself
|
||||
NUL By default, align right
|
||||
(0) Left-pad numerical values with zeros
|
||||
(-) Align left by adding space on the right, dropping zeros */
|
||||
uint8_t alignment;
|
||||
|
||||
} GPACKED(2) kprint_options_t;
|
||||
|
||||
/* kprint_formatter_t: Type of format function */
|
||||
typedef void (*kprint_formatter_t)(KFORMAT_ARGS);
|
||||
|
||||
/* kprint_register(): Register a formatter
|
||||
|
||||
The formatter designated by the specified lowercase letter (eg 'q') is
|
||||
registered. It will handle formats for both the lowercase and uppercase
|
||||
formats (eg "%q" and "%Q").
|
||||
|
||||
Default formatters can be overridden (although not advised), but formatters
|
||||
for "h" and "l" and "z" cannot be set because these letters are used as size
|
||||
specifiers (eg in "%hd" or "%zu").
|
||||
|
||||
A formatter can be removed or disabled by registering NULL.
|
||||
|
||||
@spec Specifier to use, should be a lowercase letter
|
||||
@kformat Format function */
|
||||
void kprint_register(int spec, kprint_formatter_t kformat);
|
||||
|
||||
//---
|
||||
// Helper functions for formatters
|
||||
//---
|
||||
|
||||
/* kprint_geometry_t: General geometry for a format
|
||||
This helper defines the structure of a format as follows:
|
||||
|
||||
sign v |< zeros >| |< content >|
|
||||
_ _ _ _ _ _ _ _ _ + 0 x 0 0 0 0 0 0 8 a c 7 . 3 c _ _ _ _ _ _ _ _ _ _
|
||||
|< left_spaces >| ^^^ prefix |< right_spaces >|
|
||||
|
||||
The sign character is absent if sign=0, the prefix is specified by length
|
||||
and is also absent if prefix=0. */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t left_spaces; /* Spaces before content */
|
||||
uint8_t sign; /* Sign character (NUL, ' ', '+' or '-') */
|
||||
uint8_t prefix; /* Base prefix ('0', '0x', etc) length */
|
||||
uint16_t zeros; /* For integer displays, number of zeros */
|
||||
uint16_t content; /* Content length in bytes */
|
||||
uint16_t right_spaces; /* Spaces after content */
|
||||
|
||||
/* Style of display:
|
||||
KPRINT_GENERIC Sign ignored, 0-padding ignored
|
||||
KPRINT_INTEGER .precision causes 0-padding
|
||||
KPRINT_NUMERICAL No effect */
|
||||
enum { KPRINT_GENERIC = 0, KPRINT_INTEGER, KPRINT_NUMERICAL } style;
|
||||
|
||||
} GPACKED(2) kprint_geometry_t;
|
||||
|
||||
/* kformat_geometry(): Calculate the geometry of a format
|
||||
|
||||
The caller provides as input:
|
||||
* opt, as passed by the main kprint() routine
|
||||
* g->prefix, the length of the desired prefix (if unused, 0)
|
||||
* g->content, the natural content length for the provided data
|
||||
* g->sign, the sign of the input ("+" or "-"); for KPRINT_GENERIC, 0
|
||||
|
||||
This function outputs:
|
||||
* g->sign, which can be changed to " " or NUL depending on opt
|
||||
* All other fields of g that are not part of the input
|
||||
|
||||
The algorithm for laying out the format is as follows.
|
||||
1. For numerical and integer formats, turn a "+" sign into " " if
|
||||
opt->blank_sign is set, "+" if opt->force_sign is set, NUL otherwise.
|
||||
2. Compute the total amount of padding needed to reach opt->length.
|
||||
3. In integer style, if a precision is specified and more than content
|
||||
length plus sign and prefix, turn some padding into zeros.
|
||||
4. If numerical and integer styles, if opt->alignment == '0' turn all the
|
||||
padding into zeros.
|
||||
5. Turn remaining padding into spaces at the left (if opt->alignment == NUL)
|
||||
or right (if opt->alignment == '-').
|
||||
|
||||
@opt Options provided by the main kprint() routine
|
||||
@g Format geometry (input/output) */
|
||||
void kformat_geometry(kprint_options_t *opt, kprint_geometry_t *g);
|
||||
|
||||
#endif /* GINT_KPRINT */
|
|
@ -1,5 +1,5 @@
|
|||
//---
|
||||
// The stdio formatted printer.
|
||||
// gint:kprint - gint's printf(3)-like formatted printer
|
||||
//
|
||||
// Things to change when creating new formats:
|
||||
// -> List of prototypes at the end of the definition section
|
||||
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <gint/std/stdio.h>
|
||||
#include <gint/std/string.h>
|
||||
#include <gint/kprint.h>
|
||||
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
@ -21,8 +22,6 @@
|
|||
//---
|
||||
|
||||
#define KPRINT_BUFSIZE 64
|
||||
#define KFORMAT_ARGS \
|
||||
GUNUSED int spec, GUNUSED struct kprint_options *opt, va_list *args
|
||||
|
||||
/* Current position in the output buffer */
|
||||
static char *kprint_ptr = NULL;
|
||||
|
@ -44,41 +43,6 @@ static enum {
|
|||
// KprintOutputDevice = 1,
|
||||
} kprint_type;
|
||||
|
||||
/*
|
||||
struct kprint_options -- standard format options
|
||||
*/
|
||||
struct kprint_options
|
||||
{
|
||||
/* Minimal length of formatted string (padding can be added) */
|
||||
uint16_t length;
|
||||
/* How much significant characters of data, meaning varies */
|
||||
uint16_t precision;
|
||||
|
||||
/* Size specifier for integers (%o, %x, %i, %d, %u), may be one of:
|
||||
(0) char (8-bit)
|
||||
(1) short (16-bit)
|
||||
(2) int (32-bit)
|
||||
(3) long (32-bit)
|
||||
(4) long long (64-bit) */
|
||||
uint8_t size;
|
||||
|
||||
/* (#) Alternative form: base prefixes, decimal point */
|
||||
uint alternative :1;
|
||||
/* ( ) Add a blank sign before nonnegative numbers */
|
||||
uint blank_sign :1;
|
||||
/* (+) Always add a sign before a number (overrides ' ') */
|
||||
uint force_sign :1;
|
||||
|
||||
/* Alignment options: each option overrides all others before itself
|
||||
NUL By default, align right
|
||||
(0) Left-pad numerical values with zeros
|
||||
(-) Align left by adding space on the right, dropping zeros */
|
||||
uint8_t alignment;
|
||||
|
||||
} GPACKED(2);
|
||||
|
||||
typedef void (*kprint_formatter_t)(KFORMAT_ARGS);
|
||||
|
||||
/* Default formatters */
|
||||
static void kformat_char (KFORMAT_ARGS);
|
||||
static void kformat_int (KFORMAT_ARGS);
|
||||
|
@ -99,6 +63,20 @@ kprint_formatter_t kprint_formatters[26] = {
|
|||
NULL, NULL,
|
||||
};
|
||||
|
||||
/* kprint_register(): Register a formatter */
|
||||
void kprint_register(int spec, kprint_formatter_t kformat)
|
||||
{
|
||||
spec |= 0x20;
|
||||
int i = (spec | 0x20) - 'a';
|
||||
|
||||
/* Non-letters */
|
||||
if(i < 0 || i >= 26) return;
|
||||
/* Size-specifying letters */
|
||||
if(spec == 'h' || spec == 'l' || spec == 'z') return;
|
||||
|
||||
kprint_formatters[i] = kformat;
|
||||
}
|
||||
|
||||
//---
|
||||
// Output functions
|
||||
//---
|
||||
|
@ -144,10 +122,10 @@ GINLINE static void kprint_outn(int byte, size_t n)
|
|||
//---
|
||||
|
||||
/* kprint_opt(): Parse option strings */
|
||||
struct kprint_options kprint_opt(char const **options_ptr)
|
||||
kprint_options_t kprint_opt(char const **options_ptr)
|
||||
{
|
||||
/* No options enabled by default, set the size to int */
|
||||
struct kprint_options opt = { .size = 2 };
|
||||
kprint_options_t opt = { .size = 2 };
|
||||
|
||||
/* This function acts as a deterministic finite automaton */
|
||||
enum {
|
||||
|
@ -195,6 +173,7 @@ struct kprint_options kprint_opt(char const **options_ptr)
|
|||
/* Data size */
|
||||
if(c == 'h') opt.size--;
|
||||
if(c == 'l') opt.size++;
|
||||
if(c == 'z') opt.size = 3;
|
||||
|
||||
if(c >= '1' && c <= '9') state = length, options--;
|
||||
}
|
||||
|
@ -211,7 +190,7 @@ struct kprint_options kprint_opt(char const **options_ptr)
|
|||
void kprint(char const *format, va_list *args)
|
||||
{
|
||||
int c, spec;
|
||||
struct kprint_options opt;
|
||||
kprint_options_t opt;
|
||||
|
||||
while((c = *format++))
|
||||
{
|
||||
|
@ -262,37 +241,11 @@ size_t kvsprint(char *output, size_t length, char const *format, va_list *args)
|
|||
// Formatter helpers
|
||||
//---
|
||||
|
||||
/* struct geometry: General geometry for a format */
|
||||
struct geometry
|
||||
/* kformat_geometry(): Calculate the geometry of a format */
|
||||
void kformat_geometry(kprint_options_t *opt, kprint_geometry_t *g)
|
||||
{
|
||||
uint16_t left_spaces; /* Spaces before content */
|
||||
uint8_t sign; /* Sign character (NUL, ' ', '+' or '-') */
|
||||
uint8_t prefix; /* Base prefix ('0', '0x', etc) length */
|
||||
uint16_t zeros; /* For numerical displays, number of zeros */
|
||||
uint16_t content; /* Content length in bytes */
|
||||
uint16_t right_spaces; /* Spaces after content */
|
||||
|
||||
} GPACKED(2);
|
||||
|
||||
/* kformat_geometry(): Calculate the geometry of a format
|
||||
|
||||
The caller must provide the [prefix] and [content] lengths in the geometry
|
||||
structure. Additionally, a sign must be indicated: either '+' if the
|
||||
formatted value is nonnegative, or '-' otherwise. The sign might be updated
|
||||
by this function, but not the other two fields.
|
||||
|
||||
This function is not *really* isolated from the standard kformat functions
|
||||
(as would a precise API be), because the meaning of each options and the
|
||||
process of resolving them varies so much. */
|
||||
static void kformat_geometry(int spec, struct kprint_options *opt,
|
||||
struct geometry *g)
|
||||
{
|
||||
/* Determining whether we are in numerical. The blacklist approach
|
||||
allows custom specifiers which call this function to enable zeros if
|
||||
they like (they can disable them by un-setting opt->alignment if
|
||||
it's '0' in any case) */
|
||||
int numerical = (spec != 'c' && spec != 'p' && spec != 's');
|
||||
|
||||
int integral = (g->style == KPRINT_INTEGER);
|
||||
int numerical = (g->style == KPRINT_NUMERICAL) || integral;
|
||||
ssize_t padding;
|
||||
|
||||
/* Sign character (no discussion required for negative values) */
|
||||
|
@ -305,12 +258,12 @@ static void kformat_geometry(int spec, struct kprint_options *opt,
|
|||
|
||||
g->zeros = 0;
|
||||
|
||||
padding = opt->length - !!g->sign - g->prefix
|
||||
padding = opt->length - (g->sign != 0) - g->prefix
|
||||
- max(g->content, opt->precision);
|
||||
if(padding < 0) padding = 0;
|
||||
|
||||
/* In numerical modes, precision forces zeros */
|
||||
if(numerical && opt->precision)
|
||||
/* In integral modes, precision forces zeros */
|
||||
if(integral && opt->precision)
|
||||
{
|
||||
if(opt->alignment == '0') opt->alignment = 0;
|
||||
|
||||
|
@ -384,7 +337,7 @@ static int digits_8(char *str, uint64_t n)
|
|||
|
||||
static int64_t load_i(int size, va_list *args)
|
||||
{
|
||||
/* All smaller types are promoted to int wth sign extension, so we
|
||||
/* All smaller types are promoted to int with sign extension, so we
|
||||
don't need to care about them. */
|
||||
if(size == 3) return va_arg(*args, long);
|
||||
if(size == 4) return va_arg(*args, long long);
|
||||
|
@ -410,10 +363,10 @@ static void kformat_char(KFORMAT_ARGS)
|
|||
{
|
||||
int c = va_arg(*args, int);
|
||||
|
||||
struct geometry g = {
|
||||
kprint_geometry_t g = {
|
||||
.prefix = 0, .sign = 0, .content = 1,
|
||||
};
|
||||
kformat_geometry(spec, opt, &g);
|
||||
kformat_geometry(opt, &g);
|
||||
|
||||
kprint_outn(' ', g.left_spaces);
|
||||
kprint_out(c);
|
||||
|
@ -434,10 +387,10 @@ static void kformat_str(KFORMAT_ARGS)
|
|||
uint32_t precision = opt->precision ? opt->precision : (-1);
|
||||
while(s[len] && len < precision) len++;
|
||||
|
||||
struct geometry g = {
|
||||
kprint_geometry_t g = {
|
||||
.prefix = 0, .sign = 0, .content = len,
|
||||
};
|
||||
kformat_geometry(spec, opt, &g);
|
||||
kformat_geometry(opt, &g);
|
||||
|
||||
kprint_outn(' ', g.left_spaces);
|
||||
for(size_t i = 0; i < len; i++) kprint_out(s[i]);
|
||||
|
@ -456,9 +409,10 @@ static void kformat_int(KFORMAT_ARGS)
|
|||
int64_t n = load_i(opt->size, args);
|
||||
|
||||
/* Compute the sign and the absolute value */
|
||||
struct geometry g = {
|
||||
kprint_geometry_t g = {
|
||||
.sign = (n < 0) ? '-' : '+',
|
||||
.prefix = 0,
|
||||
.style = KPRINT_INTEGER,
|
||||
};
|
||||
if(n < 0) n = -n;
|
||||
|
||||
|
@ -470,7 +424,7 @@ static void kformat_int(KFORMAT_ARGS)
|
|||
total = max(pure, opt->precision);
|
||||
g.content = total;
|
||||
|
||||
kformat_geometry(spec, opt, &g);
|
||||
kformat_geometry(opt, &g);
|
||||
|
||||
/* Print the result */
|
||||
kprint_outn(' ', g.left_spaces);
|
||||
|
@ -507,10 +461,11 @@ static void kformat_uint(KFORMAT_ARGS)
|
|||
size_t prefix = 0;
|
||||
if(opt->alternative) prefix = (specl != 'u') + (specl == 'x');
|
||||
|
||||
struct geometry g = {
|
||||
.sign = 0, .prefix = prefix, .content = total
|
||||
kprint_geometry_t g = {
|
||||
.sign = 0, .prefix = prefix, .content = total,
|
||||
.style = KPRINT_INTEGER,
|
||||
};
|
||||
kformat_geometry(spec, opt, &g);
|
||||
kformat_geometry(opt, &g);
|
||||
|
||||
/* Output */
|
||||
kprint_outn(' ', g.left_spaces);
|
||||
|
@ -551,9 +506,10 @@ static void kformat_fixed(KFORMAT_ARGS)
|
|||
int64_t n = load_i(opt->size, args);
|
||||
|
||||
/* Compute the sign and the absolute value */
|
||||
struct geometry g = {
|
||||
kprint_geometry_t g = {
|
||||
.sign = (n < 0) ? '-' : '+',
|
||||
.prefix = 0,
|
||||
.style = KPRINT_NUMERICAL,
|
||||
};
|
||||
if(n < 0) n = -n;
|
||||
|
||||
|
@ -561,7 +517,7 @@ static void kformat_fixed(KFORMAT_ARGS)
|
|||
char digits[32];
|
||||
|
||||
g.content = digits_10(digits, n) + 1;
|
||||
kformat_geometry(spec, opt, &g);
|
||||
kformat_geometry(opt, &g);
|
||||
|
||||
/* Print the result */
|
||||
kprint_outn(' ', g.left_spaces);
|
||||
|
@ -576,43 +532,3 @@ static void kformat_fixed(KFORMAT_ARGS)
|
|||
|
||||
kprint_outn(' ', g.right_spaces);
|
||||
}
|
||||
|
||||
//---
|
||||
// Standard formatted printing functions
|
||||
//---
|
||||
|
||||
/* sprintf() */
|
||||
GWEAK int sprintf(char *str, char const *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
int count = kvsprint(str, 65536, format, &args);
|
||||
|
||||
va_end(args);
|
||||
return count;
|
||||
}
|
||||
|
||||
/* vsprintf() */
|
||||
GWEAK int vsprintf(char *str, char const *format, va_list args)
|
||||
{
|
||||
return kvsprint(str, 65536, format, &args);
|
||||
}
|
||||
|
||||
/* snprintf() */
|
||||
GWEAK int snprintf(char *str, size_t n, char const *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
int count = kvsprint(str, n, format, &args);
|
||||
|
||||
va_end(args);
|
||||
return count;
|
||||
}
|
||||
|
||||
/* vsprintf() */
|
||||
GWEAK int vsnprintf(char *str, size_t n, char const *format, va_list args)
|
||||
{
|
||||
return kvsprint(str, n, format, &args);
|
||||
}
|
43
src/std/print.c
Normal file
43
src/std/print.c
Normal file
|
@ -0,0 +1,43 @@
|
|||
//---
|
||||
// gint:src:print - Standard formatted printing functions
|
||||
//---
|
||||
|
||||
#include <gint/std/stdio.h>
|
||||
#include <gint/kprint.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* sprintf() */
|
||||
GWEAK int sprintf(char *str, char const *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
int count = kvsprint(str, 65536, format, &args);
|
||||
|
||||
va_end(args);
|
||||
return count;
|
||||
}
|
||||
|
||||
/* vsprintf() */
|
||||
GWEAK int vsprintf(char *str, char const *format, va_list args)
|
||||
{
|
||||
return kvsprint(str, 65536, format, &args);
|
||||
}
|
||||
|
||||
/* snprintf() */
|
||||
GWEAK int snprintf(char *str, size_t n, char const *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
int count = kvsprint(str, n, format, &args);
|
||||
|
||||
va_end(args);
|
||||
return count;
|
||||
}
|
||||
|
||||
/* vsprintf() */
|
||||
GWEAK int vsnprintf(char *str, size_t n, char const *format, va_list args)
|
||||
{
|
||||
return kvsprint(str, n, format, &args);
|
||||
}
|
Loading…
Reference in a new issue