This commit is contained in:
Zack Buhman 2024-12-01 15:53:03 -06:00
commit 9a5602f287
26 changed files with 2022 additions and 0 deletions

21
.gitignore vendored Executable file
View File

@ -0,0 +1,21 @@
*.pyc
__pycache__
.~*
*.BIN
*.bin
*.elf
*.d
*.iso
*.cdi
*.o
*.out
*.gch
scramble
cdi4dc
tools/ttf_outline
tools/ttf_bitmap
tools/ftdi_transfer
k_means_vq
*.blend1
*.scramble
*.FCStd1

12
Makefile Normal file
View File

@ -0,0 +1,12 @@
all: $(patsubst %.cpp,%.elf,$(wildcard example/*.cpp))
include dreamcast/base.mk
include dreamcast/common.mk
include dreamcast/headers.mk
MAKEFILE_PATH := $(abspath $(firstword $(MAKEFILE_LIST)))
CFLAGS += -I$(dir $(MAKEFILE_PATH))
CFLAGS += -I$(dir $(MAKEFILE_PATH))dreamcast/
LIB ?= $(dir $(MAKEFILE_PATH))dreamcast
include aoc.mk

25
aoc.mk Normal file
View File

@ -0,0 +1,25 @@
OBJ = \
parse.o \
heapsort.o \
printf.o \
runner.o \
unparse.o
DREAMCAST_OBJ = \
runner_dreamcast.o \
input_dreamcast.o \
$(LIB)/holly/core.o \
$(LIB)/holly/region_array.o \
$(LIB)/holly/background.o \
$(LIB)/holly/ta_fifo_polygon_converter.o \
$(LIB)/holly/video_output.o \
$(LIB)/font/dejavusansmono/dejavusansmono.data.o \
$(LIB)/sh7091/serial.o
DAY1_OBJ = \
day1/sample1.txt.o \
day1/input.txt.o \
day1/solution.o
aoc.elf: LDSCRIPT = $(LIB)/main.lds
aoc.elf: $(START_OBJ) $(OBJ) $(DREAMCAST_OBJ) $(DAY1_OBJ)

1000
day1/input.txt Normal file

File diff suppressed because it is too large Load Diff

15
day1/input.txt.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t _binary_day1_input_txt_start __asm("_binary_day1_input_txt_start");
extern uint32_t _binary_day1_input_txt_end __asm("_binary_day1_input_txt_end");
extern uint32_t _binary_day1_input_txt_size __asm("_binary_day1_input_txt_size");
#ifdef __cplusplus
}
#endif

6
day1/sample1.txt Normal file
View File

@ -0,0 +1,6 @@
3 4
4 3
2 5
1 3
3 9
3 3

15
day1/sample1.txt.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
extern uint32_t _binary_day1_sample1_txt_start __asm("_binary_day1_sample1_txt_start");
extern uint32_t _binary_day1_sample1_txt_end __asm("_binary_day1_sample1_txt_end");
extern uint32_t _binary_day1_sample1_txt_size __asm("_binary_day1_sample1_txt_size");
#ifdef __cplusplus
}
#endif

1
day1/sample2.txt Symbolic link
View File

@ -0,0 +1 @@
sample1.txt

85
day1/solution.c Normal file
View File

@ -0,0 +1,85 @@
#include "input.h"
#include "parse.h"
#include "heapsort.h"
struct list {
int left[1000];
int right[1000];
};
int parse_input(const char * input, int length, struct list * list)
{
const char * end = input + length;
int i = 0;
while (input < end) {
input = parse_base10(input, &list->left[i]);
input = parse_skip(input, ' ');
input = parse_base10(input, &list->right[i]);
input = parse_skip(input, '\n');
i++;
}
return i;
}
int abs(int n)
{
if (n < 0)
return -n;
else
return n;
}
int day1_part1(char * input, int length)
{
struct list list;
int list_length = parse_input(input, length, &list);
heapsort(list.left, list_length);
heapsort(list.right, list_length);
int sum = 0;
for (int i = 0; i < list_length; i++) {
int distance = abs(list.left[i] - list.right[i]);
sum += distance;
}
return sum;
}
int day1_part2(char * input, int length)
{
struct list list;
int list_length = parse_input(input, length, &list);
heapsort(list.left, list_length);
heapsort(list.right, list_length);
int i = 0;
int j = 0;
int sum = 0;
while (i < list_length) {
int n = list.left[i];
int mul_l = 1;
while (list.left[i + mul_l] == n) {
mul_l += 1;
}
i += mul_l;
while (list.right[j] < n) {
j++;
}
int mul_r = 0;
while (list.right[j + mul_r] == n) {
mul_r += 1;
}
j += mul_r;
sum += n * mul_l * mul_r;
}
return sum;
}

1
dreamcast Symbolic link
View File

@ -0,0 +1 @@
../dreamcast

58
heapsort.c Normal file
View File

@ -0,0 +1,58 @@
#include "heapsort.h"
static inline void swap(int * a, int * b)
{
int temp = *a;
*a = *b;
*b = temp;
}
static inline int left_child_index(int i)
{
return 2 * i + 1;
}
void heapsort(int * l, int length)
{
int start = length / 2;
int end = length;
while (end > 1) {
if (start > 0) {
start = start - 1;
} else {
end = end - 1;
swap(&l[end], &l[0]);
}
int root = start;
while (left_child_index(root) < end) {
int child = left_child_index(root);
int right_child = child + 1;
if (right_child < end && l[child] < l[right_child]) {
child = right_child;
}
if (l[root] < l[child]) {
swap(&l[root], &l[child]);
root = child;
} else {
break;
}
}
}
}
#ifdef HEAPSORT_TEST
#include <stdio.h>
int main()
{
int l[] = {3, 5, 1, 8, 2, 8, 7, 5, 4, 9};
int l_length = (sizeof (l)) / (sizeof (l[0]));
heapsort(l, l_length);
for (int i = 0; i < l_length; i++) {
printf("%d ", l[i]);
}
printf("\n");
}
#endif

11
heapsort.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void heapsort(int * l, int length);
#ifdef __cplusplus
}
#endif

12
input.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void open_input(int day, char ** buf, int * length);
void open_sample(int day, int part, char ** buf, int * length);
#ifdef __cplusplus
}
#endif

50
input_dreamcast.c Normal file
View File

@ -0,0 +1,50 @@
#include <stdint.h>
#include <stddef.h>
#include "input.h"
#include "day1/sample1.txt.h"
#include "day1/input.txt.h"
struct start_size {
char * start;
uint32_t size;
};
static struct start_size input[] = {
{ ( char *)&_binary_day1_input_txt_start,
(uint32_t)&_binary_day1_input_txt_size },
};
static struct start_size sample[][2] = {
{
{ ( char *)&_binary_day1_sample1_txt_start,
(uint32_t)&_binary_day1_sample1_txt_size },
{ ( char *)&_binary_day1_sample1_txt_start,
(uint32_t)&_binary_day1_sample1_txt_size },
},
};
const int input_size = (sizeof (input)) / (sizeof (input[0]));
void open_input(int day, char ** buf, int * length)
{
if (day < 1 || day > input_size) {
*buf = NULL;
*length = 0;
} else {
*buf = input[day - 1].start;
*length = input[day - 1].size;
}
}
void open_sample(int day, int part, char ** buf, int * length)
{
if (day < 1 || day > input_size || part < 1 || part > 2) {
*buf = NULL;
*length = 0;
} else {
*buf = sample[day - 1][part - 1].start;
*length = sample[day - 1][part - 1].size;
}
}

43
input_stdlib.c Normal file
View File

@ -0,0 +1,43 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "input.h"
static void read_file(const char * filename, char ** buf, int * length)
{
FILE * f = fopen(filename, "rb");
assert(f != NULL);
int ret = fseek(f, 0L, SEEK_END);
assert(ret >= 0);
long size = ftell(f);
assert(size > 0);
rewind(f);
*buf = malloc(size);
size_t read = fread(*buf, 1, size, f);
assert(read == size);
*length = size;
}
void open_input(int day, char ** buf, int * length)
{
char filename[1024];
const char * format = "day%d/input.txt";
snprintf(filename, (sizeof (filename)), format, day);
read_file(filename, buf, length);
}
void open_sample(int day, int part, char ** buf, int * length)
{
char filename[1024];
const char * format = "day%d/sample%d.txt";
snprintf(filename, (sizeof (filename)), format, day, part);
read_file(filename, buf, length);
}

4
minmax.h Normal file
View File

@ -0,0 +1,4 @@
#pragma once
#define min(a, b) ( (a < b) ? a : b )
#define max(a, b) ( (a > b) ? a : b )

45
parse.c Normal file
View File

@ -0,0 +1,45 @@
#include <stdbool.h>
#include "parse.h"
const char * parse_skip(const char * s, char c)
{
while (*s == c) {
s++;
}
return s;
}
static int base10_digit(char c)
{
switch (c) {
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
default: return -1;
}
}
const char * parse_base10(const char * s, int * n)
{
*n = 0;
while (true) {
int digit = base10_digit(*s);
if (digit == -1)
break;
*n *= 10;
*n += digit;
s++;
}
return s;
}

12
parse.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
const char * parse_skip(const char * s, char c);
const char * parse_base10(const char * s, int * n);
#ifdef __cplusplus
}
#endif

123
printf.c Normal file
View File

@ -0,0 +1,123 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include "parse.h"
#include "unparse.h"
#include "printf.h"
enum format_type {
FORMAT_BASE10,
FORMAT_BASE16,
FORMAT_STRING,
FORMAT_PERCENT,
};
struct format {
enum format_type type;
int pad_length;
char fill_char;
};
static const char * parse_escape(const char * format, struct format * ft);
static const char * parse_fill_pad(const char * format, struct format * ft)
{
if (*format == 0)
return format;
ft->fill_char = *format++;
format = parse_base10(format, &ft->pad_length);
return parse_escape(format, ft);
}
static const char * parse_escape(const char * format, struct format * ft)
{
switch (*format) {
case 0:
return format;
case 'd':
ft->type = FORMAT_BASE10;
return format + 1;
case 'x':
ft->type = FORMAT_BASE16;
return format + 1;
case 's':
ft->type = FORMAT_STRING;
return format + 1;
case '%':
ft->type = FORMAT_PERCENT;
return format + 1;
default:
return parse_fill_pad(format, ft);
}
}
struct output_buffer global_output_buffer = {0};
void _printf(const char * format, ...)
{
va_list args;
va_start(args, format);
while (true) {
if (*format == 0)
break;
switch (*format) {
case '%':
{
struct format ft = {0};
format = parse_escape(format + 1, &ft);
switch (ft.type) {
case FORMAT_BASE10:
{
int32_t num = va_arg(args, int32_t);
char * s = &global_output_buffer.buf[global_output_buffer.buf_ix];
int offset = unparse_base10(s, num, ft.pad_length, ft.fill_char);
global_output_buffer.buf_ix += offset;
}
break;
case FORMAT_BASE16:
{
uint32_t num = va_arg(args, uint32_t);
char * s = &global_output_buffer.buf[global_output_buffer.buf_ix];
int offset = unparse_base16(s, num, ft.pad_length, ft.fill_char);
global_output_buffer.buf_ix += offset;
}
break;
case FORMAT_STRING:
{
const char * s = va_arg(args, const char *);
while (*s != 0) {
global_output_buffer.buf[global_output_buffer.buf_ix++] = *s++;
}
}
break;
case FORMAT_PERCENT:
global_output_buffer.buf[global_output_buffer.buf_ix++] = '%';
break;
}
}
break;
default:
global_output_buffer.buf[global_output_buffer.buf_ix++] = *format++;
break;
}
}
va_end(args);
}
#ifdef PRINTF_TEST
#include <stdio.h>
int main()
{
_printf("test `% 8d` %s foobar\n", 1234, "hello");
_printf("test2 `% 8d` %s foobar\n", -1234, "bar");
global_output_buffer.buf[global_output_buffer.buf_ix] = 0;
puts(global_output_buffer.buf);
}
#endif

22
printf.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void _printf(const char * format, ...);
#ifndef PRINTF_TEST
#define printf(...) _printf(__VA_ARGS__)
#endif
struct output_buffer {
int buf_ix;
char buf[16 * 1024];
};
extern struct output_buffer global_output_buffer;
#ifdef __cplusplus
}
#endif

0
putchar_dreamcast.c Normal file
View File

34
runner.c Normal file
View File

@ -0,0 +1,34 @@
#include "printf.h"
#include "input.h"
#include "runner.h"
int day1_part1(char * input, int length);
int day1_part2(char * input, int length);
typedef int (* part_func)(char * input, int length);
part_func solution[][2] = {
{day1_part1, day1_part2},
};
const int solution_days = (sizeof (solution)) / (sizeof (solution[0]));
bool runner_tick(struct runner_state * runner_state)
{
int part = runner_state->tick % 2;
int day = runner_state->tick / 2;
if (day >= solution_days)
return true;
char * buf;
int length;
//open_sample(day + 1, part + 1, &buf, &length);
open_input(day + 1, &buf, &length);
int answer = solution[day][part](buf, length);
printf("day %d part %d: %d\n", day + 1, part + 1, answer);
runner_state->tick += 1;
return false;
}

17
runner.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
struct runner_state {
int tick;
};
bool runner_tick(struct runner_state * runner_state);
#ifdef __cplusplus
}
#endif

282
runner_dreamcast.cpp Normal file
View File

@ -0,0 +1,282 @@
#include <cstdint>
#include "holly/texture_memory_alloc2.hpp"
#include "holly/isp_tsp.hpp"
#include "holly/ta_fifo_polygon_converter.hpp"
#include "holly/ta_parameter.hpp"
#include "holly/ta_global_parameter.hpp"
#include "holly/ta_vertex_parameter.hpp"
#include "holly/ta_bits.hpp"
#include "holly/background.hpp"
#include "holly/region_array.hpp"
#include "holly/holly.hpp"
#include "holly/core.hpp"
#include "holly/core_bits.hpp"
#include "holly/video_output.hpp"
#include "sh7091/store_queue.hpp"
#include "sh7091/serial.hpp"
#include "font/font.hpp"
#include "font/dejavusansmono/dejavusansmono.data.h"
#include "palette.hpp"
#include "printf.h"
#include "runner.h"
struct vertex {
float x;
float y;
float z;
float u;
float v;
};
const struct vertex strip_vertices[4] = {
// [ position ] [ uv coordinates ]
{ 0.f, 1.f, 0.f, 0.f, 1.f, },
{ 0.f, 0.f, 0.f, 0.f, 0.f, },
{ 1.f, 1.f, 0.f, 1.f, 1.f, },
{ 1.f, 0.f, 0.f, 1.f, 0.f, },
};
constexpr uint32_t strip_length = (sizeof (strip_vertices)) / (sizeof (struct vertex));
void transform_start(const uint32_t texture_width, uint32_t texture_height)
{
const uint32_t parameter_control_word = para_control::para_type::polygon_or_modifier_volume
| para_control::list_type::translucent
| obj_control::col_type::packed_color
| obj_control::texture;
const uint32_t isp_tsp_instruction_word = isp_tsp_instruction_word::depth_compare_mode::greater
| isp_tsp_instruction_word::culling_mode::no_culling;
const uint32_t tsp_instruction_word = tsp_instruction_word::src_alpha_instr::src_alpha
| tsp_instruction_word::dst_alpha_instr::one
| tsp_instruction_word::fog_control::no_fog
| tsp_instruction_word::use_alpha
| tsp_instruction_word::texture_u_size::from_int(texture_width)
| tsp_instruction_word::texture_v_size::from_int(texture_height);
const uint32_t texture_address = texture_memory_alloc::texture.start;
const uint32_t texture_control_word = texture_control_word::pixel_format::_8bpp_palette
| texture_control_word::scan_order::twiddled
| texture_control_word::texture_address(texture_address / 8);
*reinterpret_cast<ta_global_parameter::polygon_type_0 *>(store_queue) =
ta_global_parameter::polygon_type_0(parameter_control_word,
isp_tsp_instruction_word,
tsp_instruction_word,
texture_control_word,
0, // data_size_for_sort_dma
0 // next_address_for_sort_dma
);
sq_transfer_32byte(ta_fifo_polygon_converter);
}
int32_t transform_char(const uint32_t texture_width,
const uint32_t texture_height,
const uint32_t first_char_code,
const glyph * glyphs,
const char c,
int32_t horizontal_advance,
int32_t vertical_advance)
{
auto& glyph = glyphs[c - first_char_code];
for (uint32_t i = 0; i < strip_length; i++) {
float x = strip_vertices[i].x;
float y = strip_vertices[i].y;
float z = strip_vertices[i].z;
x *= glyph.bitmap.width;
y *= glyph.bitmap.height;
x += (float)(horizontal_advance + glyph.metrics.horiBearingX) / 64.0;
y += (float)(vertical_advance - glyph.metrics.horiBearingY) / 64.0;
z = 1.f / (z + 10.f);
float u = strip_vertices[i].u;
float v = strip_vertices[i].v;
u *= glyph.bitmap.width;
v *= glyph.bitmap.height;
u += glyph.bitmap.x;
v += glyph.bitmap.y;
u = u / static_cast<float>(texture_width);
v = v / static_cast<float>(texture_height);
bool end_of_strip = i == strip_length - 1;
*reinterpret_cast<ta_vertex_parameter::polygon_type_3 *>(store_queue) =
ta_vertex_parameter::polygon_type_3(polygon_vertex_parameter_control_word(end_of_strip),
x, y, z,
u, v,
0, // base_color
0 // offset_color
);
sq_transfer_32byte(ta_fifo_polygon_converter);
}
return glyph.metrics.horiAdvance;
}
void transfer_scene(const struct font * font,
const struct glyph * glyphs)
{
transform_start(font->texture_width, font->texture_height);
int32_t horizontal_advance = font->face_metrics.max_advance / 5; // 26.6 fixed point
int32_t vertical_advance = font->face_metrics.height; // 26.6 fixed point
for (int i = 0; i < global_output_buffer.buf_ix; i++) {
char c = global_output_buffer.buf[i];
if (c < 0x20 && c > 0x7f) {
continue;
} else if (c == '\n') {
horizontal_advance = font->face_metrics.max_advance / 5;
vertical_advance += font->face_metrics.height;
} else {
horizontal_advance += transform_char(font->texture_width,
font->texture_height,
font->first_char_code,
glyphs,
c,
horizontal_advance,
vertical_advance);
}
}
*reinterpret_cast<ta_global_parameter::end_of_list *>(store_queue) =
ta_global_parameter::end_of_list(para_control::para_type::end_of_list);
sq_transfer_32byte(ta_fifo_polygon_converter);
}
void copy_font(const uint8_t * src,
const uint32_t z_curve_max_ix)
{
auto texture = reinterpret_cast<volatile uint32_t *>(&texture_memory64[texture_memory_alloc::texture.start / 4]);
for (uint32_t i = 0; i < z_curve_max_ix / 4; i++) {
texture[i] = reinterpret_cast<const uint32_t *>(src)[i];
}
}
int main()
{
serial::init(0);
serial::string("framebuffer: ");
serial::integer<uint32_t>((uint32_t)&texture_memory32[texture_memory_alloc::framebuffer[0].start / 4]);
constexpr uint32_t ta_alloc = ta_alloc_ctrl::pt_opb::no_list
| ta_alloc_ctrl::tm_opb::no_list
| ta_alloc_ctrl::t_opb::_32x4byte
| ta_alloc_ctrl::om_opb::no_list
| ta_alloc_ctrl::o_opb::no_list;
constexpr int render_passes = 1;
constexpr struct opb_size opb_size[render_passes] = {
{
.opaque = 0,
.opaque_modifier = 0,
.translucent = 32 * 4,
.translucent_modifier = 0,
.punch_through = 0
}
};
holly.SOFTRESET = softreset::pipeline_soft_reset
| softreset::ta_soft_reset;
holly.SOFTRESET = 0;
core_init();
constexpr int framebuffer_width = 640;
constexpr int framebuffer_height = 480;
constexpr int tile_width = framebuffer_width / 32;
constexpr int tile_height = framebuffer_height / 32;
region_array_multipass(tile_width,
tile_height,
opb_size,
render_passes,
texture_memory_alloc::region_array[0].start,
texture_memory_alloc::object_list[0].start);
region_array_multipass(tile_width,
tile_height,
opb_size,
render_passes,
texture_memory_alloc::region_array[1].start,
texture_memory_alloc::object_list[1].start);
background_parameter2(texture_memory_alloc::background[0].start,
0xff220033);
background_parameter2(texture_memory_alloc::background[1].start,
0xff220033);
auto font = reinterpret_cast<const struct font *>(&_binary_font_dejavusansmono_dejavusansmono_data_start);
auto glyphs = reinterpret_cast<const struct glyph *>(&font[1]);
auto texture = reinterpret_cast<const uint8_t *>(&glyphs[font->glyph_count]);
copy_font(texture, font->max_z_curve_ix);
palette_data<256>();
int ta = -1;
int core = -2;
struct runner_state runner_state = {0};
video_output::set_mode_vga();
bool done = false;
while (true) {
if (core >= 0) {
// core = 0 ; core = 1
// ta = 1 ; ta = 0
core_wait_end_of_render_video();
while (!spg_status::vsync(holly.SPG_STATUS));
holly.FB_R_SOF1 = texture_memory_alloc::framebuffer[core].start;
while (spg_status::vsync(holly.SPG_STATUS));
}
if (core == 0) {
if (done == true)
break;
done = runner_tick(&runner_state);
}
// core = -2 ; core = 1 ; core = 0
// ta = -1 ; ta = 0 ; ta = 1
core += 1;
ta += 1;
if (core > 1) core = 0;
if (ta > 1) ta = 0;
if (core >= 0) {
// core = 1 ; core = 0
// ta = 0 ; ta = 1
ta_wait_translucent_list();
core_start_render2(texture_memory_alloc::region_array[core].start,
texture_memory_alloc::isp_tsp_parameters[core].start,
texture_memory_alloc::background[core].start,
texture_memory_alloc::framebuffer[core].start,
framebuffer_width);
}
// core = -1 ; core = 1 ; core = 0
// ta = 0 ; ta = 0 ; ta = 1
ta_polygon_converter_init2(texture_memory_alloc::isp_tsp_parameters[ta].start,
texture_memory_alloc::isp_tsp_parameters[ta].end,
texture_memory_alloc::object_list[ta].start,
texture_memory_alloc::object_list[ta].end,
opb_size[0].total(),
ta_alloc,
tile_width,
tile_height);
transfer_scene(font, glyphs);
}
serial::string("return\n");
}

116
unparse.c Normal file
View File

@ -0,0 +1,116 @@
#include <stdint.h>
#include <stdbool.h>
#include "minmax.h"
static int digits_base10(uint32_t n)
{
if (n >= 1000000000) return 10;
if (n >= 100000000) return 9;
if (n >= 10000000) return 8;
if (n >= 1000000) return 7;
if (n >= 100000) return 6;
if (n >= 10000) return 5;
if (n >= 1000) return 4;
if (n >= 100) return 3;
if (n >= 10) return 2;
return 1;
}
int unparse_base10(char * s, int32_t n, int len, char fill)
{
bool negative = false;
int digits = 0;
if (n < 0) {
digits += 1;
n = -n;
negative = true;
}
digits += digits_base10(n);
len = max(digits, len);
int ret = len;
while (len > digits) {
*s++ = fill;
--len;
}
if (negative) {
*s++ = '-';
len--;
}
while (len > 0) {
const uint32_t digit = n % 10;
n = n / 10;
s[--len] = digit + 48;
}
return ret;
}
static int digits_base16(uint32_t n)
{
if (n <= 0xf) return 1;
if (n <= 0xff) return 2;
if (n <= 0xfff) return 3;
if (n <= 0xffff) return 4;
if (n <= 0xfffff) return 5;
if (n <= 0xffffff) return 6;
if (n <= 0xfffffff) return 7;
return 8;
}
int unparse_base16(char * s, uint32_t n, int len, char fill)
{
int digits = digits_base16(n);
len = max(digits, len);
int ret = len;
while (len > digits) {
*s++ = fill;
--len;
}
while (len > 0) {
uint32_t nib = n & 0xf;
n = n >> 4;
if (nib > 9) {
nib += (97 - 10);
} else {
nib += (48 - 0);
}
s[--len] = nib;
}
return ret;
}
#ifdef UNPARSE_TEST
#include <stdio.h>
int main()
{
char s[1024];
{
int n = 124;
int offset = unparse_base10(s, n, 6, ' ');
s[offset] = 0;
printf("`%s`\n", s);
}
{
int n = 0x5678;
int offset = unparse_base16(s, n, 7, '0');
s[offset] = 0;
printf("`%s`\n", s);
}
}
#endif

12
unparse.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
int unparse_base10(char * s, uint32_t n, int len, char fill);
int unparse_base16(char * s, uint32_t n, int len, char fill);
#ifdef __cplusplus
}
#endif