Compare commits

...

9 Commits

Author SHA1 Message Date
mibi88 386303136c Merge branch 'dev' of https://gitea.planet-casio.com/mibi88/gint into dev 2022-11-19 20:17:58 +01:00
mibi88 8a6f3bfdce Fix typo 2022-11-19 20:14:17 +01:00
mibi88 d6e8b096b5 Added SetQuitHandler. 2022-11-19 20:14:17 +01:00
Lephe 4df3d69d8c
render: add a window setting to restrict rendering
Mono text rendering is a bad hack and probably not that fast, but meh.
We can optimize it the day it becomes a bottleneck, if ever...
2022-11-19 17:19:28 +01:00
Lephe 74438f5da5
render: add a dgetpixel() function 2022-11-16 19:12:48 +01:00
Lephe 45fa6c87c2
render-cg: fix a bug with unaligned C_INVERT drect()
The alignment optimization would write the edges twice, which is fine
for opaque color fills, but cancels out for C_INVERT.
2022-11-16 18:10:22 +01:00
Lephe 5e35871c98
usb: add timeout variants for functions
We're not using them yet (specifically in fxlink) because timeouts
leave the pipes in undesirable states that currently end up crashing.
Some reset mechanism is needed, plus support from the protocol for
canceling messages, etc.
2022-11-13 08:56:59 +01:00
Lephe 5f9553f3b8
usb: fix transmissions resuming early after world switch
When the driver goes through a world switch a reconnection with the host
is needed before operations can resume. This requires usb_open_status to
be reset.

Also: bind a FIFO before a commit that involves data transfer, mirroring
what happens in writes. This ensures that PID is set to BUF, mainly.
2022-11-12 14:51:00 +01:00
Lephe d110ab608e
usb: fix freeze on sync commit for inactive pipes
Performing an asynchronous commit on an inactive pipe would yield
USB_COMMIT_INACTVE and *not* invoke the callback (as intended),
which usb_commit_sync() ignored, causing a freeze.

This issue appeared after a world switch, which (for reasons not yet
known) appear to fail the first writes until a commit, and that commit
would then hit an inactive pipe.
2022-11-11 17:47:29 +01:00
42 changed files with 423 additions and 166 deletions

View File

@ -109,6 +109,7 @@ set(SOURCES_COMMON
src/render/dtext.c
src/render/dupdate_hook.c
src/render/dvline.c
src/render/dwindow.c
src/render/topti.c
# RTC driver
src/rtc/rtc.c
@ -132,6 +133,7 @@ set(SOURCES_FX
# Gray engine
src/gray/engine.c
src/gray/gclear.c
src/gray/ggetpixel.c
src/gray/gint_gline.c
src/gray/gpixel.c
src/gray/grect.c
@ -144,6 +146,7 @@ set(SOURCES_FX
src/render-fx/bopti-asm.s
src/render-fx/bopti.c
src/render-fx/dclear.c
src/render-fx/dgetpixel.c
src/render-fx/dpixel.c
src/render-fx/drect.c
src/render-fx/dsubimage.c
@ -193,6 +196,7 @@ set(SOURCES_CG
src/image/image_vflip_alloc.c
# Rendering
src/render-cg/dclear.c
src/render-cg/dgetpixel.c
src/render-cg/dpixel.c
src/render-cg/drect.c
src/render-cg/dsubimage.c

3
TODO
View File

@ -1,3 +1,6 @@
Bugs to fix:
* render: figure out why fx-CG dclear() now takes 4.1 ms instead of 2.6 ms
Extensions on existing code:
* clock: mono support
* usb: add PC->calc reading, and interrupt pipes

View File

@ -0,0 +1,36 @@
//---
// gint:defs:timeout - RTC-based timeouts
//
// This header provides an interface for simplistic timers used for timeout
// waiting. Currently they are based on the RTC with a resolution of 1/128 s.
//---
#ifndef GINT_DEFS_TIMEOUT
#define GINT_DEFS_TIMEOUT
#ifdef __cplusplus
extern "C" {
#endif
#include <time.h>
#include <stdbool.h>
/* Object holding information about a timeout (specifically, when it expires).
TODO: timeout: consider using a struct timespec with clock_gettime()? */
typedef clock_t timeout_t;
static inline timeout_t timeout_make_ms(int ms)
{
return clock() + (int64_t)ms * CLOCKS_PER_SEC / 1000;
}
static inline bool timeout_elapsed(timeout_t const *t)
{
return t && clock() >= *t;
}
#ifdef __cplusplus
}
#endif
#endif /* GINT_DEFS_TIMEOUT */

View File

@ -27,8 +27,6 @@ extern "C" {
#include <gint/display-cg.h>
#endif
/* TODO: dinfo() or similar */
//---
// Video RAM management
//---
@ -82,6 +80,31 @@ void dupdate_set_hook(gint_call_t function);
/* dupdate_get_hook(): Get a copy of the dupdate() hook */
gint_call_t dupdate_get_hook(void);
//---
// Rendering mode control
//---
/* dmode: Rendering mode settings
This structure indicates the current window settings. Rendering is limited
to the rectangle spanning from (left,top) included, to (right,bottom)
excluded. The default is from (0,0) to (DWIDTH,DHEIGHT). */
struct dwindow {
int left, top;
int right, bottom;
};
extern struct dwindow dwindow;
/* dwindow_set(): Set the rendering window
This function changes [dwindow] settings to limit rendering to a sub-surface
of the VRAM. Note that this doesn't change the coordinate system; the pixel
at (DWIDTH/2, DHEIGHT/2) is always in the middle of the screen regardless of
the window setting. However, it might be masked out by the window.
Returns the old dwindow settings (if it needs to be restored later). */
struct dwindow dwindow_set(struct dwindow new_mode);
//---
// Area rendering functions
//---
@ -151,6 +174,11 @@ void drect_border(int x1, int y1, int x2, int y2,
*: When the gray engine is on, see dgray(). */
void dpixel(int x, int y, int color);
/* dgetpixel(): Get a pixel's color
Returns the current color of any pixel in the VRAM. This function ignores
the rendering window. Returns -1 if (x,y) is out of bounds. */
int dgetpixel(int x, int y);
/* dline(): Render a straight line
This function draws a line using a Bresenham-style algorithm. Please note

View File

@ -41,6 +41,8 @@ extern "C" {
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
struct dwindow;
//---
// Image structures
//---
@ -653,13 +655,14 @@ struct gint_image_box
};
/* Clip the provided box against the input. If, after clipping, the box no
longer intersects the output (whose size is specified as out_w/out_h),
returns false. Otherwise, returns true. */
longer intersects the output window, returns false. Otherwise, returns
true. */
bool gint_image_clip_input(image_t const *img, struct gint_image_box *box,
int out_w, int out_h);
struct dwindow const *window);
/* Clip the provided box against the output. */
void gint_image_clip_output(struct gint_image_box *b, int out_w, int out_h);
void gint_image_clip_output(struct gint_image_box *b,
struct dwindow const *window);
//---
// Internal image rendering routines
@ -745,15 +748,14 @@ struct gint_image_cmd
@left_edge Whether to force 2-alignment on the input (box->left)
@right_edge Whether to force 2-alignment on the width
@cmd Command to be filled
@out_width Output width (usually DWIDTH)
@out_height Output height (usually DHEIGHT)
@window Rendering window (usually {0, 0, DWIDTH, DHEIGHT})
Returns false if there is nothing to render because of clipping (in which
case [cmd] is unchanged), true otherwise. [*box] is also updated to reflect
the final box after clipping but not accounting for edges. */
bool gint_image_mkcmd(struct gint_image_box *box, image_t const *img,
int effects, bool left_edge, bool right_edge,
struct gint_image_cmd *cmd, int out_width, int out_height);
struct gint_image_cmd *cmd, struct dwindow const *window);
/* Entry point of the renderers. These functions can be called normally as long
as you can build the commands (eg. by using gint_image_mkcmd() then filling

View File

@ -10,9 +10,10 @@ extern "C" {
#endif
#include <gint/defs/types.h>
#include <gint/std/endian.h>
#include <gint/defs/timeout.h>
#include <gint/gint.h>
#include <stdarg.h>
#include <endian.h>
/* See "Interfaces to communicate with USB transfers" below for details */
typedef struct usb_interface usb_interface_t;
@ -42,11 +43,15 @@ enum {
USB_WRITE_BUSY,
/* Both FIFO controllers are busy, none is available to transfer */
USB_WRITE_NOFIFO,
/* Timeout */
USB_WRITE_TIMEOUT,
/* This pipe is busy (returned by usb_commit_async()) */
USB_COMMIT_BUSY,
/* This pipe has no ongoing transfer to commit */
USB_COMMIT_INACTIVE,
/* Timeout */
USB_COMMIT_TIMEOUT,
};
/* usb_open(): Open the USB link
@ -204,7 +209,11 @@ int usb_interface_pipe(usb_interface_t const *interface, int endpoint);
@dma Whether to use the DMA to perform the write
-> Returns an error code (0 on success). */
int usb_write_sync(int pipe, void const *data, int size, int unit_size,
bool use_dma);
bool use_dma);
/* usb_write_sync_timeout(): Synchronously write, with a timeout */
int usb_write_sync_timeout(int pipe, void const *data, int size,
int unit_size, bool use_dma, timeout_t const *timeout);
/* usb_write_async(): Asynchronously write to a USB pipe
@ -241,6 +250,9 @@ int usb_write_async(int pipe, void const *data, int size, int unit_size,
transfers whatever data is left, and returns when the transfer completes. */
void usb_commit_sync(int pipe);
/* usb_commit_sync_timeout(): Synchronously commit a write, with timeout */
int usb_commit_sync_timeout(int pipe, timeout_t const *timeout);
/* usb_commit_async(): Asynchronously commit a write
This function commits the specified pipe, causing the pipe to transfer

View File

@ -41,6 +41,7 @@ static struct rendering_mode const gray_mode = {
.dclear = gclear,
.drect = grect,
.dpixel = gpixel,
.dgetpixel = ggetpixel,
.gint_dhline = gint_ghline,
.gint_dvline = gint_gvline,
.dtext_opt = gtext_opt,
@ -51,6 +52,7 @@ static struct rendering_mode const gray_exit_mode = {
.dclear = NULL,
.drect = NULL,
.dpixel = NULL,
.dgetpixel = NULL,
.gint_dhline = NULL,
.gint_dvline = NULL,
.dtext_opt = NULL,

15
src/gray/ggetpixel.c Normal file
View File

@ -0,0 +1,15 @@
#include <gint/gray.h>
#include <gint/defs/types.h>
int ggetpixel(int x, int y)
{
uint32_t *light, *dark;
dgray_getvram(&light, &dark);
int offset = (y << 2) + (x >> 5);
uint32_t mask = 1 << (~x & 31);
bool l = (light[offset] & mask) != 0;
bool d = (dark [offset] & mask) != 0;
return (d << 1) | l;
}

View File

@ -1,4 +1,5 @@
#include <gint/image.h>
#include <gint/display.h>
#include <string.h>
#undef image_sub
@ -15,8 +16,9 @@ image_t *image_sub(image_t const *src, int left, int top, int w, int h,
h = src->height - top;
struct gint_image_box box = { 0, 0, w, h, left, top };
struct dwindow in_window = { 0, 0, w, h };
if(!image_valid(src) || IMAGE_IS_P4(src->format) ||
!gint_image_clip_input(src, &box, w, h)) {
!gint_image_clip_input(src, &box, &in_window)) {
memset(dst, 0, sizeof *dst);
return dst;
}

View File

@ -1,9 +1,18 @@
#include <gint/display.h>
#include <gint/dma.h>
/* dclear() - fill the screen with a single color */
void dclear(uint16_t color)
{
dma_memset(gint_vram, (color << 16) | color, 396 * 224 * 2);
return;
bool full_width = (dwindow.left == 0 && dwindow.right == DWIDTH);
bool dma_aligned = !(dwindow.top & 3) && !(dwindow.bottom & 3);
if(full_width && dma_aligned) {
uint16_t *vram = gint_vram + DWIDTH * dwindow.top;
int size_bytes = DWIDTH * (dwindow.bottom - dwindow.top) * 2;
dma_memset(vram, (color << 16) | color, size_bytes);
}
else {
drect(dwindow.left, dwindow.top, dwindow.right - 1,
dwindow.bottom - 1, color);
}
}

View File

@ -0,0 +1,7 @@
#include <gint/display.h>
int dgetpixel(int x, int y)
{
if((uint)x >= DWIDTH || (uint)y >= DHEIGHT) return -1;
return gint_vram[DWIDTH * y + x];
}

View File

@ -1,11 +1,12 @@
#include <gint/display.h>
/* dpixel() - change a pixel's color */
void dpixel(int x, int y, int color)
{
/* Coordinate checks */
if((uint)x >= 396 || (uint)y >= 224 || color == C_NONE) return;
int index = 396 * y + x;
if(x < dwindow.left || x >= dwindow.right) return;
if(y < dwindow.top || y >= dwindow.bottom) return;
if(color == C_NONE) return;
int index = DWIDTH * y + x;
if(color == C_INVERT) gint_vram[index] ^= 0xffff;
else gint_vram[index] = color;

View File

@ -1,7 +1,6 @@
#include <gint/defs/util.h>
#include <gint/display.h>
/* drect() - fill a rectangle of the screen */
void drect(int x1, int y1, int x2, int y2, int color)
{
if(color == C_NONE) return;
@ -9,17 +8,19 @@ void drect(int x1, int y1, int x2, int y2, int color)
if(x1 > x2) swap(x1, x2);
if(y1 > y2) swap(y1, y2);
/* Order and bounds */
if(x1 >= 396 || x2 < 0 || y1 >= 224 || y2 < 0) return;
if(x1 < 0) x1 = 0;
if(x2 >= 396) x2 = 395;
if(y1 < 0) y1 = 0;
if(y2 >= 224) y2 = 223;
/* Rectangle is completely outside the rendering window */
if(x1 >= dwindow.right || x2 < dwindow.left) return;
if(y1 >= dwindow.bottom || y2 < dwindow.top) return;
/* Clipping */
x1 = max(x1, dwindow.left);
x2 = min(x2, dwindow.right - 1);
y1 = max(y1, dwindow.top);
y2 = min(y2, dwindow.bottom - 1);
/* The method is exactly like dhline(). I first handle odd endpoints,
then write longwords for the longest section */
uint16_t *base = gint_vram + 396 * y1;
uint16_t *base = gint_vram + DWIDTH * y1;
int height = y2 - y1 + 1;
/* Now copy everything that's left as longwords */
@ -33,18 +34,19 @@ void drect(int x1, int y1, int x2, int y2, int color)
if(color == C_INVERT) for(int h = height; h; h--)
{
base[x1] ^= 0xffff;
base[x2] ^= 0xffff;
/* We can't double-draw on base[x1] and base[x2] here */
if(x1 & 1) base[x1] ^= 0xffff;
if(!(x2 & 1)) base[x2] ^= 0xffff;
for(int w = 0; w < width; w++) v[w] ^= 0xffffffff;
v += 198;
base += 396;
v += DWIDTH / 2;
base += DWIDTH;
}
else for(int h = height; h; h--)
{
base[x1] = color;
base[x2] = color;
for(int w = 0; w < width; w++) v[w] = op;
v += 198;
base += 396;
v += DWIDTH / 2;
base += DWIDTH;
}
}

View File

@ -11,7 +11,7 @@ bool dvram_init(void)
int const MARGIN = 32;
/* Leave MARGIN bytes on each side of the region; this enables some
important optimisations in the image renderer. We also add another
important optimizations in the image renderer. We also add another
32 bytes so we can manually 32-align the region */
uint32_t region = (uint32_t)malloc(DWIDTH*DHEIGHT*2 + MARGIN*2 + 32);
if(region == 0)

View File

@ -4,14 +4,13 @@
/* gint_dhline(): Optimized horizontal line */
void gint_dhline(int x1, int x2, int y, uint16_t color)
{
/* Order and bounds */
if((uint)y >= 224) return;
if(y < dwindow.top || y >= dwindow.bottom) return;
if(x1 > x2) swap(x1, x2);
if(x1 >= 396 || x2 < 0) return;
if(x1 < 0) x1 = 0;
if(x2 >= 396) x2 = 395;
if(x1 >= dwindow.right || x2 < dwindow.left) return;
x1 = max(x1, dwindow.left);
x2 = min(x2, dwindow.right - 1);
int offset = 396 * y;
int offset = DWIDTH * y;
/* Use longwords to do the copy, but first paint the endpoints to heed
for odd x1 and x2. Checking the parity may be a waste of time. */
@ -33,15 +32,14 @@ void gint_dhline(int x1, int x2, int y, uint16_t color)
/* gint_dvline(): Optimized vertical line */
void gint_dvline(int y1, int y2, int x, uint16_t color)
{
/* Order and bounds */
if((uint)x >= 396) return;
if(x < dwindow.left || x >= dwindow.right) return;
if(y1 > y2) swap(y1, y2);
if(y1 < 0) y1 = 0;
if(y2 >= 224) y2 = 223;
y1 = max(y1, dwindow.top);
y2 = min(y2, dwindow.bottom - 1);
uint16_t *v = gint_vram + 396 * y1 + x;
uint16_t *v = gint_vram + DWIDTH * y1 + x;
int height = y2 - y1 + 1;
while(height-- > 0) *v = color, v += 396;
while(height-- > 0) *v = color, v += DWIDTH;
}

View File

@ -1,8 +1,9 @@
#include <gint/image.h>
#include <gint/display.h>
#include <gint/defs/util.h>
bool gint_image_clip_input(image_t const *img, struct gint_image_box *b,
int out_w, int out_h)
struct dwindow const *window)
{
/* Adjust the bounding box of the input image */
if(b->left < 0) b->w += b->left, b->x -= b->left, b->left = 0;
@ -13,37 +14,50 @@ bool gint_image_clip_input(image_t const *img, struct gint_image_box *b,
/* Check whether the box intersects the screen */
if(b->w <= 0 || b->h <= 0)
return false;
if(b->x + b->w <= 0 || b->x >= out_w)
if(b->x + b->w <= window->left || b->x >= window->right)
return false;
if(b->y + b->h <= 0 || b->y >= out_h)
if(b->y + b->h <= window->top || b->y >= window->bottom)
return false;
return true;
}
void gint_image_clip_output(struct gint_image_box *b, int out_w, int out_h)
void gint_image_clip_output(struct gint_image_box *b,
struct dwindow const *window)
{
/* Intersect with the bounding box on-screen */
if(b->y < 0) b->top -= b->y, b->h += b->y, b->y = 0;
if(b->y + b->h > out_h) b->h = (out_h - b->y);
if(b->x < 0) b->left -= b->x, b->w += b->x, b->x = 0;
if(b->x + b->w > out_w) b->w = (out_w - b->x);
if(b->y < window->top) {
int d = window->top - b->y; /* > 0 */
b->top += d;
b->h -= d;
b->y += d;
}
b->h = min(b->h, window->bottom - b->y);
if(b->x < window->left) {
int d = window->left - b->x; /* > 0 */
b->left += d;
b->w -= d;
b->x += d;
}
b->w = min(b->w, window->right - b->x);
}
bool gint_image_mkcmd(struct gint_image_box *box, image_t const *img,
int effects, bool left_edge, bool right_edge,
struct gint_image_cmd *cmd, int out_width, int out_height)
struct gint_image_cmd *cmd, struct dwindow const *window)
{
/* Convert the old DIMAGE_NOCLIP flag */
if(effects & DIMAGE_NOCLIP)
effects |= IMAGE_NOCLIP;
if(!(effects & IMAGE_NOCLIP_INPUT)) {
if(!gint_image_clip_input(img, box, out_width, out_height))
if(!gint_image_clip_input(img, box, window))
return false;
}
if(!(effects & IMAGE_NOCLIP_OUTPUT))
gint_image_clip_output(box, out_width, out_height);
gint_image_clip_output(box, window);
cmd->effect = (effects & (IMAGE_VFLIP | IMAGE_HFLIP)) >> 8;
cmd->columns = box->w;

View File

@ -16,8 +16,8 @@ void dsubimage_p4(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.loop = gint_image_p4_normal;
gint_image_p4_loop(DWIDTH, &cmd);
}
@ -33,8 +33,8 @@ void dsubimage_p4_clearbg(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.color_1 = bg_color;
cmd.loop = gint_image_p4_clearbg;
gint_image_p4_loop(DWIDTH, &cmd);

View File

@ -13,8 +13,8 @@ void dsubimage_p4_clearbg_alt(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, &dwindow))
return;
cmd.color_1 = bg_color;
cmd.loop = gint_image_p4_clearbg_alt;
gint_image_p4_loop(DWIDTH, &cmd);

View File

@ -13,8 +13,8 @@ void dsubimage_p4_dye(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, &dwindow))
return;
cmd.color_1 = image_alpha(img->format);
cmd.color_2 = dye_color;
cmd.loop = gint_image_p4_dye;

View File

@ -14,8 +14,8 @@ void dsubimage_p4_swapcolor(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, &dwindow))
return;
cmd.color_1 = old_index;
cmd.color_2 = new_color;
cmd.loop = gint_image_p4_swapcolor;
@ -35,8 +35,8 @@ void dsubimage_p4_addbg(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, &dwindow))
return;
cmd.color_1 = image_alpha(img->format);
cmd.color_2 = bg_color;
cmd.loop = gint_image_p4_swapcolor;

View File

@ -16,8 +16,8 @@ void dsubimage_p8(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.loop = gint_image_p8_normal;
gint_image_p8_loop(DWIDTH, &cmd);
}
@ -33,8 +33,8 @@ void dsubimage_p8_clearbg(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, true, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, true, &cmd, &dwindow))
return;
cmd.color_1 = bg_color;
cmd.loop = gint_image_p8_clearbg;
gint_image_p8_loop(DWIDTH, &cmd);

View File

@ -13,8 +13,8 @@ void dsubimage_p8_dye(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, true, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, true, &cmd, &dwindow))
return;
cmd.color_1 = image_alpha(img->format);
cmd.color_2 = dye_color;
cmd.loop = gint_image_p8_dye;

View File

@ -14,8 +14,8 @@ void dsubimage_p8_swapcolor(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.color_1 = old_index;
cmd.color_2 = new_color;
cmd.loop = gint_image_p8_swapcolor;
@ -35,8 +35,8 @@ void dsubimage_p8_addbg(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.color_1 = image_alpha(img->format);
cmd.color_2 = bg_color;
cmd.loop = gint_image_p8_swapcolor;

View File

@ -16,8 +16,8 @@ void dsubimage_rgb16(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.loop = gint_image_rgb16_normal;
gint_image_rgb16_loop(DWIDTH, &cmd);
}
@ -34,8 +34,8 @@ void dsubimage_rgb16_clearbg(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.color_1 = bg_color;
cmd.loop = gint_image_rgb16_clearbg;
gint_image_rgb16_loop(DWIDTH, &cmd);

View File

@ -13,8 +13,8 @@ void dsubimage_rgb16_dye(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.color_1 = image_alpha(img->format);
cmd.color_2 = dye_color;
cmd.loop = gint_image_rgb16_dye;

View File

@ -14,8 +14,8 @@ void dsubimage_rgb16_swapcolor(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.color_1 = old_color;
cmd.color_2 = new_color;
cmd.loop = gint_image_rgb16_swapcolor;
@ -35,8 +35,8 @@ void dsubimage_rgb16_addbg(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.color_1 = image_alpha(img->format);
cmd.color_2 = bg_color;
cmd.loop = gint_image_rgb16_swapcolor;

View File

@ -1,5 +1,6 @@
#include <gint/defs/types.h>
#include <gint/defs/attributes.h>
#include <gint/defs/util.h>
#include <gint/display.h>
#include <string.h>
@ -56,12 +57,19 @@ static void topti_render(int x, int y, char const *str_char, font_t const *f,
int height = f->data_height, top = 0;
/* Vertical clipping */
if(x > 395 || y > 223 || y + height <= 0) return;
if(y + height > 224) height = 224 - y;
if(y < 0) top = -y, height += y, y = 0;
if(x >= dwindow.right || y >= dwindow.bottom) return;
if(y + height <= dwindow.top) return;
height = min(height, dwindow.bottom - y);
int top_overflow = y - dwindow.top;
if(top_overflow < 0) {
top = -top_overflow;
height += top_overflow;
y -= top_overflow;
}
/* Move to top row */
uint16_t *target = gint_vram + 396 * y;
uint16_t *target = gint_vram + DWIDTH * y;
/* Character spacing waiting to be drawn, in pixels */
int space = 0;
@ -80,7 +88,7 @@ static void topti_render(int x, int y, char const *str_char, font_t const *f,
/* Draw character spacing if background is opaque */
if(space && bg >= 0) drect(x, y, x+space-1, y+height-1, bg);
x += space;
if(x >= 396) break;
if(x >= dwindow.right) break;
int index = topti_offset(f, glyph);
@ -88,14 +96,17 @@ static void topti_render(int x, int y, char const *str_char, font_t const *f,
int width = dataw, left = 0;
if(x + dataw <= 0)
if(x + dataw <= dwindow.left)
{
x += dataw;
space = f->char_spacing;
continue;
}
if(x < 0) left = -x, width += x;
if(x + width > 396) width = 396 - x;
if(x < dwindow.left) {
left = dwindow.left - x;
width -= left;
}
width = min(width, dwindow.right - x);
/* Render glyph */

View File

@ -1,4 +1,5 @@
#include <gint/defs/types.h>
#include <gint/defs/util.h>
#include <gint/display.h>
#include "render-fx.h"
#include "bopti-asm.h"
@ -271,18 +272,29 @@ int bopti_clip(bopti_image_t const *img, struct rbox *r)
int x = r->visual_x, y = r->y;
int left = r->left, top = r->top;
int width = r->width, height = r->height;
int diff;
/* Adjust the bounding box of the input image */
if(left < 0) width += left, x -= left, left = 0;
if(top < 0) height += top, y -= top, top = 0;
if(left + width > img->width) width = img->width - left;
if(top + height > img->height) height = img->height - top;
width = min(width, img->width - left);
height = min(height, img->height - top);
/* Intersect with the bounding box on-screen */
if(x < 0) width += x, left -= x, x = 0;
if(y < 0) height += y, top -= y, y = 0;
if(x + width > DWIDTH) width = DWIDTH - x;
if(y + height > DHEIGHT) height = DHEIGHT - y;
if((diff = dwindow.left - x) > 0)
{
width -= diff;
left += diff;
x += diff;
}
if((diff = dwindow.top - y) > 0)
{
height -= diff;
top += diff;
y += diff;
}
width = min(width, dwindow.right - x);
height = min(height, dwindow.bottom - y);
r->visual_x = x;
r->y = y;

View File

@ -4,6 +4,12 @@
/* dclear() - fill the screen with a single color */
void dclear(color_t color)
{
if(dwindow.left != 0 || dwindow.right != DWIDTH) {
drect(dwindow.left, dwindow.top, dwindow.right - 1,
dwindow.bottom - 1, color);
return;
}
DMODE_OVERRIDE(dclear, color);
/* SuperH only supports a single write-move addressing mode, which is
@ -13,9 +19,10 @@ void dclear(color_t color)
if(color != C_WHITE && color != C_BLACK) return;
uint32_t fill = -(color >> 1);
uint32_t *index = gint_vram + 256;
uint32_t *start = gint_vram + 4 * dwindow.top;
uint32_t *index = gint_vram + 4 * dwindow.bottom;
while(index > gint_vram)
while(index > start)
{
/* Do it by batches to avoid losing cycles on loop tests */
*--index = fill;

16
src/render-fx/dgetpixel.c Normal file
View File

@ -0,0 +1,16 @@
#include <gint/display.h>
#include <gint/defs/types.h>
#include "render-fx.h"
int dgetpixel(int x, int y)
{
if(x < dwindow.left || x >= dwindow.right) return -1;
if(y < dwindow.top || y >= dwindow.bottom) return -1;
DMODE_OVERRIDE(dgetpixel, x, y);
int offset = (y << 2) + (x >> 5);
uint32_t mask = 1 << (~x & 31);
return (gint_vram[offset] & mask) ? C_BLACK : C_WHITE;
}

View File

@ -5,8 +5,8 @@
/* dpixel() - change a pixel's color */
void dpixel(int x, int y, int color)
{
/* Sanity checks */
if((uint)x >= 128 || (uint)y >= 64) return;
if(x < dwindow.left || x >= dwindow.right) return;
if(y < dwindow.top || y >= dwindow.bottom) return;
DMODE_OVERRIDE(dpixel, x, y, color);

View File

@ -2,18 +2,19 @@
#include <gint/display.h>
#include "render-fx.h"
/* drect() - fill a rectangle of the screen */
void drect(int x1, int y1, int x2, int y2, int 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;
/* Rectangle is completely outside the rendering window */
if(x1 >= dwindow.right || x2 < dwindow.left) return;
if(y1 >= dwindow.bottom || y2 < dwindow.top) return;
/* Clipping */
x1 = max(x1, dwindow.left);
x2 = min(x2, dwindow.right - 1);
y1 = max(y1, dwindow.top);
y2 = min(y2, dwindow.bottom - 1);
DMODE_OVERRIDE(drect, x1, y1, x2, y2, color);

View File

@ -5,9 +5,9 @@
/* gint_dhline(): Optimized horizontal line using a rectangle mask */
void gint_dhline(int x1, int x2, int y, int color)
{
if((uint)y >= 64) return;
if(y < dwindow.top || y >= dwindow.bottom) return;
if(x1 > x2) swap(x1, x2);
if(x1 >= 128 || x2 < 0) return;
if(x1 >= dwindow.right || x2 < dwindow.left) return;
/* Get the masks for the [x1, x2] range */
uint32_t m[4];
@ -41,11 +41,11 @@ void gint_dhline(int x1, int x2, int y, int color)
/* gint_dvline(): Optimized vertical line */
void gint_dvline(int y1, int y2, int x, int color)
{
if((uint)x >= 128) return;
if(x < dwindow.left || x >= dwindow.right) return;
if(y1 > y2) swap(y1, y2);
if(y1 >= 64 || y2 < 0) return;
if(y1 < 0) y1 = 0;
if(y2 >= 64) y2 = 63;
if(y1 >= dwindow.bottom || y2 < dwindow.top) return;
y1 = max(y1, dwindow.top);
y2 = min(y2, dwindow.bottom - 1);
uint32_t *base = gint_vram + (y1 << 2) + (x >> 5);
uint32_t *lword = base + ((y2 - y1 + 1) << 2);

View File

@ -1,28 +1,24 @@
#include <gint/defs/util.h>
#include "render-fx.h"
/* masks() - compute the vram masks for a given rectangle */
void masks(int x1, int x2, uint32_t *masks)
{
if(x1 < 0) x1 = 0;
if(x2 >= 128) x2 = 127;
x1 = max(x1, dwindow.left);
x2 = min(x2, dwindow.right - 1);
/* Indexes of the first and last non-empty longs */
size_t l1 = x1 >> 5;
size_t l2 = x2 >> 5;
size_t i = 0;
/* Base masks (0's are final, 0xffffffff will be adjusted later) */
while(i < l1) masks[i++] = 0x00000000;
while(i <= l2) masks[i++] = 0xffffffff;
while(i < 4) masks[i++] = 0x00000000;
/* Remove the index information in x1 and x2 (it's now in l1 and l2)
and keep only the offsets */
/* Remove the index information in x1 and x2; keep only the offsets */
x1 &= 31;
/* For x2 we also want the complement to 31 to invert the shift */
x2 = ~x2 & 31;
/* Now roll! */
masks[l1] &= (0xffffffffu >> x1);
masks[l2] &= (0xffffffffu << x2);
}

View File

@ -69,6 +69,7 @@ struct rendering_mode
void (*drect)(int x1, int y1, int x2, int y2, color_t color);
/* Point rendering */
void (*dpixel)(int x, int y, color_t color);
int (*dgetpixel)(int x, int y);
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 */
@ -87,6 +88,7 @@ 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);
int ggetpixel(int x, int y);
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,
@ -94,10 +96,9 @@ void gtext_opt(int x, int y, int fg, int bg, int halign, int valign,
void gsubimage(bopti_image_t const *image, struct rbox *r, 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; \
#define DMODE_OVERRIDE(func, ...) \
if(dmode && dmode->func) { \
return dmode->func(__VA_ARGS__); \
}
#endif /* RENDER_FX */

View File

@ -1,5 +1,6 @@
#include <gint/defs/types.h>
#include <gint/defs/attributes.h>
#include <gint/defs/util.h>
#include <gint/display.h>
#include "../render/render.h"
@ -97,12 +98,20 @@ void topti_render(int x, int y, char const *str_char, font_t const *f,
uint32_t const *data = f->data;
/* Basic clipping */
if(x > 127 || y > 63 || y + height <= 0) return;
if(y + height > 64) height = 64 - y;
if(x >= dwindow.right || y >= dwindow.bottom) return;
if(y + height <= dwindow.top) return;
height = min(height, dwindow.bottom - y);
/* How much we need to skip vertically if we render text at y < 0 */
/* How much we skip vertically if we render at y < dwindow.top */
int vdisp = 0;
if(y < 0) vdisp = -y, y = 0;
if(y < dwindow.top)
{
vdisp = dwindow.top - y;
y = dwindow.top;
}
uint32_t bg_mask[4];
masks(dwindow.left, dwindow.right, bg_mask);
/* Operator data and background */
uint32_t operators[height];
@ -146,6 +155,12 @@ void topti_render(int x, int y, char const *str_char, font_t const *f,
if(x >= 0)
{
for(int i = 0; i < height; i++)
{
operators[i] &= bg_mask[x];
bg[i] &= bg_mask[x];
}
asm_bg(v1, v2, bg + vdisp, height - vdisp);
asm_fg(v1, v2, operators + vdisp, height - vdisp);
}
@ -171,7 +186,11 @@ void topti_render(int x, int y, char const *str_char, font_t const *f,
/* Put the final longwords */
if(x >= 0 && x < 4 && free < 32)
{
for(int i = 0; i < height; i++) bg[i] &= ~((1 << free) - 1);
for(int i = 0; i < height; i++)
{
operators[i] &= bg_mask[x];
bg[i] &= bg_mask[x] & ~((1 << free) - 1);
}
asm_bg(v1, v2, bg + vdisp, height - vdisp);
asm_fg(v1, v2, operators + vdisp, height - vdisp);
}

View File

@ -3,5 +3,5 @@
/* dhline(): Full-width horizontal line */
void dhline(int y, int color)
{
dline(0, y, DWIDTH - 1, y, color);
dline(dwindow.left, y, dwindow.right - 1, y, color);
}

View File

@ -8,7 +8,8 @@ void drect_border(int x1, int y1, int x2, int y2, int fill, int width,
if(x1 > x2) swap(x1, x2);
if(y1 > y2) swap(y1, y2);
if(x1 >= DWIDTH || x2 < 0 || y1 >= DHEIGHT || y2 < 0) return;
if(x1 >= dwindow.right || x2 < dwindow.left) return;
if(y1 >= dwindow.bottom || y2 < dwindow.top) return;
drect(x1, y1, x2, y1 + (width-1), border);
drect(x1, y2 - (width-1), x2, y2, border);

View File

@ -3,5 +3,5 @@
/* dvline(): Full-height vertical line */
void dvline(int x, int color)
{
dline(x, 0, x, DHEIGHT - 1, color);
dline(x, dwindow.top, x, dwindow.bottom - 1, color);
}

21
src/render/dwindow.c Normal file
View File

@ -0,0 +1,21 @@
#include <gint/display.h>
#include <gint/defs/util.h>
struct dwindow dwindow = {
.left = 0,
.top = 0,
.right = DWIDTH,
.bottom = DHEIGHT,
};
struct dwindow dwindow_set(struct dwindow m)
{
m.left = max(m.left, 0);
m.top = max(m.top, 0);
m.right = max(m.left, min(m.right, DWIDTH));
m.bottom = max(m.top, min(m.bottom, DHEIGHT));
struct dwindow old_mode = dwindow;
dwindow = m;
return old_mode;
}

View File

@ -87,8 +87,10 @@ static void fifo_bind(fifo_t ct, int pipe, int mode, int size)
{
size = (size - (size == 4) - 1) & 3;
if(ct == CF)
if(pipe == 0)
{
if(USB.CFIFOSEL.ISEL == 1 && USB.DCPCTR.PID == 1) return;
if(mode == FIFO_WRITE) USB.DCPCTR.PID = 1;
/* RCNT=0 REW=0 MBW=size BIGEND=1 ISEL=mode CURPIPE=0 */
USB.CFIFOSEL.word = 0x0100 | (mode << 5) | (size << 10);
@ -129,9 +131,13 @@ static void fifo_unbind(fifo_t ct)
/* Current operation waiting to be performed on each pipe. There are two
possible states for a pipe's transfer data:
-> Either there is a transfer going on, in which case (data != NULL),
(size != 0), (controller != NOF), and (used) has no meaning.
-> Either there is no transfer going on, and (data = NULL), (size = 0), and
(controller = NOF).
(size != 0), and (used) has no meaning.
-> Either there is no transfer going on, and (data = NULL), (size = 0).
A controller is assigned to t->ct when a write first occurs until the pipe
is fully committed. (ct = NOF) indicates an unused pipe, while (ct != NOF)
indicates that stuff has been written and is waiting a commit.
Additionally, between a call to write_round() and the corresponding
finish_write(), the (flying) attribute is set to a non-zero value indicating
how many bytes are waiting for write completion. */
@ -224,7 +230,7 @@ static void finish_transfer(struct transfer volatile *t, int pipe)
This function is called when a write round completes, either by the handler
of the BEMP interrupt if the round filled the FIFO, or by the handler of the
DMA transfer the or write_round() function itself if it didn't.
DMA transfer or the write_round() function itself if it didn't.
It the current write operation has finished with this round, this function
invokes the write_async callback. */
@ -261,13 +267,7 @@ static void write_round(struct transfer volatile *t, int pipe)
if(ct == CF) FIFO = &USB.CFIFO;
if(ct == D0F) FIFO = &USB.D0FIFO;
if(ct == D1F) FIFO = &USB.D1FIFO;
if(pipe == 0)
{
if(USB.CFIFOSEL.ISEL != 1 || USB.DCPCTR.PID != 1)
fifo_bind(ct, 0, FIFO_WRITE, 1);
}
else fifo_bind(ct, pipe, FIFO_WRITE, t->unit_size);
fifo_bind(ct, pipe, FIFO_WRITE, t->unit_size);
/* Amount of data that can be transferred in a single run */
int available = pipe_bufsize(pipe) - (pipe == 0 ? 0 : t->used);
@ -323,7 +323,7 @@ int usb_write_async(int pipe, void const *data, int size, int unit_size,
t->data = data;
t->size = size;
t->unit_size = unit_size;
t->unit_size = (pipe == 0) ? 1 : unit_size;
t->dma = use_dma;
t->committed = false;
t->ct = ct;
@ -337,26 +337,40 @@ int usb_write_async(int pipe, void const *data, int size, int unit_size,
return 0;
}
int usb_write_sync(int pipe, void const *data, int size, int unit_size,
bool use_dma)
int usb_write_sync_timeout(int pipe, void const *data, int size, int unit_size,
bool use_dma, timeout_t const *timeout)
{
volatile int flag = 0;
int rc;
while(1)
{
rc = usb_write_async(pipe, data, size, unit_size, use_dma,
int rc = usb_write_async(pipe, data, size, unit_size, use_dma,
GINT_CALL_SET(&flag));
if(rc == 0) break;
if(rc == 0)
break;
if(rc == USB_WRITE_NOFIFO)
usb_log("USB_WRITE_NOFIFO\n");
if(rc != USB_WRITE_BUSY)
return rc;
if(timeout_elapsed(timeout))
return USB_WRITE_TIMEOUT;
sleep();
}
while(!flag) sleep();
while(!flag)
{
if(timeout_elapsed(timeout))
return USB_WRITE_TIMEOUT;
sleep();
}
return 0;
}
int usb_write_sync(int pipe, void const *data, int size, int unit, bool dma)
{
return usb_write_sync_timeout(pipe, data, size, unit, dma, NULL);
}
int usb_commit_async(int pipe, gint_call_t callback)
{
struct transfer volatile *t = &pipe_transfers[pipe];
@ -375,7 +389,7 @@ int usb_commit_async(int pipe, gint_call_t callback)
return 0;
}
/* Commiting an empty pipe ends the transfer on the spot */
/* Committing an empty pipe ends the transfer on the spot */
if(t->used == 0)
{
finish_transfer(t, pipe);
@ -384,6 +398,7 @@ int usb_commit_async(int pipe, gint_call_t callback)
/* Set BVAL=1 and inform the BEMP handler of the commitment with the
committed flag; the handler will invoke finish_transfer() */
fifo_bind(t->ct, pipe, FIFO_WRITE, t->unit_size);
if(t->ct == D0F) USB.D0FIFOCTR.BVAL = 1;
if(t->ct == D1F) USB.D1FIFOCTR.BVAL = 1;
usb_log("[PIPE%d] Committed transfer\n", pipe);
@ -391,17 +406,36 @@ int usb_commit_async(int pipe, gint_call_t callback)
return 0;
}
void usb_commit_sync(int pipe)
int usb_commit_sync_timeout(int pipe, timeout_t const *timeout)
{
volatile int flag = 0;
int rc = 0;
int volatile flag = 0;
/* Wait until the pipe is free, then commit */
do rc = usb_commit_async(pipe, GINT_CALL_SET(&flag));
while(rc == USB_COMMIT_BUSY);
while(1)
{
int rc = usb_commit_async(pipe, GINT_CALL_SET(&flag));
if(rc == 0)
break;
if(rc != USB_COMMIT_BUSY)
return rc;
if(timeout_elapsed(timeout))
return USB_COMMIT_TIMEOUT;
sleep();
}
/* Wait until the commit completes */
while(!flag) sleep();
while(!flag)
{
if(timeout_elapsed(timeout))
return USB_COMMIT_TIMEOUT;
sleep();
}
return 0;
}
void usb_commit_sync(int pipe)
{
usb_commit_sync_timeout(pipe, NULL);
}
/* usb_pipe_write_bemp(): Callback for the BEMP interrupt on a pipe */

View File

@ -287,6 +287,9 @@ static void hrestore(usb_state_t const *s)
{
hpoweron_write();
/* We will need to reconnect with the PC */
usb_open_status = false;
USB.DVSTCTR.word = s->DVSTCTR;
USB.TESTMODE.word = s->TESTMODE;
USB.REG_C2 = s->REG_C2;