diff --git a/Makefile b/Makefile index 6ed5617..ea3834a 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ -# -# fx-9860g lib Makefile. -# +#! /usr/bin/make -f -.PHONY: all clean fclean re install +#--- +# fx-9860g lib Makefile. +#--- @@ -23,8 +23,7 @@ bin = build/ginttest.bin elf = build/ginttest.elf # Command-line options -cflags = -m3 -mb -nostdlib -ffreestanding \ - -W -Wall -pedantic -std=c11 \ +cflags = -m3 -mb -nostdlib -ffreestanding -W -Wall \ -I . -isystem include lib = -lgcc -L. -lgint -lc @@ -42,20 +41,20 @@ src-lib = crt0.c syscalls.s \ 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 = $(addprefix build/, $(addsuffix .o, $(src-lib))) -hdr-lib = $(addprefix include/, $(hea-lib)) +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 = $(addprefix build/, $(addsuffix .o, $(src-std))) -hdr-std = $(addprefix include/, $(hea-std)) +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 = build/font.o $(addprefix build/, $(addsuffix .o, $(img-app))) +res-app = $(patsubst %, build/%.o, $(img-app)) build/font.o # @@ -77,7 +76,7 @@ libc.a: $(obj-std) @ echo "\033[32;1mStandard file size: "`stat -c %s libc.a` \ "bytes\033[0m" -ginttest.g1a: libgint.a $(src-app) $(res-app) +$(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 @@ -97,7 +96,7 @@ build/%.s.o: src/%.s $(as) -c $^ -o $@ build/%.bmp.o: resources/%.bmp - fxconv $^ -o $@ + fxconv $^ -o $@ --preview build/font.o: resources/font.bmp fxconv --font $^ -o $@ @@ -136,3 +135,7 @@ re: distclean all install: usb-connector SEND ginttest.g1a ginttest.g1a fls0 + + + +.PHONY: all clean mrproper distclean re install diff --git a/TODO b/TODO index 304e43c..1c84e6c 100644 --- a/TODO +++ b/TODO @@ -1,27 +1,34 @@ -@ vram overflow -@ keyboard interface -- upgraded blending modes -- blending modes for text -- information masks for text -- test all font encodings -- font clipping + -------------- + Lots of things to do + -------------- -- bitmap parts -- bitmap clipping + @ known bugs + + simple improvements + - important milestones + ~ needs investigation -- multi-getkey repeats (if possible, which doesn't seem likely) -- it appears that multi-getkey does not always trigger rectangle effects on - sh7305 -- write and test gray engine -- full rtc driver (time) -- callbacks and complete user API +@ vram overflow +@ keyboard test threading interface -- exhaustive save for setjmp() -- registers that need to be saved when configuring gint ++ compute frequencies ++ gray text ++ effective rtc callback ++ properly test gray drawing ++ upgraded blending modes ++ blending modes for text ++ information masks for text ++ test all font encodings ++ font clipping ++ bitmap parts ++ bitmap clipping -- check possible bug for optimization of __attribute__((interrupt_handler)) +- write and test gray engine +- full rtc driver (time) +- callbacks and complete user API -- 7305.h -- libc +~ shadowy rectangle effect for Shift + Alpha + Left + Down +~ exhaustive save for setjmp() +~ registers that need to be saved when configuring gint +~ possible bug when -O2 __attribute__((interrupt_handler)) diff --git a/doc/bopti.md b/doc/bopti.md new file mode 100644 index 0000000..5113f56 --- /dev/null +++ b/doc/bopti.md @@ -0,0 +1,305 @@ + +# gint documentation: bitmap rendering # + + +*Warning: this is a draft. The current implementation of bopti is different* +*from this description, though similar.* + + + +## Basics + +The bitmap drawing module, *bopti*, is based on video-ram (vram) bitwise +operations. The images are made of layers that describe (more or less) which +pixels of the image an operation applies to. Rendering the image consists in +applying an operation function to the existing vram pixels. + +*bopti* makes an extensive use of longword operations and 4-alignment to take +advantage of the bit-based structure of the monochrome vram and enhance +performance. Among all possible optimizations, avoiding direct pixel access has +proven to be the most efficient. + + + +--- + + + +## Operations + +Operations are functions applied to update a vram longword in accordance with +an operation mask. Bits that are set in the mask indicate pixels which have to +be updated by the operation. Bits that are reset indicate pixels that must not +be changed. + +All the point is, the functions must not access the bit information in the mask +or the vram data individually. They must operate globally using longword +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. +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 +parameters, and performs `data = data & ~mask`, which is 2 processor cycles +long. + +The following operations are defined by *bopti*: + +- `Draw `: Draws black pixels. +- `Alpha `: Erases non-transparent pixels. +- `Change `: Changes the pixels' color. +- `Lighten `: Lightens gray pixels. +- `Lighten2`: Lightens gray pixels more. +- `Darken `: Darkens gray pixels. +- `Darken2 `: Darkens gray pixels more. + +To perform an operation, *bopti* uses the mask data, which is taken from a +layer, and calls the associated operation function. Every operation has its +default layer mask (except `change`), but this setting may be overridden. +*bopti* allows user programs to use any monochrome image as a mask for an +operation. For instance, a black rectangle may be drawn by any of the operation +functions, resulting in various results. + +An additional operation, `fill`, is defined by the library. It does all the job +necessary to render the full image, which often falls back to performing the +operations that correspond to the kind of image. + + + +--- + + + +## Operation on gray pixels + +*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. + + white = 0 [00] + lightgray = 1 [01] + darkgray = 2 [10] + black = 3 [11] + +The `Lighten` operation affects pixels as if decrementing their value (white +pixels are not changed), and `darken` does the opposite (black pixels are not +changed). +Operations `Lighten2` and `darken2` do the same two times. + +From this description, and considering two bits `light` and `dark`, it follows +that: + +```c +lighten2 (light, dark) = (0, light & dark) +lighten (light, dark) = (light & dark, light & ~dark) +darken (light, dark) = (light | dark, light | ~dark) +darken2 (light, dark) = (1, light | dark) +``` + +This does not take account of a possible operation mask. See section +[Operation functions](#operation-functions) for more flexible functions. + + + +--- + + + +## Partial transparency + +*bopti* allows monochrome images to have semi-transparent pixels. Consider for +example a white background. An opaque black pixel will render black, while a +1/3-transparent black pixel will render dark gray, and a 2/3-transparent black +pixel will render light gray. Which means that: + +* 1/3-transparent white pixels form the mask for `lighten2` +* 2/3-transparent white pixels form the mask for `lighten` +* 2/3-transparent black pixels form the mask for `darken` +* 1/3-transparent black pixels form the mask for `darken2` + +Partial transparency on gray pixels is not allowed. Apart from the complexity +of the generic partial transparency rendering operation, semi-transparent gray +pixels are not of any use. + + + +--- + + + +## Operation functions + +Operations on monochrome buffers are defined as functions of two parameters: +the vram data longword to update, `data`, and the operation mask, `x`. Every +of these functions must satisfy `f(data, 0) = data`. + +Operations on gray buffers take three arguments: `light` and `dark`, which are +longwords from the [gray buffers](gray-engine), and the operation mask `x`. +They update both longwords and return them. These functions must satisfy +`f(light, dark, 0) = (light, dark)`. + +The functions for each of the operations are the following: + +~~~c +# Draw function +draw(data, x) = data | x + +# Alpha function +alpha(data, x) = data & ~x + +# Change function +change(data, x) = data ^ x + +# Lighten function +lighten(light, dark, x) = (light & (dark | ~x), (light | ~x) & (x ^ dark)) + +# Lighten2 function +lighten2(light, dark, x) = (light & ~x, (light | ~x) & dark) + +# Darken function +darken(light, dark, x) = (light | (dark & x), (light & x) | (x ^ dark)) + +# Darken2 function +darken2(light, dark, x) = (light | x, (light & x) | dark) +~~~ + +One could easily check that these functions do their jobs when `x = 1` and +leave the data unchanged when `x = 0`. + + + +--- + + + +## Image format + +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. +* 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). +* Non-transparent gray images also have two layers: one for each +[gray buffer](gray-engine). Both are for the `draw` operation. +* Transparent gray images have three layers. Two of them constitute the two-bit +color information for the `draw` operation, and the third is the `alpha` +operation mask. +* Semi-transparent monochrome images also have three layers. Two are used to +store the two-bit transparency level information (0 is opaque, 3 is fully +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 + +------+------+------+---+ + | | | | | + | | | | | + | | | | | + +------+------+------+---+ + + Bitmap + +The first bytes of the layer data is the column data. Each column is encoded +as a 32-bit integer array from top to bottom. Columns are written from left to +right. The end is encoded as an 8-bit or 16-bit integer array depending on its +size, and written from top to bottom. Additionally, 0 to 3 NUL (0x00) bytes are +added to make the layer size a multiple of 4 (to allow 32-bit access to the +column data of the following layer). + +In case of big images (see the image structure below), the end is expanded to +a 32-pixel column to improve performance. + +The image itself is a structure of the following kind (in case of small +images): + +```c +struct Image +{ + unsigned char magic; + unsigned char format; + + unsigned char width; + unsigned char height; + + const uint32_t data[]; + +} __attribute__((aligned(4))); +``` + +For bigger images (`width` > 255 or `height` > 255), both `width` and `height` +are set to `0` and the actual size information is written on two shorts just +where the data resides: + +```c +struct BigImage +{ + unsigned char magic; + unsigned char format; + + unsigned char null_width; /* contains 0 */ + unsigned char null_height; /* contains 0 */ + + unsigned short width; + unsigned short height; + + const uint32_t data[]; + +} __attribute__((aligned(4))); +``` + +This does not create a memory loss because a two-byte gap was needed to make +the data 4-aligned. + +* The `magic` number, which is common to all the file formats of *gint*, +identifies the file type and version of the structure. *bopti* will not render +an image which is not encoded for its specific version. + +* The `format` attribute describes the layer distribution, as specified by the +following enum: + + ```c + enum ImageFormat + { + ImageFormat_Mono = 0x01, + ImageFormat_MonoAlpha = 0x09, + ImageFormat_Gray = 0x06, + ImageFormat_GrayAlpha = 0x0e, + ImageFormat_GreaterAlpha = 0x31, + + ImageFormat_ColorMask = 0x07, + ImageFormat_AlphaMask = 0x38, + }; + ``` + + `Alpha` refers to uniform transparency. The only format that supports + partial transparency is `GreaterAlpha`, and it is always encoded as + monochrome (because using gray pixels would lead to 9 different colors, + which is rather unoptimized). Gray images with partial transparency + will be refused by *fxconv*. + + +* The `width` and `height` attributes are exactly what you expect. + +* The `data` is simply made of all the layers put one after another. Layers are +put in the following order: + + [0] Monochrome `draw` layer + [1] Dark gray `draw` layer + [2] Light gray `draw` layer + [3] Uniform `alpha` layer + [4] First semi-`alpha` layer + [5] Second semi-`alpha` layer + + Not every format uses the six layers, of course. The layers used by + each format may be found by reading the position of the `1`'s in the + corresponding `enum ImageFormat` entry. Layers that are not needed are + skipped. diff --git a/ginttest.c b/ginttest.c index a1ef815..9e54fd0 100644 --- a/ginttest.c +++ b/ginttest.c @@ -213,7 +213,7 @@ void bitmap_test(void) Image *sybl = &binary_resources_symbol_start; Image *sybl2 = &binary_resources_symbol2_start; - enum BlendingMode blend = Blend_Or; +// enum BlendingMode blend = Blend_Or; uint32_t a32 = 0xffffffff; int black_bg = 0; int key; @@ -223,20 +223,22 @@ void bitmap_test(void) dclear(); if(black_bg) dreverse_area(0, 0, 127, 63); - dimage(opt, 0, 57, Blend_Invert); + dimage(opt, 0, 57); - dimage(sprites, 2 & a32, 2, blend); - dimage(sybl, 30 & a32, 40, blend); - dimage(sybl2, 62 & a32, 40, blend); + dimage(sprites, 2 & a32, 2); + dimage(sybl, 30 & a32, 40); + dimage(sybl2, 62 & a32, 40); dupdate(); key = getkey(); if(key == KEY_EXIT) break; +/* if(key == KEY_F1) blend = Blend_Or; if(key == KEY_F2) blend = Blend_And; if(key == KEY_F3) blend = Blend_Invert; +*/ if(key == KEY_F4) black_bg = !black_bg; if(key == KEY_F5) a32 ^= 31; @@ -257,7 +259,7 @@ void text_test(void) extern Font binary_resources_font_start; Font *font = &binary_resources_font_start; - print_configure(font, Blend_Or); + print_configure(font); dclear(); @@ -290,18 +292,23 @@ void gray_test(void) gray_getDelays(&light, &dark); gray_start(); - dimage(illustration, 0, 0, Blend_Or); while(1) { + gclear(); + dimage(illustration, 0, 0); + gclear_area(64, 0, 127, 63); +// gupdate(); + key = getkey(); if(key == KEY_EXIT) break; - +/* if(key == KEY_F1) gray_setDelays(--light, dark); if(key == KEY_F2) gray_setDelays(++light, dark); if(key == KEY_F5) gray_setDelays(light, --dark); if(key == KEY_F6) gray_setDelays(light, ++dark); +*/ } gray_stop(); @@ -367,7 +374,7 @@ int main(void) extern Font binary_resources_font_start; Font *font = &binary_resources_font_start; - print_configure(font, Blend_Or); + print_configure(font); int app; diff --git a/ginttest.g1a b/ginttest.g1a index 78b5a4e..5542edb 100644 Binary files a/ginttest.g1a and b/ginttest.g1a differ diff --git a/include/ctype.h b/include/ctype.h index 0a716be..82748b5 100644 --- a/include/ctype.h +++ b/include/ctype.h @@ -21,7 +21,7 @@ ((c) >= 'a' && (c) <= 'f')) // Character manipulation macros. -#define tolower(c) (isupper(c) ? (c)|32 : (c)) -#define toupper(c) (islower(c) ? (c)&~32 : (c)) +#define tolower(c) (isupper(c) ? (c) | 32 : (c)) +#define toupper(c) (islower(c) ? (c) & ~32 : (c)) #endif // _CTYPE_H diff --git a/include/display.h b/include/display.h index 58a111e..afca785 100644 --- a/include/display.h +++ b/include/display.h @@ -1,62 +1,29 @@ +//--- +// +// gint drawing module: display +// +// Handles vram manipulation and drawing for plain monochrome display. +// +//--- + #ifndef _DISPLAY_H #define _DISPLAY_H 1 + //--- -// Included submodules. +// Heading declarations. //--- #include - - -//--- -// Types and constants. -//--- - enum Color { - Color_White = 0, // White (AND 0) - Color_Black = 1, // Black (OR 1) - Color_None = 2, // Transparent (NOP) - Color_Invert = 3, // Invert (XOR 1) -}; - -/* - enum BlendingMode - Describes the various blending modes available for drawing images. - Blending modes may be combined for special effects! For instance: - - Or Only sets black pixels. - - And Only erases white pixels. - - Or | And Fully draws the bitmap. - - Invert Inverts pixels where the bitmap is black. - - Or | Invert Erases black pixels. - Adding Checker to an existing combination limits the operation to - pixels (x, y) that satisfy (x + y) & 1 == 1. - Operations are done in the following order : Or - Invert - And. -*/ -enum BlendingMode -{ - Blend_Or = 0x01, - Blend_Invert = 0x02, - Blend_And = 0x04, - - Blend_Checker = 0x10, -}; - -/* - enum ImageFormat - Describes the various image formats available. Colors may be encoded - as monochrome (1 layer) or gray (2 layers). Whatever the color map, any - bitmap may also have an additional alpha layer. -*/ -enum ImageFormat -{ - ImageFormat_Mono = 0x01, - ImageFormat_Gray = 0x02, - - ImageFormat_Alpha = 0x10, - - ImageFormat_ColorMask = 0x0f, + Color_White = 0, + Color_Light = 1, + Color_Dark = 2, + Color_Black = 3, + Color_None = 4, + Color_Invert = 5, }; /* @@ -69,10 +36,10 @@ enum ImageFormat struct Image { unsigned char magic; + unsigned char format; unsigned char width; unsigned char height; - unsigned char format; const unsigned char __attribute__((aligned(4))) data[]; @@ -94,12 +61,10 @@ typedef struct Image Image; /* display_getLocalVRAM() - Returns the local video ram. This function always return the same - address. + Returns the local video ram address. This function always return the + same address. The buffer returned by this function should not be used directly when running the gray engine. - - @return Video ram address of the monochrome display module. */ void *display_getLocalVRAM(void); @@ -114,13 +79,11 @@ void *display_getCurrentVRAM(void); /* display_useVRAM() - Changes the current video ram address. The argument *MUST* be a - 4-aligned 1024 buffer ; otherwise any drawing operation will crash the - program. + Changes the current video ram address. The argument MUST be a 4- + aligned 1024-byte buffer; otherwise any drawing operation will crash + the program. This function will most likely have no effect when running the gray engine. - - @arg New video ram address. */ void display_useVRAM(void *vram); @@ -132,7 +95,8 @@ void display_useVRAM(void *vram); /* dupdate() - Displays the vram on the physical screen. + Displays the vram on the physical screen. Does nothing when the gray + engine is running. */ void dupdate(void); @@ -144,23 +108,15 @@ void dclear(void); /* dclear_area() - Clears an area of the video ram. - - @arg x1 - @arg y1 - @arg x2 - @arg y2 + Clears an area of the video ram. Both (x1, y1) and (x2, y2) are + cleared. */ void dclear_area(int x1, int y1, int x2, int y2); /* dreverse_area() - Reverses an area of the screen. - - @arg x1 - @arg y1 - @arg x2 - @arg y2 + Reverses an area of the vram. (x1, y1) and (x2, y2) are reversed as + well. */ void dreverse_area(int x1, int y1, int x2, int y2); @@ -172,45 +128,25 @@ void dreverse_area(int x1, int y1, int x2, int y2); /* dpixel() - Puts a pixel on the screen. - - @arg x - @arg y - @arg color + Puts a pixel in the vram. */ void dpixel(int x, int y, enum Color color); /* dline() - Draws a line on the screen. Automatically optimizes horizontal and + Draws a line in the vram. Automatically optimizes horizontal and vertical lines. Uses an algorithm written by PierrotLL for MonochromeLib. - - @arg x1 - @arg y1 - @arg x2 - @arg y2 - @arg color */ void dline(int x1, int y1, int x2, int y2, enum Color color); - - -//--- -// Image drawing. -//--- - /* dimage() - Displays an image in the vram. Does a real lot of optimization. - - @arg image - @arg x - @arg y - @arg mode + Displays a monochrome image in the vram. Does a real lot of + optimization. */ -void dimage(struct Image *image, int x, int y, enum BlendingMode mode); +void dimage(struct Image *image, int x, int y); diff --git a/include/gray.h b/include/gray.h index c685fd1..94653ac 100644 --- a/include/gray.h +++ b/include/gray.h @@ -1,10 +1,26 @@ +//--- +// +// gint core/drawing module: gray +// +// Runs the gray engine and handles drawing for the dual-buffer system. +// +//--- + #ifndef _GRAY_H #define _GRAY_H 1 +#include + //--- -// Public API. +// Engine control. //--- +/* + gray_runs() + Returns 1 if the gray engine is running, 0 otherwise. +*/ +int gray_runs(void); + /* gray_start() Starts the gray engine. The control of the screen is transferred to the @@ -33,10 +49,7 @@ void *gray_darkVRAM(void); /* gray_getDelays() - Returns the gray engine delays. - - @arg light Will be set if non-NULL. - @arg dark Will be set if non-NULL. + Returns the gray engine delays. Pointers are not set if NULL. */ void gray_getDelays(int *light, int *dark); @@ -47,23 +60,72 @@ void gray_getDelays(int *light, int *dark); Finding values that give proper grays is quite the hard part of the gray engine. Usual values are about 1000, with light being between 75 and 90% of dark. - - @arg light Light gray duration (the lower). - @arg dark Dark gray duration (the higher). */ void gray_setDelays(int light, int dark); + + +//--- +// Global drawing functions. +//--- + +/* + gupdate() + Swaps the vram buffer sets. +*/ +void gupdate(void); + +/* + gclear() + Clears the video ram. +*/ +void gclear(void); + +/* + gclear_area() + Clears an area of the video ram. End points (x1, y1) and (x2, y2) are + included. +*/ +void gclear_area(int x1, int y1, int x2, int y2); + +/* + greverse_area() + Reverses an area of the vram. End points (x1, y1) and (x2, y2) are + included. +*/ +void greverse_area(int x1, int y1, int x2, int y2); + + + +//--- +// Local drawing functions. +//--- + +/* + gpixel() + Puts a pixel in the vram. +*/ +void gpixel(int x, int y, enum Color color); + +/* + gline() + Draws a line in the vram. Automatically optimizes special cases. +*/ +void gline(int x1, int y1, int x2, int y2, enum Color color); + +/* + gimage() + Displays a gray image in the vram. +*/ +void gimage(struct Image *image, int x, int y); + + + //--- // Internal API. // Referenced here for documentation purposes only. Do not call. //-- -/* - gray_swap() - Swaps the vram buffers. -*/ -void gray_swap(void); - /* gray_interrupt() Answers a timer interrupt. Swaps the two buffers. diff --git a/include/stdlib.h b/include/stdlib.h index 8d0fb91..de053e8 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -23,8 +23,6 @@ void abort(void); exit() Stops the program execution with the given status code, after calling the exit handlers. - - @arg status */ void exit(int status); @@ -38,10 +36,6 @@ void exit(int status); malloc() Allocs 'size' bytes and returns a pointer to a free memory area. Returns NULL on error. - - @arg size Size to allocate, in bytes. - - @return Memory area address, or NULL. */ void *malloc(size_t size); @@ -49,19 +43,12 @@ void *malloc(size_t size); calloc() Allocs 'n' elements of size 'size' and wipes the memory area. Returns NULL on error. - - @arg n Element number. - @arg size Element size. - - @return Memory area address, or NULL. */ void *calloc(size_t n, size_t size); /* free() Frees a memory block allocated with malloc(). - - @arg ptr Pointer to free. */ void free(void *ptr); diff --git a/include/tales.h b/include/tales.h index 14e6646..f191c32 100644 --- a/include/tales.h +++ b/include/tales.h @@ -8,8 +8,6 @@ // Types and constants. //--- -enum BlendingMode; - /* enum ImageFormat This type holds information about the characters in the font. Each bit @@ -78,7 +76,6 @@ typedef struct Font Font; - //--- // Generic functions. //--- @@ -90,7 +87,7 @@ typedef struct Font Font; @arg font @arg mode */ -void print_configure(struct Font *font, enum BlendingMode mode); +void print_configure(struct Font *font); /* print_raw() diff --git a/libc.a b/libc.a index 79d61d8..c3e9146 100644 Binary files a/libc.a and b/libc.a differ diff --git a/libgint.a b/libgint.a index cf2e8b9..045f71e 100644 Binary files a/libgint.a and b/libgint.a differ diff --git a/src/bopti.c b/src/bopti.c index aaa6998..674252e 100644 --- a/src/bopti.c +++ b/src/bopti.c @@ -1,74 +1,152 @@ +//--- +// +// gint drawing module: bopti +// +// bopti does every job related to display images. There is only one +// public function, but there are lots of internal optimizations. +// +// Some bit-manipulation expressions may look written out of nowhere. The +// idea is always the same: get a part of the image in an 'operator', +// which is a 32-bit variable, shift this operator so that its bits +// correspond to the desired position for the bitmap on the screen, and +// edit the video-ram long entry which correspond to this position using +// a 'mask' that indicates which bits of the operator contain information. +// +//--- + #include #include #include + //--- -// Image drawing. There is only one public function dimage(), but there -// are lots of local methods and optimizations. -// -// Some expressions may look nonsense sometimes. The procedure is always -// the same : get a part of the image in an operator, shift it depending -// on the drawing x-coordinate, compute a mask that indicates which bits -// of the operator contain information, and modify a vram long using the -// operator and the mask. +// Heading declarations. //--- -// This pointer is set by bopti(). -static int *vram; +/* + enum Channel + Determines the kind of information written into a layer. Every image is + made of one or more channels. +*/ +enum Channel +{ + Channel_Mono = 0x01, + Channel_Light = 0x02, + Channel_Dark = 0x04, + + Channel_FullAlpha = 0x08, + Channel_LightAlpha = 0x10, + Channel_DarkAlpha = 0x20, +}; + +/* + enum Format + Describes the various combination of channels allowed by bopti. +*/ +enum Format +{ + Format_Mono = Channel_Mono, + Format_MonoAlpha = Format_Mono | Channel_FullAlpha, + Format_Gray = Channel_Light | Channel_Dark, + Format_GrayAlpha = Format_Gray | Channel_FullAlpha, + Format_GreaterAlpha = Format_Mono | Channel_LightAlpha | + Channel_DarkAlpha +}; + +// These pointers are set by dimage() to avoid having to repeatedly determine +// which video ram to use. +static int *vram, *v1, *v2; +// The following variables refer to parameters that do not change during the +// drawing operation (at least for the time of a layer). They could be passed +// on by every function from the module, but this would be heavy and useless. +static enum Channel channel; +static int height; +static void (*op)(int offset, uint32_t operator, uint32_t op_mask); + + + +//--- +// Drawing operation. +//--- /* bopti_op() Operates on a vram long. The operator will often not contain 32 bits of - image information. In this case, the bits outside the image must be set - to 0 for Or and Invert operations... 1 for And operations. Which means - that the calling procedure must indicate what part of the operator - belongs to the image, which is done through the image_mask argument. - - @arg offset Vram offset where edition is planned. - @arg operator Longword to operate with. - @arg image_mask Part of the operator that is inside the image. - @arg mode Operation mode. + image information. Since neutral bits are not the same for all + operations, the op_mask argument indicates which bits should be used + for the operation. Which operation has to be done is determined by the + channel setting. */ -static void bopti_op(int offset, uint32_t operator, uint32_t image_mask, - enum BlendingMode mode) +static void bopti_op_mono(int offset, uint32_t operator, uint32_t op_mask) { - if(mode & Blend_Checker) operator &= 0x55555555; - if(mode & Blend_Or) vram[offset] |= operator; - if(mode & Blend_Invert) vram[offset] ^= operator; - operator |= ~image_mask; - if(mode & Blend_And) vram[offset] &= operator; + operator &= op_mask; + + switch(channel) + { + case Channel_Mono: + vram[offset] |= operator; + break; + + case Channel_FullAlpha: + vram[offset] &= ~operator; + break; + + default: + break; + } +} +static void bopti_op_gray(int offset, uint32_t operator, uint32_t op_mask) +{ + operator &= op_mask; + + switch(channel) + { + case Channel_Mono: + v1[offset] |= operator; + v2[offset] |= operator; + break; + + case Channel_Light: + v1[offset] |= operator; + break; + + case Channel_Dark: + v2[offset] |= operator; + break; + + case Channel_FullAlpha: + v1[offset] &= ~operator; + v2[offset] &= ~operator; + break; + + case Channel_LightAlpha: + case Channel_DarkAlpha: + break; + } } /* bopti_grid() -- general form bopti_grid_a32() -- when x is a multiple of 32 - Draws a layer whose length is a multiple of 32. + Draws the grid at the beginning of a layer's data. The length of this + grid is always a multiple of 32. The need for bopti_grid_a32() is not only linked to optimization, - because one of the bit shifts in bopti_grid() will reach 32 when x is - a multiple of 32, which is undefined behavior. - - @arg layer Raw column data (column data is located at the - beginning of layer data). - @arg x - @arg y - @arg column_number - @arg height - @arg mode + because bopti_grid() will perform a 32-bit shift when x is a multiple + of 32, which is undefined behavior. */ - static void bopti_grid_a32(const uint32_t *layer, int x, int y, - int column_number, int height, enum BlendingMode mode) + int column_count) { int vram_column_offset = (y << 2) + (x >> 5); int vram_offset = vram_column_offset; int column, row; - for(column = 0; column < column_number; column++) + for(column = 0; column < column_count; column++) { for(row = 0; row < height; row++) { - bopti_op(vram_offset, *layer, 0xffffffff, mode); + (*op)(vram_offset, *layer, 0xffffffff); layer++; vram_offset += 4; } @@ -77,10 +155,15 @@ static void bopti_grid_a32(const uint32_t *layer, int x, int y, vram_offset = vram_column_offset; } } - -static void bopti_grid(const uint32_t *layer, int x, int y, int column_number, - int height, enum BlendingMode mode) +static void bopti_grid(const uint32_t *layer, int x, int y, int column_count) { + if(!column_count) return; + if(!(x & 31)) + { + bopti_grid_a32(layer, x, y, column_count); + return; + } + const uint32_t *p1, *p2; uint32_t l1, l2; int right_column, line; @@ -95,33 +178,26 @@ static void bopti_grid(const uint32_t *layer, int x, int y, int column_number, uint32_t and_mask_0 = 0xffffffff >> shift2; uint32_t and_mask_1 = 0xffffffff << shift1; - if(!column_number) return; - if(!(x & 31)) - { - bopti_grid_a32(layer, x, y, column_number, height, mode); - return; - } - // Initializing two pointers. Since the columns are written one after // another, they will be updated directly to parse the whole grid. p1 = layer - height; p2 = layer; // Drawing vram longwords, using pairs of columns. - for(right_column = 0; right_column <= column_number; right_column++) + for(right_column = 0; right_column <= column_count; right_column++) { and_mask = 0xffffffff; if(right_column == 0) and_mask &= and_mask_0; - if(right_column == column_number) and_mask &= and_mask_1; + if(right_column == column_count) and_mask &= and_mask_1; for(line = 0; line < height; line++) { l1 = (right_column > 0) ? (*p1) : (0); - l2 = (right_column < column_number) ? (*p2) : (0); + l2 = (right_column < column_count) ? (*p2) : (0); p1++, p2++; operator = (l1 << shift1) | (l2 >> shift2); - bopti_op(vram_offset, operator, and_mask, mode); + (*op)(vram_offset, operator, and_mask); vram_offset += 4; } @@ -131,65 +207,44 @@ static void bopti_grid(const uint32_t *layer, int x, int y, int column_number, } /* - bopti_rest_get() - Returns the line of a bitmap, whose width is lower than 32. The given - pointer is read and set according to the bitmap width. - - @arg ptr Address of data pointer. - @arg size Element size (should be 1, 2, or 4 bytes). + bopti_end_get() + Returns an operator for the end of a line, whose width is lower than 32 + (by design: otherwise, it would have been a column). The given pointer + is read and updated so that it points to the next line at the end of + the operation. */ -static uint32_t bopti_rest_get(const unsigned char **data, int size) +static uint32_t bopti_end_get1(const unsigned char **data) { - uint32_t line; - - if(size == 4) - { - line = *((uint32_t *)*data); - *data += 4; - return line; - } - - else if(size == 2) - { - line = *((uint16_t *)*data); - *data += 2; - return line; - } - - else - { - line = **data; - (*data)++; - return line; - } + uint32_t operator = **data; + *data += 1; + return operator; +} +static uint32_t bopti_end_get2(const unsigned char **data) +{ + uint32_t operator = *((uint16_t *)*data); + *data += 2; + return operator; } /* bopti_rest() -- general form - bopti_rest_nover() -- when the bitmap does not overlap two longs + bopti_rest_nover() -- when the end does not overlap two vram longs - Draws a bitmap, whose width is lower than 32. It is called the 'rest' - since the biggest part will be drawn by bopti_grid(). - - @arg rest Ending data. Encoded on 1, 2 or 4 bytes per line - depending on the rest width. - @arg x - @arg y - @arg width Rest width. - @arg height - @arg mode + Draws the end of a layer, which can be considered as a whole layer + whose with is lower than 32. (Actually is it lower or equal to 16; + otherwise it would have been a column and the end would be empty). */ - -static void bopti_rest_nover(const unsigned char *rest, int x, int y, - int width, int height, enum BlendingMode mode) +static void bopti_end_nover(const unsigned char *end, int x, int y, int width) { - int element_size = (width > 16) ? (4) : (width > 8) ? (2) : (1); + uint32_t (*get)(const unsigned char **data) = + (width > 8) ? bopti_end_get2 : bopti_end_get1; + int vram_offset = (y << 2) + (x >> 5); int row; // We *have* shift >= 0 because of this function's 'no overlap' // requirement. - int shift_base = (4 - element_size) << 3; + int shift_base = (width > 8) ? 16 : 24; int shift = shift_base - (x & 31); uint32_t and_mask = (0xffffffff << (32 - width)) >> (x & 31); @@ -197,28 +252,28 @@ static void bopti_rest_nover(const unsigned char *rest, int x, int y, for(row = 0; row < height; row++) { - operator = bopti_rest_get(&rest, element_size); + operator = (*get)(&end); operator <<= shift; - bopti_op(vram_offset, operator, and_mask, mode); + (*op)(vram_offset, operator, and_mask); vram_offset += 4; } } - -static void bopti_rest(const unsigned char *rest, int x, int y, int width, - int height, enum BlendingMode mode) +static void bopti_end(const unsigned char *end, int x, int y, int width) { if((x & 31) + width <= 32) { - bopti_rest_nover(rest, x, y, width, height, mode); + bopti_end_nover(end, x, y, width); return; } - int element_size = (width > 16) ? (4) : (width > 8) ? (2) : (1); + uint32_t (*get)(const unsigned char **data) = + (width > 8) ? (bopti_end_get2) : (bopti_end_get1); + int vram_offset = (y << 2) + (x >> 5); int row; - int shift_base = (4 - element_size) << 3; + int shift_base = (width > 8) ? 16 : 24; int shift1 = (x & 31) - shift_base; int shift2 = shift_base + 32 - (x & 31); @@ -229,99 +284,150 @@ static void bopti_rest(const unsigned char *rest, int x, int y, int width, for(row = 0; row < height; row++) { - row_data = bopti_rest_get(&rest, element_size); + row_data = (*get)(&end); operator = row_data >> shift1; - bopti_op(vram_offset, operator, and_mask_0, mode); + (*op)(vram_offset, operator, and_mask_0); operator = row_data << shift2; - bopti_op(vram_offset + 1, operator, and_mask_1, mode); + (*op)(vram_offset + 1, operator, and_mask_1); vram_offset += 4; } } + + +//--- +// Wrappers and various functions. +//--- + /* bopti() - Draws an image layer in the video ram. - - @arg bitmap Raw layer data. - @arg x - @arg y - @arg width - @arg height - @arg mode + Draws a layer in the video ram. */ -static void bopti(const unsigned char *layer, int x, int y, int width, - int height, enum BlendingMode mode) +static void bopti(const unsigned char *layer, int x, int y, int columns, + int end_size) { - int column_number = width >> 5; - int rest_width = width & 31; + const unsigned char *end = layer + ((columns * height) << 2); + int end_x = x + (columns << 5); - vram = display_getCurrentVRAM(); + bopti_grid((const uint32_t *)layer, x, y, columns); + if(end_size) bopti_end(end, end_x, y, end_size); +} - // Skipping 'column_number' columns of 'height' longs. - const unsigned char *rest = layer + ((column_number * height) << 2); - int rest_x = x + (width - rest_width); +/* + getStructure() + Determines the image size and data pointer. +*/ +static void getStructure(struct Image *img, int *width, int *height, + int *layer_size, const unsigned char **data, int *columns, + int *end_size) +{ + int column_count, end, end_bytes, layer; - bopti_grid((const uint32_t *)layer, x, y, column_number, height, mode); - if(!rest_width) return; - bopti_rest(rest, rest_x, y, rest_width, height, mode); + // Large images. + if(!img->width && !img->height) + { + if(width) *width = (img->data[0] << 8) | img->data[1]; + if(height) *height = (img->data[2] << 8) | img->data[3]; + if(data) *data = img->data + 4; + + column_count = (*width + 31) >> 5; + end = end_bytes = 0; + } + else + { + if(width) *width = img->width; + if(height) *height = img->height; + if(data) *data = img->data; + + column_count = img->width >> 5; + end = img->width & 31; + end_bytes = + !end ? 0 : + end <= 8 ? 1 : + end <= 16 ? 2 : + 4; + } + + // The layer size must be rounded to a multiple of 4. + layer = img->height * ((column_count << 2) + end_bytes); + if(layer & 3) layer += 4 - (layer & 3); + + if(columns) *columns = column_count; + if(end_size) *end_size = end; + if(layer_size) *layer_size = layer; } /* dimage() - Displays an image in the vram. - - @arg image - @arg x - @arg y - @arg mode + Displays a monochrome image in the video ram. */ - -void dimage(struct Image *image, int x, int y, enum BlendingMode mode) +void dimage(struct Image *img, int x, int y) { - int width = image->width; - int height = image->height; - const unsigned char *data = (const unsigned char *)&(image->data); + int width, layer_size, columns, end; + int format = img->format, i = 0; + const unsigned char *data; - if(image->magic != 0xb7) return; + if(img->magic != 0xb7) return; + if(img->format != Format_Mono && img->format != Format_MonoAlpha) + return; + op = bopti_op_mono; - // Computing the layer size. - int columns = image->width >> 5; - int rest = image->width & 31; - int rest_size = - !rest ? 0 : - rest <= 8 ? 1 : - rest <= 16 ? 2 : - 4; - int layer_size = ((columns << 2) + rest_size) * image->height; - // The layer size must be a multiple of 4. - if(layer_size & 3) layer_size += 4 - (layer_size & 3); + // 'height' refers to a static variable for this file. + getStructure(img, &width, &height, &layer_size, &data, &columns, &end); - switch(image->format & ImageFormat_ColorMask) + vram = display_getCurrentVRAM(); + + while(format) { - case ImageFormat_Mono: - if(image->format & ImageFormat_Alpha) + // Drawing every layer, in order of formats. + if(format & 1) { - bopti(data + layer_size, x, y, width, height, - Blend_And); - } - bopti(data, x, y, width, height, mode); - break; - - case ImageFormat_Gray: - if(image->format & ImageFormat_Alpha) - { - bopti(data + 2 * layer_size, x, y, width, height, - Blend_And); + channel = (1 << i); + bopti(data, x, y, columns, end); + data += layer_size; } - display_useVRAM(gray_darkVRAM()); - bopti(data, x, y, width, height, mode); - display_useVRAM(gray_lightVRAM()); - bopti(data + layer_size, x, y, width, height, mode); - break; + format >>= 1; + i++; } + +} + +/* + gimage() + Displays a gray image in the video ram. +*/ +void gimage(struct Image *img, int x, int y) +{ + int width, layer_size, columns, end; + int format = img->format, i = 0; + const unsigned char *data; + + if(img->magic != 0xb7) return; + op = bopti_op_gray; + + // 'height' refers to a static variable for this file. + getStructure(img, &width, &height, &layer_size, &data, &columns, &end); + + v1 = gray_lightVRAM(); + v2 = gray_darkVRAM(); + + while(format) + { + // Drawing every layer, in order of formats. + if(format & 1) + { + channel = (1 << i); + bopti(data, x, y, columns, end); + data += layer_size; + } + + format >>= 1; + i++; + } + } diff --git a/src/display.c b/src/display.c index e6d2d48..2fa046f 100644 --- a/src/display.c +++ b/src/display.c @@ -1,27 +1,29 @@ -/* - display - - Handles vram manipulation and drawing. - - :: Rectangle masks - - The concept of 'rectangle masks' is used several times in this module. - It consists in saying that an operation that affects a rectangle acts - the same on all the lines (considering that only the lines that - intersect the rectangle are changed) and therefore it is possible to - represent the behavior on a single line using 'masks' that 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, reversing a rectangle of - vram only needs vram[offset] ^= masks[i]. - - This technique can also be used in more subtle cases with more complex - patterns, but within this module it is unlikely to happen. -*/ +//--- +// +// gint drawing module: display +// +// Handles vram manipulation and drawing. +// +// +// :: 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. +// +//--- #include #include @@ -47,14 +49,10 @@ static int *vram = local_vram; /* adjust() Adjusts the given rectangle coordinates to ensure that : - - The rectangle is entirely contained in the screen, - - x1 < x2 and y1 < y2, + - the rectangle is entirely contained in the screen + - x1 < x2 + - y1 < y2 which is needed when working with screen rectangles. - - @arg x1 - @arg y1 - @arg x2 - @arg y2 */ static void adjust(int *x1, int *y1, int *x2, int *y2) { @@ -75,11 +73,8 @@ static void adjust(int *x1, int *y1, int *x2, int *y2) getmasks() Computes the rectangle masks needed to affect pixels located between x1 - and x2 (both included). - - @arg x1 - @arg x2 - @arg masks Four-integer-array pointer. + and x2 (both included). The four masks are stored in the third argument + (seen as an array). */ static void getmasks(int x1, int x2, unsigned int *masks) { @@ -99,11 +94,10 @@ static void getmasks(int x1, int x2, unsigned int *masks) // number of null bits to add in l1 and l2. x1 &= 31; // Inverting x2 is here the same as computing 32 - x, since 32 is a - // power of 2 (actually it creates positive bits at the left but those - // ones are removed by the bitwise-and mask). + // power of 2 (positive bits at the left are removed by the mask). x2 = ~x2 & 31; - // Setting the last masks. + // Setting the first and last masks. masks[l1] &= (0xffffffff >> x1); masks[l2] &= (0xffffffff << x2); } @@ -116,34 +110,37 @@ static void getmasks(int x1, int x2, unsigned int *masks) /* display_getLocalVRAM() - Returns the local video ram. - - @return Video ram address. + Returns the local video ram address. This function always return the + same address. + The buffer returned by this function should not be used directly when + running the gray engine. */ -void *display_getLocalVRAM(void) +inline void *display_getLocalVRAM(void) { return (void *)local_vram; } /* display_getCurrentVRAM() - Returns the current vido ram. - - @return Video ram address. + Returns the current video ram. This function usually returns the + parameter of the last call to display_useVRAM(), unless the gray engine + is running (in which case the result is undefined). Returns the local + vram address by default. */ -void *display_getCurrentVRAM(void) +inline void *display_getCurrentVRAM(void) { return (void *)vram; } /* display_useVRAM() - Changes the current video ram address. Expects a *4-aligned* 1024-byte - buffer. - - @arg New video ram address. + Changes the current video ram address. The argument MUST be a 4- + aligned 1024-byte buffer ; otherwise any drawing operation will crash + the program. + This function will most likely have no effect when running the gray + engine. */ -void display_useVRAM(void *ptr) +inline void display_useVRAM(void *ptr) { vram = (int *)ptr; } @@ -160,6 +157,7 @@ void display_useVRAM(void *ptr) */ void dupdate(void) { + if(gray_runs()) return; screen_display((const void *)local_vram); } @@ -175,12 +173,8 @@ void dclear(void) /* dclear_area() - Clears an area of the vram using rectangle masks. - - @arg x1 - @arg y1 - @arg x2 - @arg y2 + Clears an area of the vram using rectangle masks. Both (x1, y1) and + (x2, y2) are cleared. */ void dclear_area(int x1, int y1, int x2, int y2) { @@ -188,23 +182,19 @@ void dclear_area(int x1, int y1, int x2, int y2) adjust(&x1, &y1, &x2, &y2); getmasks(x1, x2, masks); - int offset = y1 << 2; + int begin = y1 << 2; int end = (y2 + 1) << 2; int i; for(i = 0; i < 4; i++) masks[i] = ~masks[i]; - while(offset < end) vram[offset] &= masks[offset & 3], offset++; + for(i = begin; i < end; i++) vram[i] &= masks[i & 3]; } /* dreverse_area() Reverses an area of the vram. This function is a simple application of - the rectangle masks concept. - - @arg x1 - @arg y1 - @arg x2 - @arg y2 + the rectangle masks concept. (x1, y1) and (x2, y2) are reversed as + well. */ void dreverse_area(int x1, int y1, int x2, int y2) { @@ -212,10 +202,25 @@ void dreverse_area(int x1, int y1, int x2, int y2) adjust(&x1, &y1, &x2, &y2); getmasks(x1, x2, masks); - int offset = y1 << 2; + int begin = y1 << 2; int end = (y2 + 1) << 2; + int i; - while(offset < end) vram[offset] ^= masks[offset & 3], offset++; + if(gray_runs()) + { + int *v1 = gray_lightVRAM(); + int *v2 = gray_darkVRAM(); + + for(i = begin; i < end; i++) + { + v1[i] ^= masks[i & 3]; + v2[i] ^= masks[i & 3]; + } + } + else for(i = begin; i < end; i++) + { + vram[i] ^= masks[i & 3]; + } } @@ -226,15 +231,12 @@ void dreverse_area(int x1, int y1, int x2, int y2) /* dpixel() - Puts a pixel on the screen. - - @arg x - @arg y - @arg color + Puts a pixel in the vram. */ void dpixel(int x, int y, enum Color color) { if((unsigned int)x > 127 || (unsigned int)y > 63) return; + int offset = (y << 2) + (x >> 5); int mask = 0x80000000 >> (x & 31); @@ -248,12 +250,12 @@ void dpixel(int x, int y, enum Color color) vram[offset] |= mask; break; - case Color_None: - return; - case Color_Invert: vram[offset] ^= mask; break; + + default: + break; } } @@ -261,12 +263,6 @@ void dpixel(int x, int y, enum Color color) dline() Draws a line on the screen. Automatically optimizes horizontal and vertical lines. - - @arg x1 - @arg y1 - @arg x2 - @arg y2 - @arg color */ static void dhline(int x1, int x2, int y, enum Color color) @@ -275,9 +271,60 @@ static void dhline(int x1, int x2, int y, enum Color color) int offset = y << 2; int i; + // Swapping x1 and x2 if needed. + if(x1 > x2) x1 ^= x2, x2 ^= x1, x1 ^= x2; getmasks(x1, x2, masks); - switch(color) + int *v1 = gray_lightVRAM(); + int *v2 = gray_darkVRAM(); + + if(gray_runs()) switch(color) + { + case Color_White: + for(i = 0; i < 4; i++) + { + v1[offset + i] &= ~masks[i]; + v2[offset + i] &= ~masks[i]; + } + break; + + case Color_Light: + for(i = 0; i < 4; i++) + { + v1[offset + i] |= masks[i]; + v2[offset + i] &= ~masks[i]; + } + break; + + case Color_Dark: + for(i = 0; i < 4; i++) + { + v1[offset + i] &= ~masks[i]; + v2[offset + i] |= masks[i]; + } + break; + + case Color_Black: + for(i = 0; i < 4; i++) + { + v1[offset + i] |= masks[i]; + v2[offset + i] |= masks[i]; + } + break; + + case Color_Invert: + for(i = 0; i < 4; i++) + { + v1[offset + i] ^= masks[i]; + v2[offset + i] ^= masks[i]; + } + break; + + default: + break; + } + + else switch(color) { case Color_White: for(i = 0; i < 4; i++) vram[offset + i] &= ~masks[i]; @@ -287,12 +334,12 @@ static void dhline(int x1, int x2, int y, enum Color color) for(i = 0; i < 4; i++) vram[offset + i] |= masks[i]; break; - case Color_None: - return; - case Color_Invert: for(i = 0; i < 4; i++) vram[offset + i] ^= masks[i]; break; + + default: + break; } } @@ -302,7 +349,61 @@ static void dvline(int y1, int y2, int x, enum Color color) int end = (y2 << 2) + (x >> 5); int mask = 0x80000000 >> (x & 31); - switch(color) + int *v1 = gray_lightVRAM(); + int *v2 = gray_darkVRAM(); + + if(gray_runs()) switch(color) + { + case Color_White: + while(offset <= end) + { + v1[offset] &= ~mask; + v2[offset] &= ~mask; + offset += 4; + } + break; + + case Color_Light: + while(offset <= end) + { + v1[offset] |= mask; + v2[offset] &= ~mask; + offset += 4; + } + break; + + case Color_Dark: + while(offset <= end) + { + v1[offset] &= ~mask; + v2[offset] |= mask; + offset += 4; + } + break; + + case Color_Black: + while(offset <= end) + { + v1[offset] |= mask; + v2[offset] |= mask; + offset += 4; + } + break; + + case Color_Invert: + while(offset <= end) + { + v1[offset] ^= mask; + v2[offset] ^= mask; + offset += 4; + } + break; + + default: + break; + } + + else switch(color) { case Color_White: while(offset <= end) vram[offset] &= ~mask, offset += 4; @@ -312,12 +413,12 @@ static void dvline(int y1, int y2, int x, enum Color color) while(offset <= end) vram[offset] |= mask, offset += 4; break; - case Color_None: - return; - case Color_Invert: while(offset <= end) vram[offset] ^= mask, offset += 4; break; + + default: + break; } } diff --git a/src/gint_7305.c b/src/gint_7305.c index 12f310e..d103920 100644 --- a/src/gint_7305.c +++ b/src/gint_7305.c @@ -91,10 +91,6 @@ static void kdelay(void) krow() Reads a keyboard row. Works like krow() for SH7705. See gint_7705.c for more details. - - @arg row Row to check (0 <= row <= 9). - - @return Bit-based representation of pressed keys in the checked row. */ static int krow(int row) { diff --git a/src/gray.c b/src/gray.c index 8687df9..3e86b2e 100644 --- a/src/gray.c +++ b/src/gray.c @@ -1,3 +1,11 @@ +//--- +// +// gint core/drawing module: gray +// +// Runs the gray engine and handles drawing for the dual-buffer system. +// +//--- + #include #include #include @@ -9,8 +17,25 @@ const void *vrams[4]; static int current = 0; static int delays[2]; +static int runs = 0; + #define GRAY_PRESCALER TIMER_Po_64 + + +//--- +// Engine control. +//--- + +/* + gray_runs() + Returns 1 if the gray engine is running, 0 otherwise. +*/ +inline int gray_runs(void) +{ + return runs; +} + /* gray_start() Starts the gray engine. The control of the screen is transferred to the @@ -19,6 +44,7 @@ static int delays[2]; void gray_start(void) { timer_start(TIMER_GRAY, delays[0], GRAY_PRESCALER, gray_interrupt, 0); + runs = 1; } /* @@ -29,6 +55,8 @@ void gray_start(void) void gray_stop(void) { timer_stop(TIMER_GRAY); + runs = 0; + display_useVRAM(display_getLocalVRAM()); } @@ -36,26 +64,23 @@ void gray_stop(void) gray_lightVRAM() Returns the module's gray vram address. */ -void *gray_lightVRAM(void) +inline void *gray_lightVRAM(void) { - return (void *)vrams[current]; + return (void *)vrams[current & 2]; } /* gray_lightVRAM() Returns the module's dark vram address. */ -void *gray_darkVRAM(void) +inline void *gray_darkVRAM(void) { - return (void *)vrams[current + 1]; + return (void *)vrams[(current & 2) | 1]; } /* gray_getDelays() - Returns the gray engine delays. - - @arg light Will be set if non-NULL. - @arg dark Will be set if non-NULL. + Returns the gray engine delays. Pointers are not set if NULL. */ void gray_getDelays(int *light, int *dark) { @@ -66,9 +91,6 @@ void gray_getDelays(int *light, int *dark) /* gray_setDelays() Changes the gray engine delays. - - @arg light Light gray duration (the lower). - @arg dark Dark gray duration (the higher). */ void gray_setDelays(int light, int dark) { @@ -82,15 +104,6 @@ void gray_setDelays(int light, int dark) // Internal API. //--- -/* - gray_swap() - Swaps the vram buffers. -*/ -void gray_swap(void) -{ - current = (current + 2) & 3; -} - /* gray_interrupt() Answers a timer interrupt. Swaps the buffers. @@ -113,6 +126,130 @@ void gray_init(void) vrams[2] = (const void *)internal_vrams[1]; vrams[3] = (const void *)internal_vrams[2]; - delays[0] = 3269; - delays[1] = 6987; + delays[0] = 900; + delays[1] = 1000; +} + + + +//--- +// Global drawing functions +//--- + +/* + gupdate() + Swaps the vram buffer sets. +*/ +inline void gupdate(void) +{ + current ^= 2; +} + +/* + gclear() + Clears the video ram. +*/ +void gclear(void) +{ + int *v1 = gray_lightVRAM(); + int *v2 = gray_darkVRAM(); + int i; + + for(i = 0; i < 256; i++) v1[i] = v2[i] = 0; +} + +/* + gclear_area() + Clears an area of the video ram. End points (x1, y1) and (x2, y2) are + included. +*/ +void gclear_area(int x1, int y1, int x2, int y2) +{ + display_useVRAM(gray_lightVRAM()); + dclear_area(x1, y1, x2, y2); + display_useVRAM(gray_darkVRAM()); + dclear_area(x1, y1, x2, y2); +} + +/* + greverse_area() + Reverses an area of the vram. End points (x1, y1) and (x2, y2) are + included. +*/ +void greverse_area(int x1, int y1, int x2, int y2) +{ + display_useVRAM(gray_lightVRAM()); + dreverse_area(x1, y1, x2, y2); + display_useVRAM(gray_darkVRAM()); + dreverse_area(x1, y1, x2, y2); +} + + + +//--- +// Local drawing functions. +//--- + +/* + gpixel() + Puts a pixel in the vram. +*/ +void gpixel(int x, int y, enum Color color) +{ + if((unsigned int)x > 127 || (unsigned int)y > 63) return; + + int offset = (y << 2) + (x >> 5); + int mask = 0x80000000 >> (x & 31); + + int *v1 = gray_lightVRAM(); + int *v2 = gray_lightVRAM(); + + switch(color) + { + case Color_White: + v1[offset] &= ~mask; + v2[offset] &= ~mask; + break; + + case Color_Light: + v1[offset] |= mask; + v2[offset] &= ~mask; + break; + + case Color_Dark: + v1[offset] &= ~mask; + v2[offset] |= mask; + break; + + case Color_Black: + v1[offset] |= mask; + v2[offset] |= mask; + break; + + case Color_Invert: + v1[offset] ^= mask; + v2[offset] ^= mask; + break; + + default: + break; + } +} + +/* + gline() + Draws a line in the vram. Automatically optimizes special cases. +*/ +void gline(int x1, int y1, int x2, int y2, enum Color color) +{ + enum Color c1, c2; + + if(color == Color_None) return; + else if(color == Color_Invert) c1 = c2 = Color_Invert; + else c1 = color & 1, c2 = color >> 1; + + display_useVRAM(gray_lightVRAM()); + dline(x1, y1, x2, y2, c1); + display_useVRAM(gray_darkVRAM()); + dline(x1, y1, x2, y2, c2); } diff --git a/src/tales.c b/src/tales.c index fa564a2..777134e 100644 --- a/src/tales.c +++ b/src/tales.c @@ -6,7 +6,6 @@ #include static struct Font *font; -static enum BlendingMode mode; //--- // Local functions. @@ -198,10 +197,9 @@ static int update(uint32_t *operators, int height, int available, @arg font @arg mode */ -void print_configure(struct Font *next_font, enum BlendingMode next_mode) +void print_configure(struct Font *next_font) { font = next_font; - mode = next_mode; } /*