From 94fb300e7258e7a54aaffac92f522a133eb3700c Mon Sep 17 00:00:00 2001 From: Lephe Date: Mon, 13 Jul 2020 13:49:07 +0200 Subject: [PATCH] gray: finalize the gray engine API * Define dgray() to replace gray_start() and gray_stop() * Introduce a mechanism to override the d*() functions rather than using another set of functions, namely g*(). Gray rendering should now be done with d*() (a compatibility macro for g*() is available until v2.1). * Gray engine now reserves TMU0 at the start of the add-in to prevent surprises if timers are exhausted, so it nevers fails to start * Replace other gray engine functions with dgray_*() * More general rendering functions (in render/) to lessen the burden of porting them to the gray engine. As a consequence, dtext_opt(), dprint_opt() and drect_border() are now available in the gray engine, which was an omission from 230b796. * Allow C_NONE in more functions, mainly on fx-CG 50 * Remove the now-unused dupdate_noint() --- TODO | 3 +- include/display/fx.h | 61 +++++- include/gint/display.h | 209 +++++++++++-------- include/gint/gray.h | 254 ++++++++++-------------- src/gray/engine.c | 196 ++++++++++++------ src/gray/gclear.c | 2 +- src/gray/ghline.c | 7 - src/gray/gint_gline.c | 16 ++ src/gray/gline.c | 55 ----- src/gray/gpixel.c | 4 +- src/gray/grect.c | 12 +- src/gray/{gimage.c => gsubimage.c} | 12 +- src/gray/gtext.c | 20 +- src/gray/gvline.c | 7 - src/render-cg/dhline.c | 7 - src/render-cg/dpixel.c | 4 +- src/render-cg/drect.c | 4 +- src/render-cg/{dimage.c => dsubimage.c} | 8 +- src/render-cg/dupdate.c | 6 - src/render-cg/{dline.c => gint_dline.c} | 0 src/render-cg/topti.c | 6 - src/render-fx/dclear.c | 3 + src/render-fx/dpixel.c | 5 +- src/render-fx/drect.c | 4 +- src/render-fx/{dimage.c => dsubimage.c} | 12 +- src/render-fx/dtext.c | 30 --- src/render-fx/dupdate.c | 18 +- src/render-fx/dvline.c | 7 - src/render-fx/{dline.c => gint_dline.c} | 4 +- src/render-fx/topti.c | 24 +++ src/{render-fx => render}/dhline.c | 4 +- src/render/dimage.c | 7 + src/render/dline.c | 16 +- src/render/dtext.c | 7 + src/{render-cg => render}/dvline.c | 4 +- 35 files changed, 557 insertions(+), 481 deletions(-) delete mode 100644 src/gray/ghline.c create mode 100644 src/gray/gint_gline.c delete mode 100644 src/gray/gline.c rename src/gray/{gimage.c => gsubimage.c} (71%) delete mode 100644 src/gray/gvline.c delete mode 100644 src/render-cg/dhline.c rename src/render-cg/{dimage.c => dsubimage.c} (61%) rename src/render-cg/{dline.c => gint_dline.c} (100%) rename src/render-fx/{dimage.c => dsubimage.c} (67%) delete mode 100644 src/render-fx/dtext.c delete mode 100644 src/render-fx/dvline.c rename src/render-fx/{dline.c => gint_dline.c} (91%) rename src/{render-fx => render}/dhline.c (52%) create mode 100644 src/render/dimage.c create mode 100644 src/render/dtext.c rename src/{render-cg => render}/dvline.c (52%) diff --git a/TODO b/TODO index 506b871..c1e08c0 100644 --- a/TODO +++ b/TODO @@ -1,14 +1,15 @@ For the 2.1.0 release: * bopti: remove the deprecated image_t definition * project: remove the compat branch +* project: remove the gray aliases Issues: * #10 support fx-CG 20 Extensions on existing code: +* build: move the private headers to the src/ folder * tmu: make interrupt handlers more elegant * bopti: try to display fullscreen images with TLB access + DMA on fxcg50 -* gray: double-buffer gray settings and unify d* with g* * topti: support unicode fonts * dma: fx9860g support (need to switch it on and update the Makefile) * core: try to leave add-in without reset in case of panic diff --git a/include/display/fx.h b/include/display/fx.h index 349e98e..d4257e3 100644 --- a/include/display/fx.h +++ b/include/display/fx.h @@ -8,7 +8,7 @@ #include #include -/* masks() - compute the vram masks for a given rectangle +/* masks(): Compute the vram masks for a given rectangle Since the VRAM is line-based with four uin32_t elements per row, we can execute any operation on a rectangle by running it on each set of four @@ -24,7 +24,7 @@ @masks Stores the result of the function (four uint32_t values) */ void masks(int x1, int x2, uint32_t *masks); -/* bopti_render_clip() - render a bopti image with clipping +/* bopti_render_clip(): Render a bopti image with clipping @x @y Location of the top-left corner @img Image encoded by [fxconv] @left @top @w @h Bounding box to render @@ -33,7 +33,7 @@ void masks(int x1, int x2, uint32_t *masks); void bopti_render_clip(int x, int y, bopti_image_t const *img, int left, int top, int w, int h, uint32_t *v1, uint32_t *v2, void *bopti_asm); -/* bopti_render_noclip() - render a bopti image without clipping +/* bopti_render_noclip(): Render a bopti image without clipping This function is only ever slightly faster than bopti_render_clip(), eliminating two types of coordinate checks: 1. The bounding box does not overflow from the image @@ -47,4 +47,59 @@ void bopti_render_clip(int x, int y, bopti_image_t const *img, int left, void bopti_render_noclip(int x, int y, bopti_image_t const *img, int left, int top, int w, int h, uint32_t *v1, uint32_t *v2, void *bopti_asm); +//--- +// Alternate rendering modes +//--- + +/* The gray engine overrides the rendering functions by specifying a set of + alternate primitives that are suited to work with two VRAMs. To avoid + linking with them when the gray engine is not used, the display module + exposes a global state in the form of a struct rendering_mode and the gray + engine modifies that state when it runs. */ +struct rendering_mode +{ + /* Because the gray engine still has business to do after the call to + dgray(DGRAY_OFF), the original dupdate() is made to execute after + the replacement one if the replacement one returns 1. */ + int (*dupdate)(void); + /* Area rendering */ + void (*dclear)(color_t color); + void (*drect)(int x1, int y1, int x2, int y2, color_t color); + /* Point rendering */ + void (*dpixel)(int x, int y, color_t color); + void (*gint_dhline)(int x1, int x2, int y, int color); + void (*gint_dvline)(int y1, int y2, int x, int color); + /* Text and image rendering */ + void (*dtext_opt) + (int x, int y, int fg, int bg, int halign, int valign, + char const *str); + void (*dsubimage) + (int x, int y, bopti_image_t const *image, int left, int top, + int width, int height, int flags); +}; + +/* The alternate rendering mode pointer (initially NULL)*/ +extern struct rendering_mode const *dmode; + +/* These are the corresponding gray rendering functions */ +int gupdate(void); +void gclear(color_t color); +void grect(int x1, int y1, int x2, int y2, color_t color); +void gpixel(int x, int y, color_t color); +void gint_ghline(int x1, int x2, int y, int color); +void gint_gvline(int y1, int y2, int x, int color); +void gtext_opt + (int x, int y, int fg, int bg, int halign, int valign, + char const *str); +void gsubimage + (int x, int y, bopti_image_t const *image, int left, int top, + int width, int height, int flags); + +/* Short macro to call the alternate rendering function when available */ +#define DMODE_OVERRIDE(func, ...) \ + if(dmode && dmode->func) { \ + dmode->func(__VA_ARGS__); \ + return; \ + } + #endif /* DISPLAY_FX */ diff --git a/include/gint/display.h b/include/gint/display.h index 3c00c0c..2bdf74a 100644 --- a/include/gint/display.h +++ b/include/gint/display.h @@ -29,12 +29,33 @@ //--- /* dupdate(): Push the video RAM to the display driver - This function makes the contents of the VRAM visible on the screen. It is - the direct equivalent of Bdisp_PutDisp_DD(). - On fxcg50, if triple buffering is enabled (which is the default and disabled - only by calling dvram()), it also swaps buffers. Due to the DMA being used, - always waits for the previous call to dupdate() call() to finish. */ + This function makes the contents of the VRAM visible on the screen. It is + the equivalent of Bdisp_PutDisp_DD() in most situations. + + On fx-9860G, this function also manages the gray engine settings. When the + gray engine is stopped, it pushes the contents of the VRAM to screen, and + when it is on, it swaps buffer and lets the engine's timer push the VRAMs to + screen when suitable. To make the transition between the two modes smooth, + dgray() does not enable the gray engine immediately; instead the first call + to update() after dgray() switches the gray engine on and off. + + On fx-CG 50, because rendering is slow and sending data to screen is also + slow, a special mechanism known as triple buffering is implemented. Two + VRAMs are allocated, and frames are alternately rendered on each VRAM. When + dupdate() is called to push one of the VRAMs to screen, it starts the DMA + (which performs the push in the background) and immediately returns after + switching to the other VRAM so that the user can draw during the push. + + The transfer from the DMA to the screen lasts about 10 ms if memory is + available every cycle. Each access to the memory delays the transfer by a + bit, so to fully use the available time, try to run AIs, physics or other + calculation-intensive code rather than using the VRAM. + + Typical running time without overclock: + fx-9860G: 1 ms (gray engine off) + fx-9860G: ~30% of CPU time (gray engine on) + fx-CG 50: 11 ms */ void dupdate(void); //--- @@ -42,88 +63,108 @@ void dupdate(void); //--- /* dclear(): Fill the screen with a single color + This function clears the screen by painting all the pixels in a single - color. It is optimized for opaque colors. + color. It is optimized for opaque colors; on fx-CG 50, it uses dma_memset() + to fill in the VRAM. - On fx9860g, use drect() if you need complex operators such as invert. + Typical running time without overclock: + fx-9860G SH3: 70 µs + fx-9860G SH4: 15 µs + fx-CG 50: 2.5 ms (full-screen drect() is about 6 ms) - @color fx9860g: white, black - fxcg50: Any R5G6B5 color */ + On fx9860g, use drect() if you need operators that modify existing pixels + instead of replacing them such as invert. + + @color fx-9860G: white light* dark* black + fx-CG 50: Any R5G6B5 color + + *: When the gray engine is on, see dgray(). */ void dclear(color_t color); /* drect(): Fill a rectangle of the screen This functions applies a color or an operator to a rectangle defined by two points (x1 y1) and (x2 y2). Both are included in the rectangle. - @x1 @y1 @x2 @y2 Bounding rectangle (drawn area). - @color fx9860g: white, black, none, invert - fxcg50: Any R5G6B5 color */ -void drect(int x1, int y1, int x2, int y2, color_t color); + @x1 @y1 Top-left rectangle corner (included) + @x2 @y2 Bottom-right rectangle corner (included) + @color fx-9860G: white light* dark* black none invert lighten* darken* + fx-CG 50: Any R5G6B5 color or C_NONE + + *: When the gray engine is on, see dgray(). */ +void drect(int x1, int y1, int x2, int y2, int color); /* drect_border(): Rectangle with border This function draws a rectangle with an inner border. The border width must - be smaller than half the width and half the height. */ -void drect_border(int x1, int y1, int x2, int y2, int fill_color, - int border_width, int border_color); + be smaller than half the width and half the height. + + @x1 @y1 @x2 @y2 Top-left and bottom-right rectangle corners (included) + @fill_color Center color (same values as drect() are allowed) + @border_width Amount of pixels reserved for the border on each side + @border_color Border color (same values as drect() are allowed) */ +void drect_border(int x1, int y1, int x2, int y2, + int fill_color, int border_width, int border_color); //--- // Point drawing functions //--- /* dpixel(): Change a pixel's color - Paints the selected pixel with an opaque color. Setting pixels individually - is a slow method for rendering. Other functions that draw lines, rectangles, - images or text will take advantage of possible optimizations to make the - rendering faster: check them out first. - On fx9860g, if an operator such as invert is used, the result will depend + Paints the selected pixel with an opaque color or applies an operator to a + pixel. Setting pixels individually is a slow method for rendering. Other + functions that draw lines, rectangles, images or text will take advantage of + possible optimizations to make the rendering faster, so prefer using them + when they apply. + + On fx-9860G, if an operator such as invert is used, the result will depend on the current color of the pixel. @x @y Coordinates of the pixel to repaint - @color fx9860g: white, black, none, invert - fxcg50: Any R5G6B5 color */ -void dpixel(int x, int y, color_t color); + @color fx-9860G: white light* dark* black none invert lighten* darken* + fx-CG 50: Any R5G6B5 color or C_NONE + + *: When the gray engine is on, see dgray(). */ +void dpixel(int x, int y, int color); /* dline(): Render a straight line + This function draws a line using a Bresenham-style algorithm. Please note that dline() may not render lines exactly like Bdisp_DrawLineVRAM(). - dline() has optimization facilities for horizontal and vertical lines. The - speedup for horizontal lines is about x2 on fxcg50 and probably about x30 - on fx9860g. Vertical lines have a smaller speedup. + dline() has optimizations for horizontal and vertical lines. The speedup for + horizontal lines is about x2 on fx-CG 50 and probably about x30 on fx-9860G. + Vertical lines have a smaller speedup. dline() is currently not able to clip arbitrary lines without calculating all the pixels, so drawing a line from (-1e6,0) to (1e6,395) will work but - will be veeery slow. + will be very slow. - @x1 @y1 @x2 @y2 End points of the line (both included). - @color fx9860g: white, black, none, invert - fxcg50: Any R5G6B5 color */ -void dline(int x1, int y1, int x2, int y2, color_t color); + @x1 @y1 @x2 @y2 Endpoints of the line (both included). + @color Line color (same values as dpixel() are allowed) */ +void dline(int x1, int y1, int x2, int y2, int color); /* dhline(): Full-width horizontal line This function draws a horizontal line from the left end of the screen to the right end, much like the Basic command "Horizontal". @y Line number - @color fx9860g: white, black, none, invert - fxcg50: Any R5G6B5 color */ -void dhline(int y, color_t color); + @color Line color (same values as dline() are allowed) */ +void dhline(int y, int color); /* dvline(): Full-height vertical line This function draws a vertical line from the top end of the screen to the bottom end, much like the Basic command "Vertical". @x Column number - @color fx9860g: white, black, none, invert - fxcg50: Any R5G6B5 color */ -void dvline(int x, color_t color); + @color Line color (same values as dline() are allowed) */ +void dvline(int x, int color); //--- // Text rendering (topti) //--- -/* font_t - font data encoded for topti */ +/* font_t: Font data encoded for topti */ typedef struct { /* Length of font name (not necessarily NUL-terminated!) */ @@ -172,17 +213,28 @@ typedef struct } GPACKED(4) font_t; /* dfont(): Set the default font for text rendering - This font will be used by dtext() and sister functions. If [font = NULL], - gint's default font is used. - On fx9860g, the default font is a 5x7 font very close to the system's. - On fxcg50, the default font is an original 8x9 font. + This function changes the default font for text rendering; this affects + dtext_opt(), dtext() and the related functions. If the specified font is + NULL, gint's default font is used instead. + + On fx-9860G, the default font is a 5x7 font very close to the system's. + On fx-CG 50, the default font is an original, proportional 8x9 font. + + This function returns the previously configured font. Normally you want to + restore it after you're done so as to not affect ambiant calls to text + rendering functions. It would look like this: + + font_t const *old_font = dfont(new_font); + // Do the text rendering... + dfont(old_font); @font Font to use for subsequent text rendering calls Returns the previously configured font. */ -font_t const *dfont(font_t const * font); +font_t const *dfont(font_t const *font); /* dsize(): Get the width and height of rendered text + This function computes the size that the given string would take up if rendered with a certain font. If you specify a NULL font, the currently configured font will be used; this is different from dfont(), which uses @@ -191,15 +243,15 @@ font_t const *dfont(font_t const * font); Note that the height of each glyph is not stored in the font, only the maximum. Usually this is what you want because vertically-centered strings must have the same baseline regardless of their contents. So the height - returned by dsize() is the same for all strings, only depends on the font. + set by dsize() is the same for all strings and only depends on the font. The height is computed in constant time, and the width in linear time. If - [w = NULL], this function returns in constant time. + the third argument is NULL, this function returns in constant time. @str String whose size must be evaluated @font Font to use; if NULL, defaults to the current font @w @h Set to the width and height of the rendered text, may be NULL */ -void dsize(char const *str, font_t const * font, int *w, int *h); +void dsize(char const *str, font_t const *font, int *w, int *h); /* Alignment settings for dtext_opt() and dprint_opt(). Combining a vertical and a horizontal alignment option specifies where a given point (x,y) should @@ -221,61 +273,66 @@ enum { default if no such font was set). This function has a lot of parameters, see dtext() for a simpler version. - On fx9860g, due to the particular design of topti, this function performs - drastic rendering optimizations using the line structure of the VRAM and is - able to render several characters at once. + The alignment options specify where (x y) should be relative to the rendered + string. The default is halign=DTEXT_LEFT and valign=DTEXT_TOP, which means + that (x y) is the top-left corder of the rendered string. The different + combinations of settings provide 9 positions of (x y) to choose from. - This is not a printf()-family function so [str] cannot contain formats like + On fx-9860G, due to the particular design of topti, this function performs + drastic rendering optimizations using the line structure of the VRAM and + renders 5 or 6 characters simultaneously. + + This is not a printf()-family function so str cannot contain formats like "%d" and you cannot pass additional arguments. See dprint_opt() and dprint() for that. - @x @y Coordinates of top-left corner of the rendered string - @fg Text color - fx9860g: white, black, none, invert - fxcg50: Any R5G6B6 color, or C_NONE - @bg Background color - fx9860g: white, black, none, invert - fxcg50: Any R5G6B5 color, or C_NONE - @halign Where x should be relative to the rendered string (see above enum) - @valign Where y should be relative to the rendered string (see above enum) + @x @y Coordinates of the anchor of the rendered string + @fg @bg Text color and background color + fx-9860G: white light* dark* black none invert lighten* darken* + fx-CG 50: Any R5G6B6 color, or C_NONE + @halign Where x should be relative to the rendered string + @valign Where y should be relative to the rendered string @str String to display */ void dtext_opt(int x, int y, int fg, int bg, int halign, int valign, char const *str); /* dtext(): Simple version of dtext_opt() with defaults - This is exactly dtext_opt() with bg=C_NONE, halign=DTEXT_LEFT and - valign=DTEXT_TOP. */ + Calls dtext_opt() with bg=C_NONE, halign=DTEXT_LEFT and valign=DTEXT_TOP. */ void dtext(int x, int y, int fg, char const *str); /* dprint_opt(): Display a formatted string - Much like dtext_opt(), but accepts printf-like formats with arguments. See - for a detailed view of what this format supports. */ + + This function is exactly like dtext_opt(), but accepts printf-like formats + with arguments. See for a detailed view of what this + format supports. For example: + + dprint_opt(x, y, fg, bg, halign, valign, "A=%d B=%d", A, B); */ void dprint_opt(int x, int y, int fg, int bg, int halign, int valign, char const *format, ...); /* dprint(): Simple version of dprint_op() with defaults - Like dtext() with formatted printing. */ + Calls dprint_opt() with bg=C_NONE, halign=DTEXT_LEFT and valign=DTEXT_TOP */ void dprint(int x, int y, int fg, char const *format, ...); //--- // Image rendering (bopti) //--- -/* The bopti_image_t structure is platform-dependent. */ +/* The bopti_image_t structure is platform-dependent, see + and if you're curious. */ /* dimage(): Render a full image This function blits an image on the VRAM using gint's special format. It is a special case of dsubimage() where the full image is drawn with clipping. @x @y Coordinates of the top-left corner of the image - @image Pointer to image encoded with [fxconv] */ + @image Pointer to image encoded with fxconv for bopti */ void dimage(int x, int y, bopti_image_t const *image); /* Option values for dsubimage() */ enum { /* No option */ DIMAGE_NONE = 0x00, - /* Disable clipping, ie. adjustments to the specified subrectangle and screen location such that any part that overflows from the image or the screen is ignored. Slightly faster. */ @@ -287,23 +344,11 @@ enum { the VRAM. It is more general than dimage() and also provides a few options. @x @y Coordinates on screen of the rendered subrectangle - @image Pointer to image encoded with [fxconv] - @left @top Top-left coordinates of the subrectangle within [image] + @image Pointer to image encoded with fxconv for bopti + @left @top Top-left coordinates of the subrectangle within the image @width @height Subrectangle dimensions @flags OR-combination of DIMAGE_* flags */ void dsubimage(int x, int y, bopti_image_t const *image, int left, int top, int width, int height, int flags); -//--- -// Advanced functions -//--- - -/* dupdate_noint(): Push VRAM to the display without interrupts - This function does exactly as dupdate(), but does not use interrupts and - always returns when the transfer is finished. It actively waits for the end - of the transfer and is thus bad for any general-purpose use. In fact, it is - only needed to display panic messages in exception handlers. Don't use it - unless you know precisely why you're doing it. */ -void dupdate_noint(void); - #endif /* GINT_DISPLAY */ diff --git a/include/gint/gray.h b/include/gint/gray.h index 8cb2331..d488fd4 100644 --- a/include/gint/gray.h +++ b/include/gint/gray.h @@ -1,5 +1,5 @@ //--- -// gint:gray - Gray engine and rendering functions +// gint:gray - Gray engine control //--- #ifndef GINT_GRAY @@ -8,34 +8,71 @@ #include #include -//--- -// Engine control -//--- +/* Commands for the gray engine */ +enum { + /* Start or stop the engine */ + DGRAY_ON, + DGRAY_OFF, + /* Start or stop the engine, but remember previous state */ + DGRAY_PUSH_ON, + DGRAY_PUSH_OFF, + /* Restore previous state remembered by DGRAY_PUSH_* */ + DGRAY_POP, +}; -/* gray_start(): Start the gray engine +/* dgray(): Start or stop the gray engine at the next dupdate() - The control of the screen is transferred to the engine; you should not use - dupdate() after this function, only gupdate(). + This function configures the display module to work with or without the gray + engine. When the mode is set to DGRAY_ON, all rendering functions are + replaced to use gray rendering, and gray frames will start being displayed + after the next dupdate(). To transition from monochrome rendering to gray + rendering, you must first call dgray(DGRAY_ON), then draw your first gray + frame from scratch, then call dupdate(). - The engine uses timer number GRAY_TIMER (equal to 0 by default) to run. If - the timer is unavailable, this function will fail. Otherwise, the gray - engine is started and any attempt to use timer 0 while it is running will be - denied by the timer module. */ -void gray_start(void); + Similarly, when the mode is DGRAY_OFF, all rendering functions are replaced + to use monochrome rendering, and the next frame sent with dupdate() will + stop the gray engine on the display. To transition back to monochrome + rendering, call dgray(DGRAY_OFF), draw your first monochrome frame from + scratch, then call dupdate(). -/* gray_stop(): Stop the gray engine + The gray engine uses the timer id GRAY_TIMER (which is 0) to run and needs 3 + additional VRAMs (totaling 4), obtained on the heap by default. Timer 0 is + chosen for its precision and high priority. If GRAY_TIMER is unavailable or + VRAMs cannot be obtained with malloc(), the gray engine will fail to start + and this function will return a non-zero error code. - Safe to call if the engine was not running. The gray engine returns the - control of the screen and dupdate() is safe to use again. + When functions that use the gray engine call functions that don't and vice- + versa, it can be tedious to know exacly when the gray engine should be + running. This function provides a stack of gray engine states to solve this + problem. When entering a function that uses/doesn't use the gray engine, you + can call dgray(DGRAY_PUSH_ON)/dgray(DGRAY_PUSH_OFF) to start/stop the + engine; then call dgray(DGRAY_POP) at the end of the function. + dgray(DGRAY_POP) will restore the state of the engine as it was before the + previous DGRAY_PUSH_*. This allows each function to set its own state + without altering the state of their callers. - This function will leave the screen in whatever state it is, which is most - probably one of the gray buffers being displayed. You should dupdate() - quickly after this call to avoid visual artifacts. If the next monochrome - frame is slow to render, consider rendering it before stopping the gray - engine, and calling dupdate() immediately after. */ -void gray_stop(void); + This function returns non-zero if the engine has failed to initialize at + startup (because it failed to obtain either GRAY_TIMER or some of its VRAM + memory). Because changes to the gray engine only apply after a call to + dupdate(), you can check if the engine is successfully initialized with: -/* gray_delays(): Set the gray engine delays + int engine_successfully_initialized = dgray(DGRAY_ON); + dgray(DGRAY_OFF); + + @mode DGRAY_ON or DGRAY_PUSH_ON to turn the engine on; + DGRAY_OFF or DGRAY_PUSH_OFF to turn the engine off; + DGRAY_POP to restore the state as before the last DGRAY_PUSH_*. + + Returns 0 on success and non-zero if the engine has failed to initialize. */ +int dgray(int mode); + +/* dgray_enabled(): Check whether gray mode is enabled + Returns non-zero if gray mode is enabled, that is if the gray engine is + planning to run at the next call to dupdate(). (This is true between the + calls to dgray(DGRAY_ON) and dgray(DGRAY_OFF).) */ +int dgray_enabled(void); + +/* dgray_setdelays(): Set the gray engine delays The gray engine works by swapping two images at a fast pace. Pixels that are white on both or black or both will appear as such, but pixels that are @@ -59,164 +96,83 @@ void gray_stop(void); and speed; but overall they're the *one* thing you want to avoid. * And the color of the shades themselves. - Here are values from an older version of gint, which may not look good now - but provide bases to search for good settings: + Here are values from the old fx-9860G-like family, which is every monochrome + calculator but the Graph 35+E II: LIGHT DARK BLINKING STRIPES COLORS -------------------------------------------------- - 860 1298 none terrible decent - 912 1343 heavy none good - 993 1609 medium light decent - 1325 1607 heavy light excellent + 898 1350 none common good + 1075 1444 low common too close + 609 884 medium terrible variable + 937 1333 low common decent 923 1742 none scanline good [default] -------------------------------------------------- - Here are values for this version of gint (for the Graph 35+E II only): + Here are values for the Graph 35+E II only: LIGHT DARK BLINKING STRIPES COLORS -------------------------------------------------- - 680 1078 - 762 1311 low some too light [default] - 869 1097 medium some excellent + 680 1078 low common good + 762 1311 low some good [default] + 869 1097 medium some too close 869 1311 medium none good - 937 1425 medium none good + 937 1425 none bad good -------------------------------------------------- + This function sets the delays of the gray engine. It is safe to call while + the engine is running, although for best visual effects it is better to call + it prior to dgray(GRAY_ON). + @light New light delay @dark New dark delay */ -void gray_delays(uint32_t light, uint32_t dark); +void dgray_setdelays(uint32_t light, uint32_t dark); -/* gray_config(): Get the current configuration of the engine +/* dgray_getdelays(): Get the gray engine delays Provides the value of the current light and dark delays, measured in timer ticks of prescaler P_phi/64. See on how to obtain this value. Both pointers may be NULL. @light Set to the current light delay setting - @dark Set to the current dark delay setting - Returns non-zero if the engine is currently running, 0 otherwise. */ -int gray_config(uint32_t *light, uint32_t *dark); - -//--- -// Area rendering functions -//--- - -/* gclear(): Fill the screen with a single color - Clears the VRAM and paints all the pixels in the same color. Optimized for - opaque colors; use grect() for other colors. - - @color white, light, dark, black */ -void gclear(color_t color); - -/* grect(): Fill a rectangle on the screen - Applies a color or an operator to the rectangle enclosed by (x1 y1) and - (x2 y2), both included. - - @x1 @x2 @y1 @y2 Bounding rectangle - @color white, light, dark, black, none, invert, lighten, darken */ -void grect(int x1, int y1, int x2, int y2, color_t color); - -//--- -// Point drawing functions -//--- - -/* gpixel(): Change a pixel's color - Paints the specified pixel. Use lines or area rendering when possible - because painting pixels individually is slow. - - @x @y Coordinates of the pixel to paint - @color white, light, dark, black, none, invert, lighten, darken */ -void gpixel(int x, int y, color_t color); - -/* gline(): Render a straight line - Draws a line without anti-aliasing, using a Bresenham-style algorithm. Much - like dline(), has optimizations for vertical and horizontal lines but is not - able to handle clipping. - - @x1 @y1 @x2 @y2 End points of the line (both included) - @color white, light, dark, black, none, invert, lighten, darken */ -void gline(int x1, int y1, int x2, int y2, color_t color); - -/* ghline(): Full-width horizontal line - @y Line number - @color white, light, dark, black, none, invert, lighten, darken */ -void ghline(int y, color_t color); - -/* gvline(): Full-height vertical line - @x Column number - @color white, light, dark, black, none, invert, lighten, darken */ -void gvline(int x, color_t color); - -//--- -// Text rendering -//--- - -/* dfont() and dsize() still work with the gray engine. */ - -/* gtext(): Display a string of text - Exactly like dtext(), except that the rendering is done on the two gray - VRAMs and all colors are supported. - - @x @y Coordinates of top-left corner of the rendered string - @str String to display - @fg Foreground: white, light, dark, black, none, invert, lighten, darken - @bg Background, same colors as fg */ -void gtext(int x, int y, const char *str, int fg, int bg); - -//--- -// Image rendering -//--- - -/* gimage(): Render a full image - This function is exactly like dimage(), but it draws gray image instead. - - @x @y Coordinates of the top-left corner of the image - @image Pointer to gray image encoded with [fxconv] */ -void gimage(int x, int y, bopti_image_t const *image); - -/* gsubimage(): Render a section of an image - Like dsubimage() for gray images. Same options apply. - - @x @y Coordinates on screen of the rendered subrectangle - @image Pointer to image encoded with [fxconv] - @left @top Top-left coordinates of the subrectangle within [image] - @width @height Subrectangle dimensions - @flags OR-combination of DIMAGE_* flags */ -void gsubimage(int x, int y, bopti_image_t const *image, int left, int top, - int width, int height, int flags); + @dark Set to the current dark delay setting */ +void dgray_getdelays(uint32_t *light, uint32_t *dark); //--- // VRAM management //--- -/* gupdate(): Push the current VRAMs to the screen +/* dgray_getvram(): Get the current VRAM pointers + These pointers can be used for custom rendering functions. */ +void dgray_getvram(uint32_t **light, uint32_t **dark); - This function swaps pairs of VRAM buffers in the gray engine so that the - gray and light VRAMs that were being drawn to are now available to be - displayed on the screen at the next refresh, while the VRAMs that were - previously being displayed are now available for drawing. +//--- +// Aliases for older programs +//--- - Unlike dupdate(), gupdate() does not interact with the screen as this - interaction is done very frequently by the engine itself. It performs an - atomic operation to swap buffers, but nothing on the screen will change - until the engine's timer actually fires. */ -void gupdate(void); +/* Functions for the gray engine used to be called g*() to mirror the d*() + functions of the display module. They are now bundled together, so these + aliases are for older programs that still use the g*() naming scheme */ +#ifdef GINT_GRAY_ALIASES -/* gvram(): Get the current VRAM pointers +#define gclear dclear +#define grect drect +#define gpixel dpixel +#define gline dline +#define ghline dhline +#define gvline dvline +#define gimage dimage +#define gsubimage dsubimage +#define gtext dtext +#define gtext_opt dtext_opt +#define gprint dprint +#define gprint_opt dprint_opt - This function stores the current VRAM pointers to the specified locations. - This operation needs to be done atomically because the engine might kick in - and sway buffers at any time; calling gvraml() then gvramd() is unsafe. +#define gupdate dupdate +#define gray_delays dgray_setdelays +#define gray_config dgray_getdelays +#define gvram dgray_getvram - @light Set to the current light VRAM pointer - @dark Set to the current dark VRAM pointer */ -void gvram(uint32_t **light, uint32_t **dark); - -/* gvraml(): Shorthand to retrieve the current light VRAM pointer */ -uint32_t *gvraml(void); - -/* gvramd(): Shorthand to retrieve the current dark VRAM pointer */ -uint32_t *gvramd(void); +#endif /* GINT_GRAY_ALIASES */ #endif /* GINT_GRAY */ diff --git a/src/gray/engine.c b/src/gray/engine.c index f61da05..42d6c9a 100644 --- a/src/gray/engine.c +++ b/src/gray/engine.c @@ -10,6 +10,8 @@ #include #include +#include + /* Three additional video RAMS, allocated statically if --static-gray was set at configure time, or with malloc() otherwise. */ #ifdef GINT_STATIC_GRAY @@ -17,27 +19,63 @@ GBSS static uint32_t gvrams[3][256]; #endif /* Four VRAMs: two to draw and two to display */ -GBSS static uint32_t *vrams[4]; +static uint32_t *vrams[4] = { NULL, NULL, NULL, NULL }; /* Current VRAM pair used for drawing; the value can either be 0 (draws to VRAMs 0 and 1) or 2 (draws to VRAMs 2 and 3). */ -static volatile int st = 0; - -/* Whether the engine is running. Delays of light and dark frames. */ +static int volatile st = 0; +/* Timer ID, always equal to GRAY_TIMER except if initialization fails */ +static int timer = -1; +/* Whether the engine is scheduled to run at the next frame */ static int runs = 0; + +/* Underlying timer, set to count at P_phi/64 */ +#define GRAY_TIMER 0 +#define GRAY_CLOCK TIMER_Pphi_64 +/* Delays of the light and dark frames for the above setting */ GBSS static int delays[2]; -/* Underlying timer */ -#define GRAY_TIMER 0 +/* The alternate rendering mode structure used to override d*() */ +static struct rendering_mode const gray_mode = { + .dupdate = gupdate, + .dclear = gclear, + .drect = grect, + .dpixel = gpixel, + .gint_dhline = gint_ghline, + .gint_dvline = gint_gvline, + .dtext_opt = gtext_opt, + .dsubimage = gsubimage, +}; +static struct rendering_mode const gray_exit_mode = { + .dupdate = gupdate, + .dclear = NULL, + .drect = NULL, + .dpixel = NULL, + .gint_dhline = NULL, + .gint_dvline = NULL, + .dtext_opt = NULL, + .dsubimage = NULL, +}; //--- -// Engine control +// Engine control (init/quit and start/stop) //--- -/* gray_init(): Engine setup */ +static int gray_int(void); +static void gray_quit(void); + +/* gray_isinit(): Check whether the engine is initialized and ready to run */ +static int gray_isinit(void) +{ + return (vrams[0] && vrams[1] && vrams[2] && vrams[3] && timer >= 0); +} + +/* gray_init(): Initialize the engine + This is done at startup so that memory can be reserved very early from the + heap (because not having enough memory is unrecoverable for the engine). */ GCONSTRUCTOR static void gray_init(void) { - /* Here [gint_vram] refers to the standard, monochrome VRAM */ + /* We need four VRAMs. First use the standard monochrome one */ vrams[0] = gint_vram; #ifdef GINT_STATIC_GRAY @@ -61,16 +99,74 @@ GCONSTRUCTOR static void gray_init(void) delays[0] = 923; delays[1] = 1742; } + + /* Try to obtain the timer right away */ + timer = timer_setup(GRAY_TIMER | GRAY_CLOCK, 1000, gray_int); + + /* On failure, release the resources that we obtained */ + if(!gray_isinit()) gray_quit(); } -/* gray_quit(): Engine deinitialization() */ +/* gray_quit(): Free engine resources */ GDESTRUCTOR static void gray_quit(void) { #ifndef GINT_STATIC_GRAY - free(vrams[1]); - free(vrams[2]); - free(vrams[3]); + if(vrams[1]) free(vrams[1]); + if(vrams[2]) free(vrams[2]); + if(vrams[3]) free(vrams[3]); + + vrams[1] = NULL; + vrams[2] = NULL; + vrams[3] = NULL; #endif /* GINT_STATIC_GRAY */ + + if(timer >= 0) timer_stop(timer); + timer = -1; +} + +/* gray_start(): Start the gray engine */ +static void gray_start(void) +{ + st = 2; + timer_reload(GRAY_TIMER, delays[0]); + timer_start(GRAY_TIMER); + runs = 1; +} + +/* gray_stop(): Stop the gray engine */ +static void gray_stop(void) +{ + timer_pause(GRAY_TIMER); + runs = 0; + st = 0; +} + +//--- +// Dynamic udpate and rendering mode +//--- + +/* dgray(): Start or stop the gray engine at the next dupdate() */ +int dgray(int mode) +{ + if(mode == DGRAY_ON) + { + if(!gray_isinit()) return 1; + + /* Set the display module's alternate rendering mode to + override rendering functions to use their g*() variant */ + dmode = &gray_mode; + return 0; + } + else if(mode == DGRAY_OFF) + { + /* Set the mode to a temporary one that only overrides + dupdate() so that we can stop the engine next frame */ + if(dmode == &gray_mode) dmode = &gray_exit_mode; + return 0; + } + + /* TODO: DGRAY_PUSH_* and DGRAY_POP */ + return 2; } /* gray_int(): Interrupt handler */ @@ -83,69 +179,57 @@ int gray_int(void) return TIMER_CONTINUE; } -/* gray_start(): Start the gray engine */ -void gray_start(void) +/* gupdate(): Push the current VRAMs to the screen */ +int gupdate(void) { - #ifndef GINT_STATIC_GRAY - if(!vrams[1] || !vrams[2] || !vrams[3]) return; - #endif + /* At the first gupdate(), start the engine */ + if(dmode == &gray_mode && !runs) + { + gray_start(); + return 0; + } + /* At the last gupdate(), stop the engine */ + if(dmode == &gray_exit_mode) + { + gray_stop(); + dmode = NULL; + return 1; + } - if(runs) return; - - int timer = timer_setup(GRAY_TIMER|TIMER_Pphi_64, delays[0], gray_int); - if(timer != GRAY_TIMER) return; - - timer_start(GRAY_TIMER); - st = 0; - runs = 1; + /* When the engine is running, swap frames */ + st ^= 2; + return 0; } -/* gray_stop(): Stop the gray engine */ -void gray_stop(void) +//--- +// Query and configuration functions +//--- + +/* dgray_enabled(): Check whether gray mode is enabled */ +int dgray_enabled(void) { - timer_stop(GRAY_TIMER); - runs = 0; + return (dmode == &gray_mode); } -/* gray_delays(): Set the gray engine delays */ -void gray_delays(uint32_t light, uint32_t dark) +/* dgray_setdelays(): Set the gray engine delays */ +void dgray_setdelays(uint32_t light, uint32_t dark) { delays[0] = light; delays[1] = dark; } -/* gray_config(): Get the current configuration of the engine */ -int gray_config(uint32_t *light, uint32_t *dark) +/* dgray_getdelays(): Get the gray engine delays */ +void dgray_getdelays(uint32_t *light, uint32_t *dark) { if(light) *light = delays[0]; if(dark) *dark = delays[1]; - - return runs; } -/* gupdate(): Push the current VRAMs to the screen */ -void gupdate(void) -{ - st ^= 2; -} - -/* gvram(): Get the current VRAM pointers */ -void gvram(uint32_t **light, uint32_t **dark) +/* dgray_getvram(): Get the current VRAM pointers */ +void dgray_getvram(uint32_t **light, uint32_t **dark) { int base = st; if(light) *light = vrams[base & 2]; if(dark) *dark = vrams[base | 1]; } - -/* gvraml(): Shorthand to retrieve the current light VRAM pointer */ -uint32_t *gvraml(void) -{ - return vrams[st & 2]; -} - -/* gvramd(): Shorthand to retrieve the current dark VRAM pointer */ -uint32_t *gvramd(void) -{ - return vrams[st | 1]; -} diff --git a/src/gray/gclear.c b/src/gray/gclear.c index 3559ff0..ba7420e 100644 --- a/src/gray/gclear.c +++ b/src/gray/gclear.c @@ -4,7 +4,7 @@ void gclear(color_t color) { uint32_t *light, *dark; - gvram(&light, &dark); + dgray_getvram(&light, &dark); uint32_t l = -(color == C_LIGHT || color == C_BLACK); uint32_t d = -(color == C_DARK || color == C_BLACK); diff --git a/src/gray/ghline.c b/src/gray/ghline.c deleted file mode 100644 index 5d6a15c..0000000 --- a/src/gray/ghline.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -/* ghline(): Full-width horizontal line */ -void ghline(int y, color_t color) -{ - gline(0, y, 127, y, color); -} diff --git a/src/gray/gint_gline.c b/src/gray/gint_gline.c new file mode 100644 index 0000000..75473d7 --- /dev/null +++ b/src/gray/gint_gline.c @@ -0,0 +1,16 @@ +#include +#include + +/* gint_ghline(): Optimized horizontal line, but not actually optimized */ +void gint_ghline(int x1, int x2, int y, int color) +{ + if(x1 > x2) swap(x1, x2); + for(int x = x1; x <= x2; x++) dpixel(x, y, color); +} + +/* gint_gvline(): Optimized horizontal line, but not actually optimized */ +void gint_gvline(int y1, int y2, int x, int color) +{ + if(y1 > y2) swap(y1, y2); + for(int y = y1; y <= y2; y++) dpixel(x, y, color); +} diff --git a/src/gray/gline.c b/src/gray/gline.c deleted file mode 100644 index 4714204..0000000 --- a/src/gray/gline.c +++ /dev/null @@ -1,55 +0,0 @@ -#include -#include -#include - -/* gline(): Bresenham line drawing algorithm - Remotely adapted from MonochromeLib code by Pierre "PerriotLL" Le Gall. - Relies on grect() for optimized situations. - @x1 @y1 @x2 @y2 Coordinates of endpoints of line (included) - @color Any color */ -void gline(int x1, int y1, int x2, int y2, color_t color) -{ - /* Trivial optimizations */ - if(x1 == x2 || y1 == y2) - { - grect(x1, y1, x2, y2, color); - return; - } - - /* Brensenham line drawing algorithm */ - - int i, x = x1, y = y1, cumul; - int dx = x2 - x1, dy = y2 - y1; - int sx = sgn(dx), sy = sgn(dy); - - dx = abs(dx), dy = abs(dy); - - gpixel(x1, y1, color); - - if(dx >= dy) - { - /* Start with a non-zero cumul to even the overdue between the - two ends of the line (for more regularity) */ - cumul = dx >> 1; - for(i = 1; i < dx; i++) - { - x += sx; - cumul += dy; - if(cumul > dx) cumul -= dx, y += sy; - gpixel(x, y, color); - } - } - else - { - cumul = dy >> 1; - for(i = 1; i < dy; i++) - { - y += sy; - cumul += dx; - if(cumul > dy) cumul -= dy, x += sx; - gpixel(x, y, color); - } - } - - gpixel(x2, y2, color); -} diff --git a/src/gray/gpixel.c b/src/gray/gpixel.c index 87af2a4..a3e0750 100644 --- a/src/gray/gpixel.c +++ b/src/gray/gpixel.c @@ -4,10 +4,8 @@ /* gpixel(): Change a pixel's color */ void gpixel(int x, int y, color_t color) { - if((uint)x >= 128 || (uint)y >= 64) return; - uint32_t *light, *dark; - gvram(&light, &dark); + dgray_getvram(&light, &dark); int offset = (y << 2) + (x >> 5); uint32_t mask = 1 << (~x & 31), tmp; diff --git a/src/gray/grect.c b/src/gray/grect.c index 5eb9cd3..f9d4fe5 100644 --- a/src/gray/grect.c +++ b/src/gray/grect.c @@ -5,22 +5,12 @@ /* grect(): Fill a rectangle on the screen */ void grect(int x1, int y1, int x2, int y2, color_t color) { - if(x1 > x2) swap(x1, x2); - if(y1 > y2) swap(y1, y2); - - /* Argument checking */ - if(x1 >= 128 || x2 < 0 || y1 >= 64 || y2 < 0) return; - if(x1 < 0) x1 = 0; - if(x2 >= 128) x2 = 127; - if(y1 < 0) y1 = 0; - if(y2 >= 64) y2 = 63; - /* Use masks to get the work done fast! */ uint32_t m[4]; masks(x1, x2, m); uint32_t *light, *dark; - gvram(&light, &dark); + dgray_getvram(&light, &dark); light += (y1 << 2); dark += (y1 << 2); diff --git a/src/gray/gimage.c b/src/gray/gsubimage.c similarity index 71% rename from src/gray/gimage.c rename to src/gray/gsubimage.c index 82e567d..b5aee3d 100644 --- a/src/gray/gimage.c +++ b/src/gray/gsubimage.c @@ -10,22 +10,12 @@ static void *bopti_asm[] = { bopti_gasm_gray_alpha, }; -/* gimage(): Render a full image */ -void gimage(int x, int y, bopti_image_t const *img) -{ - uint32_t *light, *dark; - gvram(&light, &dark); - - bopti_render_clip(x, y, img, 0, 0, img->width, img->height, light, - dark, bopti_asm[img->profile]); -} - /* gsubimage(): Render a section of an image */ void gsubimage(int x, int y, bopti_image_t const *img, int left, int top, int width, int height, int flags) { uint32_t *light, *dark; - gvram(&light, &dark); + dgray_getvram(&light, &dark); if(flags & DIMAGE_NOCLIP) { diff --git a/src/gray/gtext.c b/src/gray/gtext.c index 1741600..0211bf2 100644 --- a/src/gray/gtext.c +++ b/src/gray/gtext.c @@ -2,13 +2,23 @@ #include #include "../render-fx/topti-asm.h" -/* gtext(): Display a string of text */ -void gtext(int x, int y, const char *str, int fg, int bg) +/* gtext_opt(): Display a string of text */ +void gtext_opt(int x, int y, int fg, int bg, int halign, int valign, + char const *str) { - if((uint)fg >= 8 || (uint)bg >= 8) return; - uint32_t *light, *dark; - gvram(&light, &dark); + dgray_getvram(&light, &dark); + + if(halign != DTEXT_LEFT || valign != DTEXT_TOP) + { + int w, h; + dsize(str, topti_font, &w, &h); + + if(halign == DTEXT_RIGHT) x -= w; + if(halign == DTEXT_CENTER) x -= ((w+1) >> 1); + if(valign == DTEXT_BOTTOM) y -= h; + if(valign == DTEXT_MIDDLE) y -= ((h+1) >> 1); + } topti_render(x, y, str, topti_font, topti_asm_text[fg], topti_asm_text[bg], light, dark); diff --git a/src/gray/gvline.c b/src/gray/gvline.c deleted file mode 100644 index 6e298ab..0000000 --- a/src/gray/gvline.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -/* gvline(): Full-height vertical line */ -void gvline(int x, color_t color) -{ - gline(x, 0, x, 63, color); -} diff --git a/src/render-cg/dhline.c b/src/render-cg/dhline.c deleted file mode 100644 index a7a88da..0000000 --- a/src/render-cg/dhline.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -/* dhline(): Full-width horizontal line */ -void dhline(int y, color_t color) -{ - dline(0, y, 395, y, color); -} diff --git a/src/render-cg/dpixel.c b/src/render-cg/dpixel.c index 0dea647..34b40de 100644 --- a/src/render-cg/dpixel.c +++ b/src/render-cg/dpixel.c @@ -1,10 +1,10 @@ #include /* dpixel() - change a pixel's color */ -void dpixel(int x, int y, uint16_t color) +void dpixel(int x, int y, int color) { /* Coordinate checks */ - if((uint)x >= 396 || (uint)y >= 224) return; + if((uint)x >= 396 || (uint)y >= 224 || color == C_NONE) return; gint_vram[396 * y + x] = color; } diff --git a/src/render-cg/drect.c b/src/render-cg/drect.c index 4307cf5..7556082 100644 --- a/src/render-cg/drect.c +++ b/src/render-cg/drect.c @@ -2,8 +2,10 @@ #include /* drect() - fill a rectangle of the screen */ -void drect(int x1, int y1, int x2, int y2, uint16_t color) +void drect(int x1, int y1, int x2, int y2, int color) { + if(color == C_NONE) return; + if(x1 > x2) swap(x1, x2); if(y1 > y2) swap(y1, y2); diff --git a/src/render-cg/dimage.c b/src/render-cg/dsubimage.c similarity index 61% rename from src/render-cg/dimage.c rename to src/render-cg/dsubimage.c index 2883153..e3cf3f1 100644 --- a/src/render-cg/dimage.c +++ b/src/render-cg/dsubimage.c @@ -1,13 +1,7 @@ #include #include "bopti-asm.h" -/* dimage() - render a full image */ -void dimage(int x, int y, bopti_image_t const *img) -{ - bopti_render_clip(x, y, img, 0, 0, img->width, img->height); -} - -/* dsubimage() - render a section of an image */ +/* dsubimage(): Render a section of an image */ void dsubimage(int x, int y, bopti_image_t const *img, int left, int top, int width, int height, int flags) { diff --git a/src/render-cg/dupdate.c b/src/render-cg/dupdate.c index 282fd94..57aebdf 100644 --- a/src/render-cg/dupdate.c +++ b/src/render-cg/dupdate.c @@ -11,9 +11,3 @@ void dupdate(void) overwriting the data which is about to be sent. */ dvram_switch(); } - -/* dupdate_noint() - Push VRAM to the display without interrupts */ -void dupdate_noint(void) -{ - r61524_display(gint_vram, 0, 224, R61524_DMA_WAIT); -} diff --git a/src/render-cg/dline.c b/src/render-cg/gint_dline.c similarity index 100% rename from src/render-cg/dline.c rename to src/render-cg/gint_dline.c diff --git a/src/render-cg/topti.c b/src/render-cg/topti.c index e1d699a..ea7bda2 100644 --- a/src/render-cg/topti.c +++ b/src/render-cg/topti.c @@ -133,9 +133,3 @@ void dtext_opt(int x, int y, int fg, int bg, int halign, int valign, topti_render(x, y, str, strlen(str), topti_font, fg, bg); } - -/* dtext(): Simple version of dtext_opt() with defaults */ -void dtext(int x, int y, int fg, char const *str) -{ - dtext_opt(x, y, fg, C_NONE, DTEXT_LEFT, DTEXT_TOP, str); -} diff --git a/src/render-fx/dclear.c b/src/render-fx/dclear.c index adfd603..4411475 100644 --- a/src/render-fx/dclear.c +++ b/src/render-fx/dclear.c @@ -1,8 +1,11 @@ #include +#include /* dclear() - fill the screen with a single color */ void dclear(color_t color) { + DMODE_OVERRIDE(dclear, color); + /* SuperH only supports a single write-move addressing mode, which is pre-decrement write; the other similar mode is post-increment read. So we'll use pre-decrement writes to improve performance. */ diff --git a/src/render-fx/dpixel.c b/src/render-fx/dpixel.c index 457ee28..87ab44b 100644 --- a/src/render-fx/dpixel.c +++ b/src/render-fx/dpixel.c @@ -1,12 +1,15 @@ #include #include +#include /* dpixel() - change a pixel's color */ -void dpixel(int x, int y, color_t color) +void dpixel(int x, int y, int color) { /* Sanity checks */ if((uint)x >= 128 || (uint)y >= 64) return; + DMODE_OVERRIDE(dpixel, x, y, color); + uint32_t *lword = gint_vram + (y << 2) + (x >> 5); uint32_t mask = 1 << (~x & 31); diff --git a/src/render-fx/drect.c b/src/render-fx/drect.c index 123904b..dd37ffa 100644 --- a/src/render-fx/drect.c +++ b/src/render-fx/drect.c @@ -3,7 +3,7 @@ #include /* drect() - fill a rectangle of the screen */ -void drect(int x1, int y1, int x2, int y2, color_t color) +void drect(int x1, int y1, int x2, int y2, int color) { if(x1 > x2) swap(x1, x2); if(y1 > y2) swap(y1, y2); @@ -15,6 +15,8 @@ void drect(int x1, int y1, int x2, int y2, color_t color) if(y1 < 0) y1 = 0; if(y2 >= 64) y2 = 63; + DMODE_OVERRIDE(drect, x1, y1, x2, y2, color); + /* Use masks to get the work done fast! */ uint32_t m[4]; masks(x1, x2, m); diff --git a/src/render-fx/dimage.c b/src/render-fx/dsubimage.c similarity index 67% rename from src/render-fx/dimage.c rename to src/render-fx/dsubimage.c index afacca9..a338a15 100644 --- a/src/render-fx/dimage.c +++ b/src/render-fx/dsubimage.c @@ -8,18 +8,12 @@ static void *bopti_asm[] = { bopti_asm_mono_alpha, }; -/* dimage() - render a full image */ -void dimage(int x, int y, bopti_image_t const *img) -{ - if(img->gray) return; - bopti_render_clip(x, y, img, 0, 0, img->width, img->height, gint_vram, - NULL, bopti_asm[img->profile]); -} - -/* dsubimage() - render a section of an image */ +/* dsubimage(): Render a section of an image */ void dsubimage(int x, int y, bopti_image_t const *img, int left, int top, int width, int height, int flags) { + DMODE_OVERRIDE(dsubimage, x, y, img, left, top, width, height, flags); + if(img->gray) return; if(flags & DIMAGE_NOCLIP) diff --git a/src/render-fx/dtext.c b/src/render-fx/dtext.c deleted file mode 100644 index 7496281..0000000 --- a/src/render-fx/dtext.c +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include -#include "topti-asm.h" - -/* dtext_opt(): Display a string of text */ -void dtext_opt(int x, int y, int fg, int bg, int halign, int valign, - char const *str) -{ - if((uint)fg >= 8 || (uint)bg >= 8) return; - - if(halign != DTEXT_LEFT || valign != DTEXT_TOP) - { - int w, h; - dsize(str, topti_font, &w, &h); - - if(halign == DTEXT_RIGHT) x -= w; - if(halign == DTEXT_CENTER) x -= ((w+1) >> 1); - if(valign == DTEXT_BOTTOM) y -= h; - if(valign == DTEXT_MIDDLE) y -= ((h+1) >> 1); - } - - topti_render(x, y, str, topti_font, topti_asm_text[fg], - topti_asm_text[bg], gint_vram, gint_vram); -} - -/* dtext(): Simple version of dtext_opt() with defaults */ -void dtext(int x, int y, int fg, char const *str) -{ - dtext_opt(x, y, fg, C_NONE, DTEXT_LEFT, DTEXT_TOP, str); -} diff --git a/src/render-fx/dupdate.c b/src/render-fx/dupdate.c index 707020b..53cbb32 100644 --- a/src/render-fx/dupdate.c +++ b/src/render-fx/dupdate.c @@ -1,5 +1,6 @@ #include #include +#include /* Standard video RAM for fx9860g is 1 bit per pixel */ GSECTION(".bss") static uint32_t fx_vram[256]; @@ -7,14 +8,19 @@ GSECTION(".bss") static uint32_t fx_vram[256]; /* Here is the definition of the VRAM pointer, exposed in */ uint32_t *gint_vram = fx_vram; +/* The current rendering mode */ +struct rendering_mode const *dmode = NULL; + /* dupdate() - push the video RAM to the display driver */ void dupdate(void) { + if(dmode && dmode->dupdate) + { + /* Call the overridden dupdate(), but continue if itreturns + non-zero (this is used when stopping the gray engine) */ + int rc = dmode->dupdate(); + if(rc == 0) return; + } + t6k11_display(gint_vram, 0, 64, 16); } - -/* dupdate_noint() - Push VRAM to the display without interrupts */ -void dupdate_noint(void) -{ - dupdate(); -} diff --git a/src/render-fx/dvline.c b/src/render-fx/dvline.c deleted file mode 100644 index b31dd0d..0000000 --- a/src/render-fx/dvline.c +++ /dev/null @@ -1,7 +0,0 @@ -#include - -/* dvline(): Full-height vertical line */ -void dvline(int x, color_t color) -{ - dline(x, 0, x, 63, color); -} diff --git a/src/render-fx/dline.c b/src/render-fx/gint_dline.c similarity index 91% rename from src/render-fx/dline.c rename to src/render-fx/gint_dline.c index b3b4058..6eec4b9 100644 --- a/src/render-fx/dline.c +++ b/src/render-fx/gint_dline.c @@ -3,7 +3,7 @@ #include /* gint_dhline(): Optimized horizontal line using a rectangle mask */ -void gint_dhline(int x1, int x2, int y, color_t color) +void gint_dhline(int x1, int x2, int y, int color) { if((uint)y >= 64) return; if(x1 > x2) swap(x1, x2); @@ -39,7 +39,7 @@ void gint_dhline(int x1, int x2, int y, color_t color) } /* gint_dvline(): Optimized vertical line */ -void gint_dvline(int y1, int y2, int x, color_t color) +void gint_dvline(int y1, int y2, int x, int color) { if((uint)x >= 128) return; if(y1 > y2) swap(y1, y2); diff --git a/src/render-fx/topti.c b/src/render-fx/topti.c index 7ffdf3e..ada2a88 100644 --- a/src/render-fx/topti.c +++ b/src/render-fx/topti.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "topti-asm.h" /* Default font */ @@ -173,3 +174,26 @@ void topti_render(int x, int y, char const *str, font_t const *f, asm_fg(v1, v2, operators + vdisp, height - vdisp); } } + +/* dtext_opt(): Display a string of text */ +void dtext_opt(int x, int y, int fg, int bg, int halign, int valign, + char const *str) +{ + if((uint)fg >= 8 || (uint)bg >= 8) return; + + DMODE_OVERRIDE(dtext_opt, x, y, fg, bg, halign, valign, str); + + if(halign != DTEXT_LEFT || valign != DTEXT_TOP) + { + int w, h; + dsize(str, topti_font, &w, &h); + + if(halign == DTEXT_RIGHT) x -= w; + if(halign == DTEXT_CENTER) x -= ((w+1) >> 1); + if(valign == DTEXT_BOTTOM) y -= h; + if(valign == DTEXT_MIDDLE) y -= ((h+1) >> 1); + } + + topti_render(x, y, str, topti_font, topti_asm_text[fg], + topti_asm_text[bg], gint_vram, gint_vram); +} diff --git a/src/render-fx/dhline.c b/src/render/dhline.c similarity index 52% rename from src/render-fx/dhline.c rename to src/render/dhline.c index 5fb6edf..f01dc65 100644 --- a/src/render-fx/dhline.c +++ b/src/render/dhline.c @@ -1,7 +1,7 @@ #include /* dhline(): Full-width horizontal line */ -void dhline(int y, color_t color) +void dhline(int y, int color) { - dline(0, y, 127, y, color); + dline(0, y, DWIDTH - 1, y, color); } diff --git a/src/render/dimage.c b/src/render/dimage.c new file mode 100644 index 0000000..bd69d75 --- /dev/null +++ b/src/render/dimage.c @@ -0,0 +1,7 @@ +#include + +/* dimage(): Render a full image */ +void dimage(int x, int y, bopti_image_t const *img) +{ + dsubimage(x, y, img, 0, 0, img->width, img->height, DIMAGE_NONE); +} diff --git a/src/render/dline.c b/src/render/dline.c index 0328783..06888ca 100644 --- a/src/render/dline.c +++ b/src/render/dline.c @@ -2,21 +2,35 @@ #include #include +#ifdef FX9860G +#include +#endif + /* dline(): Bresenham line drawing algorithm Remotely adapted from MonochromeLib code by Pierre "PerriotLL" Le Gall. Relies on platform-dependent dhline() and dvline() for optimized situations. @x1 @y1 @x2 @y2 Coordinates of endpoints of line (included) @color Any color accepted by dpixel() on the platform */ -void dline(int x1, int y1, int x2, int y2, color_t color) +void dline(int x1, int y1, int x2, int y2, int color) { + if(color == C_NONE) return; + /* Possible optimizations */ if(y1 == y2) { + #ifdef FX9860G + DMODE_OVERRIDE(gint_dhline, x1, x2, y1, color); + #endif + gint_dhline(x1, x2, y1, color); return; } if(x1 == x2) { + #ifdef FX9860G + DMODE_OVERRIDE(gint_dvline, y1, y2, x1, color); + #endif + gint_dvline(y1, y2, x1, color); return; } diff --git a/src/render/dtext.c b/src/render/dtext.c new file mode 100644 index 0000000..7a41b72 --- /dev/null +++ b/src/render/dtext.c @@ -0,0 +1,7 @@ +#include + +/* dtext(): Simple version of dtext_opt() with defaults */ +void dtext(int x, int y, int fg, char const *str) +{ + dtext_opt(x, y, fg, C_NONE, DTEXT_LEFT, DTEXT_TOP, str); +} diff --git a/src/render-cg/dvline.c b/src/render/dvline.c similarity index 52% rename from src/render-cg/dvline.c rename to src/render/dvline.c index 2c11797..55b4da3 100644 --- a/src/render-cg/dvline.c +++ b/src/render/dvline.c @@ -1,7 +1,7 @@ #include /* dvline(): Full-height vertical line */ -void dvline(int x, color_t color) +void dvline(int x, int color) { - dline(x, 0, x, 223, color); + dline(x, 0, x, DHEIGHT - 1, color); }