Added formatted printing for stdio: handles everything except doubles and %m.

This commit is contained in:
lephe 2016-07-25 22:38:47 +02:00
parent 7f174043a5
commit b2151886bc
40 changed files with 1496 additions and 449 deletions

7
.gitignore vendored
View File

@ -1,7 +1,3 @@
# Sources for old gint versions.
gint.old.1/**
gint.old.2/**
# Build directory
build/**
@ -11,3 +7,6 @@ build/**
# Object files.
*.o
# Some notes.
LIBC

View File

@ -14,7 +14,7 @@
# Modules
modules-gint = core keyboard mmu mpu rtc screen timer \
bopti display gray tales
modules-libc = setjmp string
modules-libc = setjmp string stdio
# Targets
target-g1a = gintdemo.g1a
@ -39,7 +39,7 @@ demo-res = $(notdir $(wildcard demo/resources/*))
demo-obj = $(patsubst %,build/demo_%.o,$(demo-src) $(demo-res))
demo-elf = build/gintdemo.elf
demo-bin = build/gintdemo.bin
demo-libs = -lgcc -L. -lgint -lc
demo-libs = -L. -lgint -lc -lgcc
# Specific objects
obj-lib-spec = build/display_font_system.bmp.o

View File

@ -1,141 +0,0 @@
#! /usr/bin/make -f
#---
# fx-9860g lib Makefile.
#---
#
# Variables and configuration.
#
# Tools
cc = sh3eb-elf-gcc
as = sh3eb-elf-as
ar = sh3eb-elf-ar
ob = sh3eb-elf-objcopy
wr = g1a-wrapper
# Output files
g1a = ginttest.g1a
bin = build/ginttest.bin
elf = build/ginttest.elf
# Command-line options
cflags = -m3 -mb -nostdlib -ffreestanding -W -Wall \
-I . -isystem include
lib = -lgcc -L. -lgint -lc
#
# Source and object files.
#
# Gint library.
src-lib = crt0.c syscalls.s \
gint.c gint_vbr.s gint_7705.c gint_7305.c \
mpu.c keyboard.c screen.c display.c gray.c timer.c tales.c \
bopti.c
hea-lib = 7305.h 7705.h gint.h \
stdlib.h \
mpu.h keyboard.h screen.h display.h gray.h timer.h tales.h
obj-lib = $(patsubst %, build/%.o, $(src-lib))
hdr-lib = $(patsubst %, include/%, $(hea-lib))
# Standard library.
src-std = setjmp.s string.c
hea-std = setjmp.h string.h ctype.h
obj-std = $(patsubst %, build/%.o, $(src-std))
hdr-std = $(patsubst %, include/%, $(hea-str))
# Test application.
src-app = ginttest.c
img-app = bitmap_opt.bmp swords.bmp sprites.bmp symbol.bmp symbol2.bmp \
illustration.bmp
res-app = $(patsubst %, build/%.o, $(img-app)) build/font.o
#
# Building rules.
#
all: build libgint.a libc.a ginttest.g1a
build:
mkdir -p build
libgint.a: $(obj-lib)
$(ar) rcs libgint.a $(obj-lib)
@ echo "\033[32;1mLibrary file size: "`stat -c %s libgint.a` \
"bytes\033[0m"
libc.a: $(obj-std)
$(ar) rcs libc.a $(obj-std)
@ echo "\033[32;1mStandard file size: "`stat -c %s libc.a` \
"bytes\033[0m"
$(g1a): libgint.a $(src-app) $(res-app)
$(cc) $(src-app) $(res-app) -T ginttest.ld -o $(elf) $(cflags) $(lib)
$(ob) -R .comment -R .bss -O binary $(elf) $(bin)
$(wr) $(bin) -o ginttest.g1a -i icon.bmp
@ echo "\033[32;1mBinary file size: "`stat -c %s $(bin)`" bytes\033[0m"
@ sh3eb-elf-objdump -h build/ginttest.elf
#
# Resource management.
#
build/%.c.o: src/%.c $(hdr-lib) $(hdr-std)
$(cc) $(cflags) -O2 -c $< -o $@
build/%.s.o: src/%.s
$(as) -c $^ -o $@
build/%.bmp.o: resources/%.bmp
fxconv $^ -o $@ --preview
build/font.o: resources/font.bmp
fxconv --font $^ -o $@
# File gint.c should not be optimized... looks like attribute((interrupt_
# handler)) doesn't like it. (It could be a gint bug also, I should check.)
build/gint.c.o: src/gint.c $(hdr-lib) $(hdr-std)
$(cc) $(cflags) -c $< -o $@
%.c.o: %.c $(hdr-lib) $(hdr-std)
$(cc) $(cflags) -c $< -o $@
%.s.o: %.s
$(as) -c $^ -o $@
#
# Cleaning rules.
#
clean:
@ rm -f $(obj-lib) $(obj-std) $(obj-app) $(res-app)
@ rm -f $(bin) $(elf)
mrproper: clean
@ rm -f build/*
@ rm -f ginttest.g1a libc.a libgint.a
distclean: mrproper
re: distclean all
#
# Installing shorthand.
#
install:
usb-connector SEND ginttest.g1a ginttest.g1a fls0
.PHONY: all clean mrproper distclean re install

View File

@ -5,7 +5,7 @@ gint (pronounce 'guin') is a low-level library for fx-9860G calculators. It
provides the tools needed to develop programs under Linux using the gcc
toolchain (sh3eb-elf).
By the way, gint is free software ; you may use it for any purpose, share it,
By the way, gint is free software; you may use it for any purpose, share it,
modify it and share you changes. No credit of any kind is needed, though
appreciated.
@ -15,7 +15,8 @@ Interrupt handler
-----------------
The interrupt handler is the lowest-level part of the library. It directly
accesses the peripheral modules and performs keyboard analyzes, swaps screen buffers, etc.
accesses the peripheral modules and performs keyboard analyzes, swaps screen
buffers, etc.
gint does not allow user programs to use their own handlers. However, it is
possible to map interrupt-driven events to user callbacks using the public API
@ -37,11 +38,15 @@ register access and implements a few standard functions.
Building and installing
-----------------------
There a some dependencies:
* The `sh3eb-elf` toolchain somewhere in the PATH
* The fxSDK installed and available in the PATH
The easiest way to build gint is simply to enter a terminal and execute `make`.
This will build the following components :
* `libgint.a`, the gint library
* `libc.a`, a (very) few standard procedures
* `ginttest.g1a`, a test application
* `gintdemo.g1a`, a test application
The common `clean`, `mrproper`, and `distclean` rules will clean the directory.
@ -50,13 +55,18 @@ The common `clean`, `mrproper`, and `distclean` rules will clean the directory.
Source organization
-------------------
gint is made of *modules*. Each module has its own source directory (which is
`/src/module`), and its associated header file in `/include`. A module folder
contains three types of files :
* Internal headers: shared only among the files of a module
* Single-function source files: to avoid linking against the whole library,
some functions have their own object files. Their names are the function
names.
* Other source files: contain multiple functions that always work together, or
are lightweight enough not to be separated. Their names often begin with
`module_`.
gint is made of *modules*. Each module may have any of the following
components:
* A header file in `/include`
* An internal header file in `/include/internals`
* Single-function source files in `/src/module`: to avoid linking against the
whole library, some functions have their own object files. Their names are
those of the functions.
* Other source files in `/src/module`: contain multiple functions that always
work together, or are lightweight enough not to be separated. Their names
often begin with `module_`.
* Other files in `/src/module`: the `display` module contains a font, I think.
The demo application is in the `demo` directory.
The `doc` folder contains some documentation.

2
TODO
View File

@ -12,6 +12,7 @@
@ vram overflow
@ keyboard test threaded interface
+ have timers use structures from 7705.h and 7305.h
+ full and partial transparency
+ gint vs. ML with 248x124 at (-60, -28)
+ call exit handlers
@ -25,7 +26,6 @@
+ bitmap parts
- improve exception handler debugging information (if possible)
- write and test gray engine
- full rtc driver (time)
- callbacks and complete user API

View File

@ -9,79 +9,36 @@
#include <gray.h>
#include <stdint.h>
#include <stdarg.h>
#include <7305.h>
#include <internals/stdio.h>
//---
// A few ugly procedures for displaying text. Will have to enhance this
// soon -- which means printf().
// A few procedures for displaying text aligned on a 21*8 grid.
// Not really beautiful... but this will do.
//---
void print(int x, int y, const char *format, ...)
{
if(x < 1 || x > 21 || y < 1 || y > 8) return;
va_list args;
va_start(args, format);
__printf(0, format, args);
va_end(args);
if(gray_runs()) gtext(__stdio_buffer, x * 6 - 5, y * 8 - 8);
else dtext(__stdio_buffer, x * 6 - 5, y * 8 - 8);
}
void locate(const char *str, int x, int y)
{
if(x < 1 || x > 21 || y < 1 || y > 8) return;
if(gray_runs()) gtext(str, x * 6 - 5, y * 8 - 7);
else dtext(str, x * 6 - 5, y * 8 - 7);
}
void locate_hex(unsigned int n, int x, int y)
{
char str[11] = "0x";
int i;
for(i = 0; i < 8; i++)
{
str[9 - i] = (n & 0xf) + '0' + 39 * ((n & 0xf) > 9);
n >>= 4;
}
str[10] = 0;
locate(str, x, y);
}
void locate_bin(unsigned char n, int x, int y)
{
char str[9];
int i;
for(i = 0; i < 8;i ++)
{
str[7 - i] = (n & 1) + '0';
n >>= 1;
}
str[8] = 0;
locate(str, x, y);
}
void locate_hexa(unsigned int n, int digits, int x, int y)
{
char str[20];
int i;
for(i = digits - 1; i >= 0; i--)
{
str[i] = (n & 0xf) + '0' + 39 * ((n & 0xf) > 9);
n >>= 4;
}
str[digits] = 0;
locate(str, x, y);
}
void locate_int(int n, int x, int y)
{
char str[20] = { 0 };
int i, o = 0;
int digits = 0, copy = n;
if(!n) { str[o++] = '0'; locate(str, x, y); return; }
if(n < 0) str[o++] = '-', n = -n;
while(copy) digits++, copy /= 10;
for(i = 0; i < digits; i++)
{
str[o + digits - i - 1] = n % 10 + '0';
n /= 10;
}
locate(str, x, y);
if(gray_runs()) gtext(str, x * 6 - 5, y * 8 - 8);
else dtext(str, x * 6 - 5, y * 8 - 8);
}
@ -90,6 +47,26 @@ void locate_int(int n, int x, int y)
// Test applications.
//---
/*
keyboard_test_binary()
Prints a byte as binary/
*/
void keyboard_test_binary(int byte, int x, int y)
{
char str[9];
int i = 7;
str[8] = 0;
while(i >= 0)
{
str[i] = '0' + (byte & 1);
byte >>= 1;
i--;
}
locate(str, x, y);
}
/*
keyboard_test_timer()
Displays a keyboard test. The keyboard state is displayed as well, but
@ -103,17 +80,17 @@ void keyboard_test_timer(void)
dclear_area(5, 10, 71, 34);
locate_bin(state[0], 5, 10);
locate_bin(state[1], 5, 16);
locate_bin(state[2], 5, 22);
locate_bin(state[3], 5, 28);
locate_bin(state[4], 5, 34);
keyboard_test_binary(state[0], 1, 1);
keyboard_test_binary(state[1], 1, 2);
keyboard_test_binary(state[2], 1, 3);
keyboard_test_binary(state[3], 1, 4);
keyboard_test_binary(state[4], 1, 5);
locate_bin(state[5], 40, 10);
locate_bin(state[6], 40, 16);
locate_bin(state[7], 40, 22);
locate_bin(state[8], 40, 28);
locate_bin(state[9], 40, 34);
keyboard_test_binary(state[5], 10, 1);
keyboard_test_binary(state[6], 10, 2);
keyboard_test_binary(state[7], 10, 3);
keyboard_test_binary(state[8], 10, 4);
keyboard_test_binary(state[9], 10, 5);
dupdate();
}
@ -304,10 +281,10 @@ void gray_test(void)
}
locate("light", 15, 2);
locate_int(delays[0], 15, 3);
print(15, 3, "%d", delays[0]);
locate("dark", 15, 5);
locate_int(delays[1], 15, 6);
print(15, 6, "%d", delays[1]);
locate("\x02", 14, selected ? 6 : 3);
@ -328,7 +305,7 @@ void gray_test(void)
selected = !selected;
break;
case KEY_F2:
delays[0] = 993;
delays[0] = isSH3() ? 985 : 994;
delays[1] = 1609;
break;
case KEY_F3:
@ -356,6 +333,25 @@ void gray_test(void)
gray_stop();
}
/*
printf_test()
Tests formatting functions.
*/
void printf_test(void)
{
dclear();
locate("Formatted printing", 1, 1);
print(2, 3, "%%4.2d 5 :\"%4.2d\"", 5);
print(2, 4, "%%-3c '&':\"%-3c\"", '&');
print(2, 5, "%%#05x 27 :\"%#05x\"", 27);
print(2, 6, "%%1s \"tr\":\"%1s\"", "tr");
print(2, 7, "%%6p NULL :\"%7p\"", NULL);
dupdate();
while(getkey() != KEY_EXIT);
}
/*
static const unsigned char screen[1024] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@ -537,12 +533,8 @@ void tlb_debug(void)
{
dclear();
locate("TLB", 1, 1);
locate("way=", 8, 1);
locate("0\0001\0002\0003" + (way << 1), 12, 1);
locate_int(entry, 16 + (entry < 10), 1);
locate("-", 18, 1);
locate_int((entry + 2 > 31) ? (31) : (entry + 2) , 19, 1);
print(1, 1, "TLB way=%d %d-%d", way, entry,
entry > 29 ? 31 : entry + 2);
for(i = 0; i < 3 && entry < 32; i++, entry++)
{
@ -567,14 +559,11 @@ void tlb_debug(void)
}
r = 2 * i + 3;
locate_hexa(pointer_base, 8, 1, r);
locate(":", 11, r);
locate_hexa(ppn << 10, 8, 12, r);
print(1, r, "%08x :%08x", pointer_base, ppn << 10);
r++;
locate((d & 0x08) ? "4k" : "1k", 1, r);
locate("pr=", 5, r);
locate(protection[(d >> 5) & 3], 8, r);
print(5, r, "pr=%s", protection[(d >> 5) & 3]);
locate((d & 0x02) ? "shared" : "exclusive", 13, r);
}
@ -614,6 +603,7 @@ void main_menu(int *category, int *app)
"Image rendering",
"Text rendering",
"Real-time clock",
"Text formatting",
NULL
};
const char *list_perfs[] = {
@ -626,14 +616,15 @@ void main_menu(int *category, int *app)
NULL
};
const char **list;
int list_len;
extern unsigned int bgint, egint;
extern unsigned int romdata;
int gint_size = &egint - &bgint;
int gint_size = (char *)&egint - (char *)&bgint;
static int tab = 0, index = 0;
static int tab = 0, index = 0, scroll = 0;
// Set to 1 when interface has to be redrawn.
int leave = 1;
int i;
@ -654,14 +645,10 @@ void main_menu(int *category, int *app)
{
case 0:
locate("Demo application", 1, 1);
locate("gint version:", 2, 3);
locate(GINT_VERSION_STR, 16, 3);
locate("handler size:", 2, 4);
locate_int(gint_size, (gint_size < 1000 ? 18 : 17), 4);
locate("mpu type:", 2, 5);
locate(mpu, 21 - strlen(mpu), 5);
locate("romdata:", 2, 6);
locate_hex((unsigned int)&romdata, 11, 6);
print(2, 3, "gint version: %5s", GINT_VERSION_STR);
print(2, 4, "handler size: %5d", gint_size);
print(2, 5, "mpu type: %7s", mpu);
print(2, 6, "romdata: %08x", &romdata);
list = NULL;
break;
@ -682,16 +669,24 @@ void main_menu(int *category, int *app)
break;
default:
locate("Tab ", 1, 1);
locate_int(tab, 5, 1);
print(1, 1, "Tab %d", tab);
break;
}
dimage(&res_opt_menu_start, 0, 56);
if(list)
{
for(i = 0; list[i]; i++) locate(list[i], 2, i + 2);
dreverse_area(0, 8 * index + 9, 127, 8 * index + 16);
list_len = 0;
while(list[list_len]) list_len++;
for(i = scroll; list[i] && i < scroll + 6; i++)
locate(list[i], 2, i - scroll + 2);
if(scroll > 0) locate("\x0d", 20, 2);
if(scroll + 6 < list_len) locate("\x0e", 20, 7);
dreverse_area(0, 8 * (index - scroll) + 8, 127,
8 * (index - scroll) + 15);
}
dupdate();
@ -706,27 +701,58 @@ void main_menu(int *category, int *app)
switch(getkey())
{
case KEY_F1:
tab = 0;
index = 0;
tab = 0;
index = 0;
break;
case KEY_F2:
tab = 1;
index = 0;
tab = 1;
index = 0;
scroll = 0;
break;
case KEY_F3:
tab = 2;
index = 0;
tab = 2;
index = 0;
scroll = 0;
break;
case KEY_F4:
tab = 3;
index = 0;
tab = 3;
index = 0;
scroll = 0;
break;
case KEY_UP:
if(list && index) index--;
if(list && list_len > 1)
{
if(index)
{
index--;
if(index < scroll) scroll--;
}
else
{
index = list_len - 1;
scroll = list_len - 6;
if(scroll < 0) scroll = 0;
}
}
else leave = 0;
break;
case KEY_DOWN:
if(list && list[index + 1]) index++;
if(list && list_len > 1)
{
if(list[index + 1])
{
index++;
if(index >= scroll + 6)
scroll++;
}
else
{
index = 0;
scroll = 0;
}
}
else leave = 0;
break;
case KEY_EXE:
@ -735,7 +761,6 @@ void main_menu(int *category, int *app)
if(app) *app = index + 1;
return;
case KEY_EXIT:
case KEY_MENU:
if(category) *category = 0;
if(app) *app = 0;
@ -783,6 +808,9 @@ int main(void)
case 0x0105:
// rtc_test();
break;
case 0x0106:
printf_test();
break;
case 0x0301:
if(isSH3()) tlb_debug();

View File

@ -38,7 +38,8 @@ bitwise instructions, so that performance is maintained.
Consider for instance a logical and operation (`(a, b) -> a & b`).
Operating on pixels would need to move some data, test the value of a bit in
the mask, edit the vram data, and eventually shift both the data and the mask, for all of the 32 pixels.
the mask, edit the vram data, and eventually shift both the data and the mask,
for all of the 32 pixels.
One could not expect this from happening in less than 150 processor cycles
(in comparison, using generic-purpose `setPixel()`-like functions would be at
least 10 times as long). The smarter method operates directly on the longword
@ -76,10 +77,9 @@ operations that correspond to the kind of image.
*Detailed article: [Gray engine](gray-engine)*
Gray pixels are made of one four colors, each of which is represented by two
bits. Arguments `light` and `dark` of gray operation functions are longwords
containing the least significant and most significant of these bits,
respectively.
Gray pixels are made of four colors represented by pairs of bits. Arguments
`light` and `dark` of gray operation functions are longwords containing the
least significant and most significant of these bits, respectively.
white = 0 [00]
lightgray = 1 [01]
@ -168,7 +168,7 @@ darken(light, dark, x) = (light | (dark & x), (light & x) | (x ^ dark))
darken2(light, dark, x) = (light | x, (light & x) | dark)
~~~
One could easily check that these functions do their jobs when `x = 1` and
One could easily check that these functions do their jobs when `x = 1` and
leave the data unchanged when `x = 0`.
@ -183,7 +183,8 @@ Images are made of *layers*, each of which describe the mask for an operation.
When an image is rendered, *bopti* draws some of those layers in the vram
using the operation functions.
* Non-transparent monochrome images only have one layer, which describes the mask for the `draw` operation.
* Non-transparent monochrome images only have one layer, which describes the
mask for the `draw` operation.
* Transparent monochrome images have two layers. The first describes the mask
for the `draw` operation, while the other is the `alpha` operation mask (which
means that it indicates which pixels are transparent).
@ -199,7 +200,7 @@ transparent), and the third indicates the color.
Layers are encoded as a bit map. The image is split into a *grid*, which is
made of 32-pixel *columns*, and an *end*.
32 32 32 e
32 32 32 end
+------+------+------+---+
| | | | |
| | | | |

Binary file not shown.

View File

@ -5,7 +5,8 @@
Double-underscore prefixed structures (e.g. __st_rtc_counter) are used
internally but are not meant to be used in user programs.
Underscore-prefixed names (e.g. _R64CNT) indicate write-only registers.
Underscore-prefixed names (e.g. _R64CNT) are used to avoid name
conflicts (e.g. STRUCTURE.RTC would expand to STRUCTURE.((T *)0x...)).
*/
#pragma pack(push, 1)
@ -20,7 +21,6 @@
struct _st_rtc
{
unsigned char const R64CNT;
gap(1);
union {
@ -31,7 +31,6 @@ struct _st_rtc
unsigned ONES :4;
};
} RSECCNT;
gap(1);
union {
@ -42,7 +41,6 @@ struct _st_rtc
unsigned ONES :4;
};
} RMINCNT;
gap(1);
union {
@ -53,12 +51,10 @@ struct _st_rtc
unsigned ONES :4;
};
} RHRCNT;
gap(1);
// 0=Sunday, 1=Monday, ..., 6=Saturday, 7=Reserved (prohibited).
unsigned char RWKCNT;
gap(1);
union {
@ -69,7 +65,6 @@ struct _st_rtc
unsigned ONES :4;
};
} RDAYCNT;
gap(1);
union {
@ -80,7 +75,6 @@ struct _st_rtc
unsigned ONES :4;
};
} RMONCNT;
gap(1);
union {
@ -101,7 +95,6 @@ struct _st_rtc
unsigned ONES :4;
};
} RSECAR;
gap(1);
union {
@ -112,7 +105,6 @@ struct _st_rtc
unsigned ONES :4;
};
} RMINAR;
gap(1);
union {
@ -124,7 +116,6 @@ struct _st_rtc
unsigned ONES :4;
};
} RHRAR;
gap(1);
union {
@ -136,7 +127,6 @@ struct _st_rtc
unsigned DAY :3;
};
} RWKAR;
gap(1);
union {
@ -148,7 +138,6 @@ struct _st_rtc
unsigned ONES :4;
};
} RDAYAR;
gap(1);
union {
@ -160,7 +149,6 @@ struct _st_rtc
unsigned ONES :4;
};
} RMONAR;
gap(1);
union {
@ -174,7 +162,6 @@ struct _st_rtc
unsigned AF :1;
};
} RCR1;
gap(1);
union {
@ -188,7 +175,6 @@ struct _st_rtc
unsigned START :1;
};
} RCR2;
gap(1);
union {
@ -200,7 +186,6 @@ struct _st_rtc
unsigned ONES :4;
};
} RYRAR;
gap(2);
union {
@ -357,7 +342,6 @@ struct _st_intx
unsigned IrDA :4;
};
} IPRA;
gap(2);
union {
@ -369,7 +353,6 @@ struct _st_intx
unsigned BEU2_1 :4;
};
} IPRB;
gap(2);
union {
@ -381,7 +364,6 @@ struct _st_intx
unsigned SPU :4;
};
} IPRC;
gap(2);
union {
@ -393,7 +375,6 @@ struct _st_intx
unsigned ATAPI :4;
};
} IPRD;
gap(2);
union {
@ -405,19 +386,17 @@ struct _st_intx
unsigned VPU5F :4;
};
} IPRE;
gap(2);
union {
unsigned short WORD;
struct {
unsigned KEYSC :4;
unsigned _KEYSC :4;
unsigned DMAC0B :4;
unsigned USB01 :4;
unsigned CMT :4;
};
} IPRF;
gap(2);
union {
@ -429,7 +408,6 @@ struct _st_intx
unsigned VEU3F0 :4;
};
} IPRG;
gap(2);
union {
@ -441,7 +419,6 @@ struct _st_intx
unsigned I2C0 :4;
};
} IPRH;
gap(2);
union {
@ -453,7 +430,6 @@ struct _st_intx
unsigned _2DG_ICB :4;
};
} IPRI;
gap(2);
union {
@ -465,7 +441,6 @@ struct _st_intx
unsigned SDHI1 :4;
};
} IPRJ;
gap(2);
union {
@ -477,7 +452,6 @@ struct _st_intx
unsigned SDHI0 :4;
};
} IPRK;
gap(2);
union {
@ -489,7 +463,6 @@ struct _st_intx
unsigned _2DDMAC :4;
};
} IPRL;
char gap1[82];
union
@ -506,7 +479,6 @@ struct _st_intx
unsigned SDHII0 :1;
};
} IMR0;
gap(3);
union
@ -523,7 +495,6 @@ struct _st_intx
unsigned DEI0 :1;
};
} IMR1;
gap(3);
union
@ -538,7 +509,6 @@ struct _st_intx
unsigned SCIFA0 :1;
};
} IMR2;
gap(3);
union
@ -553,7 +523,6 @@ struct _st_intx
unsigned IRDAI :1;
};
} IMR3;
gap(3);
union
@ -569,7 +538,6 @@ struct _st_intx
unsigned LCDCI :1;
};
} IMR4;
gap(3);
union
@ -586,7 +554,6 @@ struct _st_intx
unsigned SCIF0 :1;
};
} IMR5;
gap(3);
union
@ -602,7 +569,6 @@ struct _st_intx
unsigned MSIOFI1 :1;
};
} IMR6;
gap(3);
union
@ -619,7 +585,6 @@ struct _st_intx
unsigned AL1I :1;
};
} IMR7;
gap(3);
union
@ -635,7 +600,6 @@ struct _st_intx
unsigned FSI :1;
};
} IMR8;
gap(3);
union
@ -650,7 +614,6 @@ struct _st_intx
unsigned :1;
};
} IMR9;
gap(3);
union
@ -667,7 +630,6 @@ struct _st_intx
unsigned CUI :1;
};
} IMR10;
gap(3);
union
@ -684,7 +646,6 @@ struct _st_intx
unsigned TSIFI :1;
};
} IMR11;
gap(3);
union
@ -695,7 +656,6 @@ struct _st_intx
unsigned _2DDMAC :1;
};
} IMR12;
char gap2[15];
union
@ -712,7 +672,6 @@ struct _st_intx
unsigned SDHII0 :1;
};
} _IMCR0;
gap(3);
union
@ -729,7 +688,6 @@ struct _st_intx
unsigned DEI0 :1;
};
} _IMCR1;
gap(3);
union
@ -744,7 +702,6 @@ struct _st_intx
unsigned SCIFA0 :1;
};
} _IMCR2;
gap(3);
union
@ -759,7 +716,6 @@ struct _st_intx
unsigned IRDAI :1;
};
} _IMCR3;
gap(3);
union
@ -775,7 +731,6 @@ struct _st_intx
unsigned LCDCI :1;
};
} _IMCR4;
gap(3);
union
@ -792,7 +747,6 @@ struct _st_intx
unsigned SCIF0 :1;
};
} _IMCR5;
gap(3);
union
@ -808,7 +762,6 @@ struct _st_intx
unsigned MSIOFI1 :1;
};
} _IMCR6;
gap(3);
union
@ -825,7 +778,6 @@ struct _st_intx
unsigned AL1I :1;
};
} _IMCR7;
gap(3);
union
@ -841,7 +793,6 @@ struct _st_intx
unsigned FSI :1;
};
} _IMCR8;
gap(3);
union
@ -856,7 +807,6 @@ struct _st_intx
unsigned :1;
};
} _IMCR9;
gap(3);
union
@ -873,7 +823,6 @@ struct _st_intx
unsigned CUI :1;
};
} _IMCR10;
gap(3);
union
@ -890,7 +839,6 @@ struct _st_intx
unsigned TSIFI :1;
};
} _IMCR11;
gap(3);
union

View File

@ -1,6 +1,6 @@
//---
//
// gint core module: alloca
// standard library module: alloca
//
// Allows dynamic memory allocation on the stack. Memory is automatically
// freed when the calling function exits.

View File

@ -1,3 +1,11 @@
//---
//
// standard library module: ctype
//
// Some character manipulation.
//
//---
#ifndef _CTYPE_H
#define _CTYPE_H 1

View File

@ -14,8 +14,7 @@
// Heading declarations.
//---
#include <stdint.h>
#include <tales.h>
#include <internals/tales.h>
enum Color
{
@ -149,47 +148,4 @@ void dline(int x1, int y1, int x2, int y2, enum Color color);
*/
void dimage(struct Image *image, int x, int y);
//---
// Rectangle masks.
//
// The concept of 'rectangle masks' is used several times in this module.
// It is based on the fact that an operation that affects a rectangle acts
// the same on all its lines. Therefore the behavior of the operation is
// determined by its behavior on a single line, which is represented using
// 'masks' whose bits indicate whether a pixel is affected (1) or not (0).
//
// For example when clearing the screen rectangle (16, 16, 112, 48), the
// masks will represent information '16 to 112 on x-axis', and will hold
// the following values : 0000ffff, ffffffff, ffffffff and ffff0000. These
// masks can then be used by setting vram[offset] &= ~masks[i]. This
// appears to be very flexible : for instance, vram[offset] ^= masks[i]
// will reverse the pixels in the same rectangle.
//
// This technique can also be used in more subtle cases with more complex
// patterns, but within this module it is unlikely to happen.
//
//---
/*
adjustRectangle()
Adjusts the given rectangle coordinates to ensure that :
- the rectangle is entirely contained in the screen
- x1 < x2
- y1 < y2
which is needed when working with screen rectangles.
*/
void adjustRectangle(int *x1, int *y1, int *x2, int *y2);
/*
getMasks()
Computes the rectangle masks needed to affect pixels located between x1
and x2 (both included). The four masks are stored in the third argument
(seen as an array).
*/
void getMasks(int x1, int x2, uint32_t *masks);
#endif // _DISPLAY_H

View File

@ -32,6 +32,45 @@ unsigned int gint_systemVBR(void);
//---
// Register access.
//---
/*
enum Register
Represents common registers. Used as identifiers to retrieve their
values using gint_register().
*/
enum Register
{
Register_EXPEVT,
Register_MMUCR,
Register_TEA,
};
/*
gint_register()
Returns the address of a common register. All common registers exist
on both platforms but they may hold different values for the same
information (f.i. EXPEVT may not return the same value for a given
exception on both 7705 and 7305).
*/
volatile void *gint_reg(enum Register reg);
/*
gint_strerror()
Returns a string that describe the error set in EXPEVT in case of
general exception of TLB miss exception. This string is platform-
independent.
Some exception codes represent different errors when invoked inside the
general exception handler and the TLB error handler. Parameter 'is_tlb'
should be set to zero for general exception meanings, and anything non-
zero for TLB error meanings.
*/
const char *gint_strerror(int is_tlb);
//---
// Internal API.
// Referenced here for documentation purposes only.
@ -75,6 +114,15 @@ void gint_setup_7305(void);
void gint_stop_7705(void);
void gint_stop_7305(void);
/*
gint_reg()
gint_strerror()
See "Register access" section.
*/
volatile void *gint_reg_7705(enum Register reg);
volatile void *gint_reg_7305(enum Register reg);
const char *gint_strerror_7705(int is_tlb);
const char *gint_strerror_7305(int is_tlb);
//---
@ -110,49 +158,6 @@ void gint_int_7305(void) __attribute__((section(".gint.int")));
//---
// Register access.
//---
/*
enum Register
Represents common registers. Used as identifiers to retrieve their
values using gint_register().
*/
enum Register
{
Register_EXPEVT,
Register_MMUCR,
Register_TEA,
};
/*
gint_register()
Returns the address of a common register. All common registers exist
on both platforms but they may hold different values for the same
information (f.i. EXPEVT may not return the same value for a given
exception on both 7705 and 7305).
*/
volatile void *gint_reg(enum Register reg);
volatile void *gint_reg_7705(enum Register reg);
volatile void *gint_reg_7305(enum Register reg);
/*
gint_strerror()
Returns a string that describe the error set in EXPEVT in case of
general exception of TLB miss exception. This string is platform-
independent.
Some exception codes represent different errors when invoked inside the
general exception handler and the TLB error handler. Parameter 'is_tlb'
should be set to zero for general exception meanings, and anything non-
zero for TLB error meanings.
*/
const char *gint_strerror(int is_tlb);
const char *gint_strerror_7705(int is_tlb);
const char *gint_strerror_7305(int is_tlb);
//---
// Internal platform-independent definitions.
//---

View File

@ -86,7 +86,7 @@ struct Command
// The video ram addresses are set by the public functions and used internally
// by the module.
// Monochrome video ram, light and dark buffers (in this order).
extern int *vram, *v1, *v2;
extern int *bopti_vram, *bopti_v1, *bopti_v2;

View File

@ -9,6 +9,47 @@
#ifndef _INTERNALS_DISPLAY_H
#define _INTERNALS_DISPLAY_H 1
#include <stdint.h>
extern int *vram;
//---
// Rectangle masks.
//
// The concept of 'rectangle masks' is used several times in this module.
// It is based on the fact that an operation that affects a rectangle acts
// the same on all its lines. Therefore the behavior of the operation is
// determined by its behavior on a single line, which is represented using
// 'masks' whose bits indicate whether a pixel is affected (1) or not (0).
//
// For example when clearing the screen rectangle (16, 16, 112, 48), the
// masks will represent information '16 to 112 on x-axis', and will hold
// the following values : 0000ffff, ffffffff, ffffffff and ffff0000. These
// masks can then be used by setting vram[offset] &= ~masks[i]. This
// appears to be very flexible : for instance, vram[offset] ^= masks[i]
// will reverse the pixels in the same rectangle.
//
// This technique can also be used in more subtle cases with more complex
// patterns, but within this module it is unlikely to happen.
//
//---
/*
adjustRectangle()
Adjusts the given rectangle coordinates to ensure that :
- the rectangle is entirely contained in the screen
- x1 < x2
- y1 < y2
which is needed when working with screen rectangles.
*/
void adjustRectangle(int *x1, int *y1, int *x2, int *y2);
/*
getMasks()
Computes the rectangle masks needed to affect pixels located between x1
and x2 (both included). The four masks are stored in the third argument
(seen as an array).
*/
void getMasks(int x1, int x2, uint32_t *masks);
#endif // _INTERNALS_DISPLAY_H

32
include/internals/stdio.h Normal file
View File

@ -0,0 +1,32 @@
//---
//
// standard library module: stdio
//
// Handles most input/output for the program. This module does not
// interact with the file system directly.
//
//---
#ifndef _INTERNALS_STDIO_H
#define _INTERNALS_STDIO_H 1
#include <stddef.h>
#include <stdarg.h>
//---
// Formatted printing.
//---
#ifndef __stdio_buffer_size
#define __stdio_buffer_size 256
#endif
extern char __stdio_buffer[];
/*
__printf()
Formatted printing to the stdio buffer.
*/
int __printf(size_t size, const char *format, va_list args);
#endif // _INTERNALS_STDIO_H

View File

@ -1,3 +1,12 @@
//---
//
// gint standard module: setjmp
//
// Long jumps. The register contents are saved in a buffer when setjmp()
// is called and restored at any time when longjmp() performs the jump.
//
//---
#ifndef _SETJMP_H
#define _SETJMP_H 1
@ -12,13 +21,13 @@ typedef unsigned int jmp_buf[16];
//---
/*
setjmp()
setjmp() O(1)
Configures a jump by saving data to the given jump buffer.
*/
int setjmp(jmp_buf env);
/*
longjmp()
longjmp() O(1)
Performs a long jump.
*/
void longjmp(jmp_buf env, int value);

44
include/stdio.h Normal file
View File

@ -0,0 +1,44 @@
//---
//
// standard library module: stdio
//
// Handles most input/output for the program. This module does not
// interact with the file system directly.
//
//---
#ifndef _STDIO_H
#define _STDIO_H 1
#include <stddef.h>
#include <stdarg.h>
//---
// Formatted printing.
//---
/*
sprintf()
Prints to a string.
*/
int sprintf(char *str, const char *format, ...);
/*
snprintf()
Prints to a string with a size limit.
*/
int snprintf(char *str, size_t size, const char *format, ...);
/*
vsprintf()
Prints to a string from an argument list.
*/
int vsprintf(char *str, const char *format, va_list args);
/*
vsnprintf()
The most generic formatted printing function around there.
*/
int vsnprintf(char *str, size_t size, const char *format, va_list args);
#endif // _STDIO_H

View File

@ -1,3 +1,12 @@
//---
//
// standard library module: stdlib
//
// Provides standard functionalities such as dynamic allocation,
// string/numeric conversion, and abort calls.
//
//---
#ifndef _STDLIB_H
#define _STDLIB_H 1

View File

@ -1,3 +1,12 @@
//---
//
// standard library module: string
//
// String manipulation using NUL-terminated byte arrays, without extended
// characters.
//
//---
#ifndef _STRING_H
#define _STRING_H 1
@ -28,9 +37,27 @@ void *memset(void *destination, int byte, size_t byte_number);
//---
/*
strlen() O(length)
strlen() O(len(str))
Returns the length of a string.
*/
size_t strlen(const char *str);
/*
strcpy() O(len(source))
Copies a string to another.
*/
char *strcpy(char *destination, const char *source);
/*
strchr() O(len(str))
Searches a character in a string.
*/
const char *strchr(const char *str, int value);
/*
strncpy() O(min(len(source), size))
Copies part of a string to another.
*/
char *strncpy(char *destination, const char *source, size_t size);
#endif // _STRING_H

View File

@ -2,8 +2,8 @@
//
// gint drawing module: tales
//
// Text displaying. Does some good optimization, though requires dynamic
// allocation.
// Text displaying. Does some pretty good optimization, though requires
// dynamic allocation. The stack is used.
//
//---
@ -113,4 +113,16 @@ void dtext(const char *str, int x, int y);
*/
void gtext(const char *str, int x, int y);
/*
dprint()
Prints a formatted string. Works the same as printf().
*/
void dprint(int x, int y, const char *format, ...);
/*
gprint()
Prints a formatted string. Works the same as printf().
*/
void gprint(int x, int y, const char *format, ...);
#endif // _TALES_H

BIN
libc.a

Binary file not shown.

BIN
libgint.a

Binary file not shown.

View File

@ -1,7 +1,7 @@
#include <internals/bopti.h>
// Monochrome video ram, light and dark buffers (in this order).
int *vram, *v1, *v2;
int *bopti_vram, *bopti_v1, *bopti_v2;
/*
bopti_op()
@ -19,11 +19,11 @@ void bopti_op_mono(int offset, uint32_t operator, struct Command *c)
switch(c->channel)
{
case Channel_Mono:
vram[offset] |= operator;
bopti_vram[offset] |= operator;
break;
case Channel_FullAlpha:
vram[offset] &= ~operator;
bopti_vram[offset] &= ~operator;
break;
default:
@ -37,21 +37,21 @@ void bopti_op_gray(int offset, uint32_t operator, struct Command *c)
switch(c->channel)
{
case Channel_Mono:
v1[offset] |= operator;
v2[offset] |= operator;
bopti_v1[offset] |= operator;
bopti_v2[offset] |= operator;
break;
case Channel_Light:
v1[offset] |= operator;
bopti_v1[offset] |= operator;
break;
case Channel_Dark:
v2[offset] |= operator;
bopti_v2[offset] |= operator;
break;
case Channel_FullAlpha:
v1[offset] &= ~operator;
v2[offset] &= ~operator;
bopti_v1[offset] &= ~operator;
bopti_v2[offset] &= ~operator;
break;
case Channel_LightAlpha:

View File

@ -1,5 +1,5 @@
#include <internals/bopti.h>
#include <display.h>
#include <internals/display.h>
/*
dimage()
@ -34,7 +34,7 @@ void dimage(struct Image *img, int x, int y)
if(x >= 0) getMasks(x, x + actual_width - 1, command.masks);
else getMasks(0, actual_width + x - 1, command.masks);
vram = display_getCurrentVRAM();
bopti_vram = display_getCurrentVRAM();
while(format)
{

View File

@ -1,5 +1,5 @@
#include <internals/bopti.h>
#include <display.h>
#include <internals/display.h>
#include <gray.h>
/*
@ -34,8 +34,8 @@ void gimage(struct Image *img, int x, int y)
if(x >= 0) getMasks(x, x + actual_width - 1, command.masks);
else getMasks(0, actual_width + x - 1, command.masks);
v1 = gray_lightVRAM();
v2 = gray_darkVRAM();
bopti_v1 = gray_lightVRAM();
bopti_v2 = gray_darkVRAM();
while(format)
{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -9,6 +9,7 @@
#include <gray.h>
#include <screen.h>
#include <timer.h>
#include <mpu.h>
static int internal_vrams[3][256];
static const void *vrams[4];
@ -151,6 +152,6 @@ void gray_init(void)
vrams[2] = (const void *)internal_vrams[1];
vrams[3] = (const void *)internal_vrams[2];
delays[0] = 993;
delays[0] = isSH3() ? 985 : 994;
delays[1] = 1609;
}

View File

@ -14,7 +14,7 @@ static void (*rtc_callback)(void) = NULL;
Sets the callback function for the real-time clock interrupt. If
frequency is non-NULL, the clock frequency is set to the given value.
*/
void gint_setRTCCallback(void (*callback)(void), enum RTCFrequency frequency)
void rtc_setCallback(void (*callback)(void), enum RTCFrequency frequency)
{
rtc_callback = callback;
if(frequency < 1 || frequency > 7) return;
@ -30,7 +30,7 @@ void gint_setRTCCallback(void (*callback)(void), enum RTCFrequency frequency)
Returns the callback function. If frequency is non-NULL, it is set to
the current frequency value.
*/
void (*gint_getRTCCallback(enum RTCFrequency *frequency))(void)
void (*rtc_getCallback(enum RTCFrequency *frequency))(void)
{
if(!frequency) return rtc_callback;

View File

@ -1,5 +1,5 @@
/*
gint standard module: setjmp
standard library module: setjmp
Long jumps. The register contents are saved in a buffer when setjmp()
is called and restored at any time when longjmp() performs the jump.

19
src/stdio/snprintf.c Normal file
View File

@ -0,0 +1,19 @@
#include <internals/stdio.h>
#include <stdio.h>
#include <string.h>
/*
snprintf()
Prints to a string with a size limit.
*/
int snprintf(char *str, size_t size, const char *format, ...)
{
va_list args;
va_start(args, format);
int x = __printf(size, format, args);
strncpy(str, __stdio_buffer, size);
va_end(args);
return x;
}

19
src/stdio/sprintf.c Normal file
View File

@ -0,0 +1,19 @@
#include <internals/stdio.h>
#include <stdio.h>
#include <string.h>
/*
sprintf()
Prints to a string.
*/
int sprintf(char *str, const char *format, ...)
{
va_list args;
va_start(args, format);
int x = __printf(0, format, args);
strncpy(str, __stdio_buffer, __stdio_buffer_size);
va_end(args);
return x;
}

912
src/stdio/stdio_format.c Normal file
View File

@ -0,0 +1,912 @@
#include <internals/stdio.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
/*
Internal buffer.
Using a buffer *really* simplifies everything. But it also has
disadvantages, such a memory loss and limited output size.
So, in case we find a possibility to get rid of this buffer, we will
just have to change function character(), which is for now the only
function that directly accesses the buffer.
*/
char __stdio_buffer[__stdio_buffer_size];
/*
Format composed types.
Format structure handles everything in a format, from data type to
given value, including alternative forms, alignment and digit numbers.
A format is made of a data type, which can be altered by a size option,
a number of integer and decimal digits, and additional flags.
The FormatFlags enumeration handles the various flags that can be added
to a printf()-family format.
*/
enum FormatFlags
{
// Option '#' specifies alternatives forms, mainly '0' and '0x'
// prefixes in integer display.
Alternative = 1,
// Under specific conditions, zero-padding may be used instead of
// whitespace-padding.
ZeroPadded = 2,
// Left alignment specifies that additional spaces should be added
// after the value.
LeftAlign = 4,
// In numeric display, this forces a blank sign to be written before
// positive values.
BlankSign = 8,
// In numeric display, this forces an explicit sign in all cases. This
// option overrides BlankSign (see __printf() description for further
// information on option precedence and influence).
ForceSign = 16
};
struct Format
{
// Format type, one of 'diouxXcs' ('eEfFgGaApnm' still to add).
char type;
// Format size, one of 'l', 'h', 'i' ('i' means 'hh').
char size;
// Number of characters printed.
int characters;
// Number of digits after the dot.
int precision;
// Various flags.
enum FormatFlags flags;
// Value to output.
union
{
// Signed int with formats %c, %d and %i.
signed int _int;
// Insigned int with formats %o, %u, %x and %X.
unsigned int _unsigned;
// Double with formats %f, %F, %e, %E, %g and %G.
// double _double;
// String pointer with format %s.
const char *_pointer;
};
};
//---
// Static declarations.
//---
// Outputs a character in the buffer. Updates counters.
static void character(int c);
// Outputs n timers the given character.
static void character_n(int c, int n);
// Reads a format from the given string pointer address (must begin with '%').
static struct Format get_format(const char **pointer);
// Computes the number of spaces and zeros to add to the bare output.
static void get_spacing(struct Format format, int *begin_spaces, int *sign,
int *zeros, int length, int *end_spaces);
static void format_di (struct Format format);
static void format_u (struct Format format);
static void format_oxX (struct Format format);
// static void format_e (struct Format format);
static void format_c (struct Format format);
static void format_s (struct Format format);
static void format_p (struct Format format);
#define abs(x) ((x) < 0 ? -(x) : (x))
// Number of characters currently written.
static size_t written = 0;
// Total number of function calls (characters theoretically written).
static size_t total = 0;
// Maximum number of characters to output.
static size_t max = 0;
/*
character()
Outputs a character to the buffer. This function centralizes all the
buffer interface, so that if we come to remove it for property reasons,
we would just have to edit this function.
Static variables written and total are both needed, because the
terminating NUL character may be written after the maximum has been
reached.
In other words, when the function ends, we need to have a variable
counting the current position in the buffer (written), and one other
containing the total number of theoretic characters (total) because
these two values may be different.
Of course the three variables need to be initialized before using this
function.
*/
static void character(int c)
{
// If there is space left in the buffer.
if(written < max - 1) __stdio_buffer[written++] = c;
total++;
}
/*
character_n()
Outputs n times the same character. Thought to be used to output spaces
or zeros without heavy loops.
*/
static void character_n(int c, int n)
{
int i = 0;
while(i++ < n) character(c);
}
/*
get_format()
Reads the format from the given string pointer and returns a
corresponding Format structure. The string pointer points to is also
modified, so that is points to the first character after the format.
This function expects **pointer == '%'.
*/
static struct Format get_format(const char **pointer)
{
const char *convspec = "diouxXeEfFgGaAcspnm";
struct Format format;
const char *string = *pointer, *ptr;
int c, i;
char precision[10];
// Moving the string pointer after the '%' character.
string++;
// Initializing structure.
format.type = 0;
format.size = 0;
format.flags = 0;
// Initializing digit counts.
format.characters = -1;
format.precision = -1;
// Parsing the format string. Testing each character until a
// conversion specifier is found.
while((c = *string))
{
// Looking for a conversion specifier.
ptr = strchr(convspec, c);
if(ptr)
{
format.type = *ptr;
break;
}
// Looking for a left precision string (number of digits before
// the dot), introduced by a non-null digit.
if(c >= '1' && c <= '9')
{
format.characters = 0;
for(i = 0; i < 9 && isdigit(*string); string++)
{
format.characters *= 10;
format.characters += *string - '0';
}
// As pointer is now pointing to the next character,
// we want to try tests again from the beginning.
continue;
}
// Looking for a right precision string (number of digits after
// the dot), introduced by a point.
if(c == '.')
{
string++;
if(!isdigit(*string)) continue;
format.precision = 0;
for(i = 0; i < 9 && isdigit(*string); string++)
{
format.precision *= 10;
format.precision += *string - '0';
}
// As pointer is now pointing on the next character,
// we want to try tests again from the beginning.
continue;
}
// Handling predefined characters.
switch(*string)
{
// Length modifiers.
case 'h':
format.size = 'h' + (format.size == 'h');
break;
case 'l':
case 'L':
case 'z':
case 't':
format.size = *string;
break;
// Flags.
case '#':
format.flags |= Alternative;
break;
case '0':
format.flags |= ZeroPadded;
break;
case '-':
format.flags |= LeftAlign;
break;
case ' ':
format.flags |= BlankSign;
break;
case '+':
format.flags |= ForceSign;
break;
}
string++;
}
// If the format hasn't ended, the type attribute is left to zero and
// the main loop will handle failure and break. Nothing has to be done
// here.
*pointer = string + 1;
return format;
}
/*
get_spacing()
Computes the arrangement of beginning spaces, sign, zeros, pure value
and ending spaces in formats.
This formatting follows a recurrent model which is centralized in this
function. Note that you can't have `begin_spaces` and `end_spaces`
both non-zero: at least one is null.
*/
static void get_spacing(struct Format format, int *begin_spaces, int *sign,
int *zeros, int length, int *end_spaces)
{
// Using a list of types involving a sign.
const char *signed_types = "dieEfFgGaA";
int spaces;
// Digits represents pure output + zeros (don't mix up with the *real*
// displayed digits).
int digits;
int left = format.flags & LeftAlign;
// Getting the total number of digits.
switch(format.type)
{
// In integer display, the number of digits output is specified in the
// precision.
case 'd':
case 'i':
case 'u':
digits = format.precision;
if(digits < length) digits = length;
break;
// Binary display has prefixes such as '0' and '0x'.
case 'o':
case 'x':
case 'X':
digits = format.precision;
if(digits == -1) digits = length;
if(format.flags & Alternative)
{
int hexa = (format.type == 'x' || format.type == 'X');
digits += 1 + hexa;
length += 1 + hexa;
}
if(digits < length) digits = length;
break;
// Other formats do not have additional zeros.
default:
digits = length;
break;
}
if(sign)
{
if(strchr(signed_types, format.type))
{
if(format.flags & BlankSign) *sign = ' ';
// Option '+' overrides option ' '.
if(format.flags & ForceSign) *sign = '+';
// And of course negative sign overrides everything!
if(format.type == 'd' || format.type == 'i')
{
if(format._int < 0) *sign = '-';
}
// else if(format._double < 0) *sign = '-';
}
else *sign = 0;
}
// Computing the number of spaces.
spaces = format.characters - digits;
// Computing the number of zeros.
*zeros = digits - length;
// Removing a space when a sign appears.
if(sign && *sign) spaces--;
// Option '0' translates spaces to zeros, but only if no precision is
// specified; additionally, left alignment overrides zero-padding.
if(!left && format.precision == -1 && format.flags & ZeroPadded)
{
*zeros += spaces;
spaces = 0;
}
// Setting the correct spaces number to the computed value, depending
// on the left alignment parameter.
*begin_spaces = left ? 0 : spaces;
*end_spaces = left ? spaces : 0;
}
/*
__printf()
Basic buffered formatted printing function. Fully-featured, so that
ant call to a printf()-family function can be translated into a
__printf() call.
It always returns the number of characters of the theoretic formatted
output. The real output may be limited in size by the given size
argument when working with nprintf()-family functions, or the internal
buffer itself.
The Flags structure isn't necessary, but it simplifies a lot format
argument handling (because flag effects depend on format type, which
is unknown when the flags are read). Also, length modifiers 'hh' is
stored as 'i' to simplify structure handling. 'll' is not supported.
Format '%LF' is allowed by C99 and therefore supported.
Generic information on options precedence and influence.
- Influences of integer part and mantissa digit numbers depend on
the type of data that is being displayed.
- Option '#' doesn't contend with any other.
- Option '+' overrides options ' '.
- In integer display, option '0' translates spaces to zeros, but
only if no decimal digit number is specified.
The option '-' also overrides it, forcing whitespaces to be
written at the end of the format.
Limit of function.
- Internal buffer size (should be customizable with a -D option
when compiling).
- Precision values (format %a.b) are written on 8 bits, therefore
limited to 127.
Unsupported features.
- Flag character ''' (single quote) for thousands grouping
- Flag character 'I', that outputs locale's digits (glibc 2.2)
- Length modifiers 'll' and 'q' (libc 5 and 4.4 BSD)
- This is not really a feature but incorrect characters in
formats are ignored and don't invalidate the format.
*/
int __printf(size_t size, const char *string, va_list args)
{
struct Format format;
// Avoiding overflow by adjusting the size argument.
if(!size || size > __stdio_buffer_size)
size = __stdio_buffer_size;
// Initializing character() working values.
written = 0;
total = 0;
max = size;
// Parsing the format string. At each iteration, a literal character, a
// '%%' identifier or a format is parsed.
while(*string)
{
// Literal text.
if(*string != '%')
{
character(*string++);
continue;
}
// Literal '%'.
if(string[1] == '%')
{
string += 2;
character('%');
continue;
}
// Getting the format.
format = get_format(&string);
if(!format.type) break;
/*
// Displaying an information message.
printf(
"Format found :%s%c%c, options %d, and %d.%d "
"digits\n",
format.size ? " " : "",
format.size ? format.size : ' ',
format.type,
format.flags,
format.digits,
format.mantissa
);
*/
switch(format.type)
{
// Signed integers.
case 'd':
case 'i':
format._int = va_arg(args, signed int);
// Reducing value depending on format size.
switch(format.size)
{
case 'h':
format._int &= 0x0000ffff;
break;
case 'i':
format._int &= 0x000000ff;
break;
}
format_di(format);
break;
// Unsigned integers.
case 'u':
format._unsigned = va_arg(args, unsigned int);
format_u(format);
break;
case 'o':
case 'x':
case 'X':
format._unsigned = va_arg(args, unsigned int);
format_oxX(format);
break;
/* // Exponent notation.
case 'e':
case 'E':
format._double = va_arg(args, double);
format_e(format);
break;
*/
// Characters.
case 'c':
format._int = va_arg(args, signed int) & 0xff;
format_c(format);
break;
// Strings.
case 's':
format._pointer = va_arg(args, const char *);
format_s(format);
break;
// Pointers.
case 'p':
format._unsigned = va_arg(args, unsigned int);
format_p(format);
break;
// Character counter.
case 'n':
*va_arg(args, int *) = written;
break;
}
}
// Adding a terminating NUL character. Function character() should have
// left an empty byte for that.
__stdio_buffer[written] = 0;
return total;
}
/*
format_di()
Subroutine itoa(). Writes the given signed integer to the internal
buffer, trough function character().
It is used by conversion specifiers 'd' and 'i'.
Options '#' and '0' have no effect.
*/
static void format_di(struct Format format)
{
// In integer display, character number includes pure digits and
// additional zeros and spacing.
// The precision represents the number of digits (pure digits and
// zeros) to print.
// For example: ' 0004', pure digits: 1, digits: 4, characters: 5.
int sign = 0;
signed int x = format._int;
// Using integers to store the number pure digits and additional spaces
// and zeros.
int bspaces, zeros, digits = 0, espaces;
// Using a multiplier to output digit in the correct order.
int multiplier = 1;
// Returning if the argument is null with an explicit precision of
// zero, but only if there are no spaces.
if(!x && format.characters == -1 && !format.precision) return;
//---
// Computations.
//---
// Computing the number of digits and the multiplier.
x = abs(format._int);
if(!x) digits = 1;
else while(x)
{
digits++;
x /= 10;
if(x) multiplier *= 10;
}
// Getting the corresponding spacing.
get_spacing(format, &bspaces, &sign, &zeros, digits, &espaces);
//---
// Output.
//---
character_n(' ', bspaces);
if(sign) character(sign);
character_n('0', zeros);
x = abs(format._int);
// Writing the pure digits, except if the value is null with an
// explicit precision of zero.
if(x || format.precision) while(multiplier)
{
character((x / multiplier) % 10 + '0');
multiplier /= 10;
}
character_n(' ', espaces);
}
/*
format_u()
Unsigned integers in base 10. Options ' ', '+' and '#' have no effect.
*/
static void format_u(struct Format format)
{
int bspaces, zeros, digits = 0, espaces;
int x = format._unsigned;
int multiplier = 1;
int c;
// Computing number of digits.
if(!x) digits = 1;
else while(x)
{
digits++;
x /= 10;
if(x) multiplier *= 10;
}
get_spacing(format, &bspaces, NULL, &zeros, digits, &espaces);
//---
// Output.
//---
character_n(' ', bspaces);
character_n('0', zeros);
x = format._unsigned;
while(multiplier)
{
character('0' + (x / multiplier) % 10);
multiplier /= 10;
}
character_n(' ', espaces);
}
/*
format_oxX()
Unsigned integers in base 8 or 16.
Since the argument is unsigned, options ' ' and '+' have no effect.
Option '#' adds prefix '0' in octal or '0x' in hexadecimal.
*/
static void format_oxX(struct Format format)
{
// In unsigned display, the digit number specifies the minimal number
// of characters that should be output. If the prefix (alternative
// form) is present, it is part of this count.
// Integer part and decimal part digit numbers behave the same way as
// in signed integer display.
// Using integers to store the number of digits, zeros and spaces.
int bspaces, zeros, digits = 0, espaces;
unsigned int x = format._unsigned;
int multiplier = 0, shift, mask;
int c, disp;
//---
// Computations.
//---
shift = (format.type == 'o') ? (3) : (4);
mask = (1 << shift) - 1;
disp = (format.type == 'x') ? (39) : (7);
// Computing number of digits.
if(!x) digits = 1;
else while(x)
{
digits++;
x >>= shift;
if(x) multiplier += shift;
}
// Getting the spacing distribution.
get_spacing(format, &bspaces, NULL, &zeros, digits, &espaces);
//---
// Output.
//---
character_n(' ', bspaces);
x = format._unsigned;
// Writing the alternative form prefix.
if(format.flags & Alternative && x)
{
character('0');
if(format.type != 'o') character(format.type);
}
character_n('0', zeros);
// Extracting the digits.
while(multiplier >= 0)
{
c = (x >> multiplier) & mask;
c += '0' + (c > 9) * disp;
character(c);
multiplier -= shift;
}
character_n(' ', espaces);
}
/*
format_e()
Exponent notation. Option '#' has no effect.
static void format_e(struct Format format)
{
// In exponent display, the precision is the number of digits after the
// dot.
// Using an integer to store the number exponent.
int exponent = 0;
// Using a double value for temporary computations, and another to
// store the format parameter.
double tmp = 1, x = format._double;
// Using spacing variables. Default length is for '0.e+00';
int bspaces, zeros, sign, length = 6, espaces;
// Using an iterator and a multiplier.
int i, mul;
//---
// Computations.
//---
// Computing the exponent. For positive exponents, increasing until
// the temporary value gets greater than x.
if(x > 1)
{
// Looping until we haven't reached a greater exponent.
while(tmp < x)
{
// Incrementing the exponent.
exponent++;
// Multiplying the test value.
tmp *= 10;
}
// Removing an additional incrementation.
exponent--;
}
// For negative exponents, decreasing until it's lower.
else while(tmp > x)
{
// Decrementing the exponent.
exponent--;
// Dividing the test value.
tmp *= 0.1;
}
// Adding a character if the exponent is greater that 100.
if(exponent >= 100) length++;
// Adding another one if it's greater than 1000.
if(exponent >= 1000) length++;
// Adjusting the format precision, defaulting to 6.
if(format.precision == -1) format.precision = 6;
// Adding the decimal digits.
length += format.precision;
// Getting the space repartition.
get_spacing(format, &bspaces, &sign, &zeros, length, &espaces);
//---
// Output.
//---
// Writing the beginning whitespaces.
character_n(' ', bspaces);
// Writing the sign if existing.
if(sign) character(sign);
// Writing the zeros.
character_n('0', zeros);
// Initializing x.
x = abs(format._double) / tmp;
// Writing the first digit.
character(x + '0');
character('.');
// Writing the decimal digits.
for(i = 0; i < format.precision; i++)
{
// Multiplying x by 10 and getting rid of the previous digit.
x = (x - (int)x) * 10;
// Writing the current digit.
character(x + '0');
}
// Writing the exponent letter and its sign.
character(format.type);
character(exponent < 0 ? '-' : '+');
// Getting a positive exponent.
exponent = abs(exponent);
// Using a multiplier for the exponent.
if(exponent >= 1000) mul = 1000;
else if(exponent >= 100) mul = 100;
else mul = 10;
// Writing the exponent characters.
while(mul)
{
// Writing the next character.
character((exponent / mul) % 10 + '0');
// Dividing the multiplier.
mul *= 0.1;
}
// Writing the ending whitespaces if left-aligned.
character_n(' ', espaces);
}
*/
/*
format_c()
Character output. Only handles left alignment and spacing.
Options '#', '0', ' ' and '+', as well as mantissa digit number, have
no effect.
*/
static void format_c(struct Format format)
{
// In character display, the digit number represents the number of
// characters written, including the argument and additional
// whitespaces.
int spaces = format.characters - 1;
int left = format.flags & LeftAlign;
if(!left) character_n(' ', spaces);
character(format._int & 0xff);
if(left) character_n(' ', spaces);
}
/*
format_s()
String output. Spaces if needed.
*/
void format_s(struct Format format)
{
// In string display, the character number specify the minimum size of
// output (padded with whitespaces if needed) and the precision
// specify the maximum number of string characters output.
int string = format.precision;
int spaces;
const char *str = format._pointer;
int length, i;
int left = format.flags & LeftAlign;
// Computing length of string and number of whitespaces.
length = strlen(str);
if(string > length || string == -1) string = length;
spaces = format.characters - string;
if(!left) character_n(' ', spaces);
for(i = 0; i < string; i++) character(str[i]);
if(left) character_n(' ', spaces);
}
/*
format_p()
Pointer output. Simple hexadecimal dump. Prints "(nil)" if pointer is
NULL.
*/
void format_p(struct Format format)
{
// Pointer display falls back to %#08x in the pointer is non-null,
// "(nil)" otherwise.
unsigned int x = format._unsigned;
int bspaces, zeros, digits = 0, espaces;
int c;
digits = x ? 10 : 5;
get_spacing(format, &bspaces, NULL, &zeros, digits, &espaces);
character_n(' ', bspaces);
character_n('0', zeros);
if(x)
{
character('0');
character('x');
while(x)
{
c = x >> 28;
c += '0' + 39 * (c > 9);
character(c);
x <<= 4;
}
}
else
{
character('(');
character('n');
character('i');
character('l');
character(')');
}
character_n(' ', espaces);
}

15
src/stdio/vsnprintf.c Normal file
View File

@ -0,0 +1,15 @@
#include <internals/stdio.h>
#include <stdio.h>
#include <string.h>
/*
vsnprintf()
The most generic formatted printing function around there.
*/
int vsnprintf(char *str, size_t size, const char *format, va_list args)
{
int x = __printf(size, format, args);
strncpy(str, __stdio_buffer, size);
return x;
}

15
src/stdio/vsprintf.c Normal file
View File

@ -0,0 +1,15 @@
#include <internals/stdio.h>
#include <stdio.h>
#include <string.h>
/*
vsprintf()
Prints to a string from an argument list.
*/
int vsprintf(char *str, const char *format, va_list args)
{
int x = __printf(0, format, args);
strncpy(str, __stdio_buffer, __stdio_buffer_size);
return x;
}

11
src/string/strchr.c Normal file
View File

@ -0,0 +1,11 @@
#include <string.h>
/*
strchr
Searches a character in a string.
*/
const char *strchr(const char *str, int value)
{
while(*str && *str != value) *str++;
return *str ? str : NULL;
}

11
src/string/strcpy.c Normal file
View File

@ -0,0 +1,11 @@
#include <string.h>
/*
strcpy()
Copies a string to another.
*/
char *strcpy(char *destination, const char *source)
{
size_t length = strlen(source);
return memcpy(destination, source, length);
}

20
src/string/strncpy.c Normal file
View File

@ -0,0 +1,20 @@
#include <string.h>
/*
strncpy()
Copies part of a string to another.
*/
char *strncpy(char *destination, const char *source, size_t size)
{
size_t length = strlen(source);
if(length >= size)
{
return memcpy(destination, source, size);
}
else
{
memset(destination + length, 0, size - length);
return memcpy(destination, source, length);
}
}

18
src/tales/dprint.c Normal file
View File

@ -0,0 +1,18 @@
#include <internals/tales.h>
#include <internals/stdio.h>
#include <stdarg.h>
/*
dprint()
Prints a formatted string. Works the same as printf().
*/
void dprint(int x, int y, const char *format, ...)
{
va_list args;
va_start(args, format);
__printf(0, format, args);
va_end(args);
dtext(__stdio_buffer, x, y);
}

18
src/tales/gprint.c Normal file
View File

@ -0,0 +1,18 @@
#include <internals/tales.h>
#include <internals/stdio.h>
#include <stdarg.h>
/*
gprint()
Prints a formatted string. Works the same as printf().
*/
void gprint(int x, int y, const char *format, ...)
{
va_list args;
va_start(args, format);
__printf(0, format, args);
va_end(args);
gtext(__stdio_buffer, x, y);
}