diff --git a/.gitignore b/.gitignore index 767efe3..6caafaa 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/Makefile b/Makefile index 9a01f78..e358b80 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/Makefile.old b/Makefile.old deleted file mode 100644 index ea3834a..0000000 --- a/Makefile.old +++ /dev/null @@ -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 diff --git a/README.md b/README.md index 847a24b..5892e31 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/TODO b/TODO index 9a46c54..e2f2ba8 100644 --- a/TODO +++ b/TODO @@ -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 diff --git a/demo/gintdemo.c b/demo/gintdemo.c index cfadaae..4f14e51 100644 --- a/demo/gintdemo.c +++ b/demo/gintdemo.c @@ -9,79 +9,36 @@ #include #include +#include #include <7305.h> +#include + //--- -// 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(); diff --git a/doc/bopti.md b/doc/bopti.md index 5113f56..4cb6b2b 100644 --- a/doc/bopti.md +++ b/doc/bopti.md @@ -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 +------+------+------+---+ | | | | | | | | | | diff --git a/gintdemo.g1a b/gintdemo.g1a index 7a61a46..10aa989 100644 Binary files a/gintdemo.g1a and b/gintdemo.g1a differ diff --git a/include/7305.h b/include/7305.h index 1eb7e71..3288678 100644 --- a/include/7305.h +++ b/include/7305.h @@ -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 diff --git a/include/alloca.h b/include/alloca.h index 9cf0899..37c88e7 100644 --- a/include/alloca.h +++ b/include/alloca.h @@ -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. diff --git a/include/ctype.h b/include/ctype.h index 8c5bbd5..8c7ee2d 100644 --- a/include/ctype.h +++ b/include/ctype.h @@ -1,3 +1,11 @@ +//--- +// +// standard library module: ctype +// +// Some character manipulation. +// +//--- + #ifndef _CTYPE_H #define _CTYPE_H 1 diff --git a/include/display.h b/include/display.h index 4cca184..2b53a14 100644 --- a/include/display.h +++ b/include/display.h @@ -14,8 +14,7 @@ // Heading declarations. //--- -#include -#include +#include 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 diff --git a/include/gint.h b/include/gint.h index 879d576..8e7fa35 100644 --- a/include/gint.h +++ b/include/gint.h @@ -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. //--- diff --git a/include/internals/bopti.h b/include/internals/bopti.h index 3e0116c..3ed11cb 100644 --- a/include/internals/bopti.h +++ b/include/internals/bopti.h @@ -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; diff --git a/include/internals/display.h b/include/internals/display.h index a227edd..ac8d4bd 100644 --- a/include/internals/display.h +++ b/include/internals/display.h @@ -9,6 +9,47 @@ #ifndef _INTERNALS_DISPLAY_H #define _INTERNALS_DISPLAY_H 1 +#include + 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 diff --git a/include/internals/stdio.h b/include/internals/stdio.h new file mode 100644 index 0000000..bf68b81 --- /dev/null +++ b/include/internals/stdio.h @@ -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 +#include + +//--- +// 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 diff --git a/include/setjmp.h b/include/setjmp.h index 0addccf..54dceea 100644 --- a/include/setjmp.h +++ b/include/setjmp.h @@ -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); diff --git a/include/stdio.h b/include/stdio.h new file mode 100644 index 0000000..f19ecdb --- /dev/null +++ b/include/stdio.h @@ -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 +#include + +//--- +// 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 diff --git a/include/stdlib.h b/include/stdlib.h index 0a0ff01..0eb5591 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -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 diff --git a/include/string.h b/include/string.h index e746347..dc0ac1c 100644 --- a/include/string.h +++ b/include/string.h @@ -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 diff --git a/include/tales.h b/include/tales.h index bb990b9..5d5c25f 100644 --- a/include/tales.h +++ b/include/tales.h @@ -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 diff --git a/libc.a b/libc.a index 6bdac15..ddbff9e 100644 Binary files a/libc.a and b/libc.a differ diff --git a/libgint.a b/libgint.a index 072c42e..895e9b7 100644 Binary files a/libgint.a and b/libgint.a differ diff --git a/src/bopti/bopti_internals.c b/src/bopti/bopti_internals.c index 60caec1..bb48bdb 100644 --- a/src/bopti/bopti_internals.c +++ b/src/bopti/bopti_internals.c @@ -1,7 +1,7 @@ #include // 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: diff --git a/src/bopti/dimage.c b/src/bopti/dimage.c index 8c2f5f9..d4d300f 100644 --- a/src/bopti/dimage.c +++ b/src/bopti/dimage.c @@ -1,5 +1,5 @@ #include -#include +#include /* 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) { diff --git a/src/bopti/gimage.c b/src/bopti/gimage.c index 65e0ed3..27df32b 100644 --- a/src/bopti/gimage.c +++ b/src/bopti/gimage.c @@ -1,5 +1,5 @@ #include -#include +#include #include /* @@ -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) { diff --git a/src/display/font_system.bmp b/src/display/font_system.bmp index e866558..0f49d5b 100644 Binary files a/src/display/font_system.bmp and b/src/display/font_system.bmp differ diff --git a/src/gray/gray_engine.c b/src/gray/gray_engine.c index de4f0e4..7938891 100644 --- a/src/gray/gray_engine.c +++ b/src/gray/gray_engine.c @@ -9,6 +9,7 @@ #include #include #include +#include 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; } diff --git a/src/rtc/rtc_interrupt.c b/src/rtc/rtc_interrupt.c index a606a45..96e66a3 100644 --- a/src/rtc/rtc_interrupt.c +++ b/src/rtc/rtc_interrupt.c @@ -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; diff --git a/src/setjmp/setjmp.s b/src/setjmp/setjmp.s index 81a053a..3b6f910 100644 --- a/src/setjmp/setjmp.s +++ b/src/setjmp/setjmp.s @@ -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. diff --git a/src/stdio/snprintf.c b/src/stdio/snprintf.c new file mode 100644 index 0000000..eed74db --- /dev/null +++ b/src/stdio/snprintf.c @@ -0,0 +1,19 @@ +#include +#include +#include + +/* + 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; +} diff --git a/src/stdio/sprintf.c b/src/stdio/sprintf.c new file mode 100644 index 0000000..a283635 --- /dev/null +++ b/src/stdio/sprintf.c @@ -0,0 +1,19 @@ +#include +#include +#include + +/* + 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; +} diff --git a/src/stdio/stdio_format.c b/src/stdio/stdio_format.c new file mode 100644 index 0000000..392ddc7 --- /dev/null +++ b/src/stdio/stdio_format.c @@ -0,0 +1,912 @@ +#include +#include + +#include +#include +#include + +/* + 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); +} diff --git a/src/stdio/vsnprintf.c b/src/stdio/vsnprintf.c new file mode 100644 index 0000000..5cc13e3 --- /dev/null +++ b/src/stdio/vsnprintf.c @@ -0,0 +1,15 @@ +#include +#include +#include + +/* + 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; +} diff --git a/src/stdio/vsprintf.c b/src/stdio/vsprintf.c new file mode 100644 index 0000000..448c9da --- /dev/null +++ b/src/stdio/vsprintf.c @@ -0,0 +1,15 @@ +#include +#include +#include + +/* + 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; +} diff --git a/src/string/strchr.c b/src/string/strchr.c new file mode 100644 index 0000000..c90725d --- /dev/null +++ b/src/string/strchr.c @@ -0,0 +1,11 @@ +#include + +/* + strchr + Searches a character in a string. +*/ +const char *strchr(const char *str, int value) +{ + while(*str && *str != value) *str++; + return *str ? str : NULL; +} diff --git a/src/string/strcpy.c b/src/string/strcpy.c new file mode 100644 index 0000000..7659120 --- /dev/null +++ b/src/string/strcpy.c @@ -0,0 +1,11 @@ +#include + +/* + strcpy() + Copies a string to another. +*/ +char *strcpy(char *destination, const char *source) +{ + size_t length = strlen(source); + return memcpy(destination, source, length); +} diff --git a/src/string/strncpy.c b/src/string/strncpy.c new file mode 100644 index 0000000..98422fa --- /dev/null +++ b/src/string/strncpy.c @@ -0,0 +1,20 @@ +#include + +/* + 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); + } +} diff --git a/src/tales/dprint.c b/src/tales/dprint.c new file mode 100644 index 0000000..5547c52 --- /dev/null +++ b/src/tales/dprint.c @@ -0,0 +1,18 @@ +#include +#include +#include + +/* + 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); +} diff --git a/src/tales/gprint.c b/src/tales/gprint.c new file mode 100644 index 0000000..37e6862 --- /dev/null +++ b/src/tales/gprint.c @@ -0,0 +1,18 @@ +#include +#include +#include + +/* + 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); +}