redquark-amiberry-rb/src/drawing.cpp
2017-12-21 22:09:08 +01:00

2222 lines
65 KiB
C++

/*
* UAE - The Un*x Amiga Emulator
*
* Screen drawing functions
*
* Copyright 1995-2000 Bernd Schmidt
* Copyright 1995 Alessandro Bissacco
* Copyright 2000-2008 Toni Wilen
*/
/* There are a couple of concepts of "coordinates" in this file.
- DIW coordinates
- DDF coordinates (essentially cycles, resolution lower than lores by a factor of 2)
- Pixel coordinates
* in the Amiga's resolution as determined by BPLCON0 ("Amiga coordinates")
* in the window resolution as determined by the preferences ("window coordinates").
* in the window resolution, and with the origin being the topmost left corner of
the window ("native coordinates")
One note about window coordinates. The visible area depends on the width of the
window, and the centering code. The first visible horizontal window coordinate is
often _not_ 0, but the value of VISIBLE_LEFT_BORDER instead.
One important thing to remember: DIW coordinates are in the lowest possible
resolution.
To prevent extremely bad things (think pixels cut in half by window borders) from
happening, all ports should restrict window widths to be multiples of 16 pixels. */
#include "sysconfig.h"
#include "sysdeps.h"
#include <ctype.h>
#include <assert.h>
#include "options.h"
#include "threaddep/thread.h"
#include "uae.h"
#include "include/memory.h"
#include "custom.h"
#include "newcpu.h"
#include "xwin.h"
#include "autoconf.h"
#include "gui.h"
#include "picasso96.h"
#include "drawing.h"
#include "savestate.h"
#include "statusline.h"
#include "cd32_fmv.h"
#include "audio.h"
#include "devices.h"
#define VERTICAL_OFFSET 18
#define RENDER_SIGNAL_PARTIAL 1
#define RENDER_SIGNAL_FRAME_DONE 2
#define RENDER_SIGNAL_QUIT 3
static uae_thread_id render_tid = 0;
static smp_comm_pipe *volatile render_pipe = 0;
static uae_sem_t render_sem = 0;
static bool volatile render_thread_busy = false;
extern int sprite_buffer_res;
int lores_shift;
static void pfield_set_linetoscr(void);
static void lores_set(int lores)
{
int old = lores_shift;
lores_shift = lores;
if (lores_shift != old)
pfield_set_linetoscr();
}
static void lores_reset (void)
{
lores_set(currprefs.gfx_resolution);
sprite_buffer_res = currprefs.gfx_resolution;
}
bool aga_mode; /* mirror of chipset_mask & CSMASK_AGA */
/* The shift factor to apply when converting between Amiga coordinates and window
coordinates. Zero if the resolution is the same, positive if window coordinates
have a higher resolution (i.e. we're stretching the image), negative if window
coordinates have a lower resolution (i.e. we're shrinking the image). */
static int res_shift;
static int linedbl;
int interlace_seen;
/* Lookup tables for dual playfields. The dblpf_*1 versions are for the case
that playfield 1 has the priority, dbplpf_*2 are used if playfield 2 has
priority. If we need an array for non-dual playfield mode, it has no number. */
/* The dbplpf_ms? arrays contain a shift value. plf_spritemask is initialized
to contain two 16 bit words, with the appropriate mask if pf1 is in the
foreground being at bit offset 0, the one used if pf2 is in front being at
offset 16. */
static int dblpf_ms1[256], dblpf_ms2[256];
static int dblpf_ind1[256], dblpf_ind2[256];
static int dblpf_2nd1[256], dblpf_2nd2[256];
static const int dblpfofs[] = { 0, 2, 4, 8, 16, 32, 64, 128 };
static int sprite_col_nat[65536];
static int sprite_col_at[65536];
static int sprite_bit[65536];
static uae_u32 clxtab[256];
/* Video buffer description structure. Filled in by the graphics system
* dependent code. */
struct vidbuf_description gfxvidinfo;
/* OCS/ECS color lookup table. */
xcolnr xcolors[4096];
struct spritepixelsbuf {
uae_u8 attach;
uae_u8 stdata;
uae_u16 data;
};
static struct spritepixelsbuf spritepixels_buffer[MAX_PIXELS_PER_LINE];
static struct spritepixelsbuf *spritepixels;
static int sprite_first_x, sprite_last_x;
/* AGA mode color lookup tables */
#ifndef ARMV6_ASSEMBLY
unsigned int xredcolors[256], xgreencolors[256], xbluecolors[256];
#endif
static int dblpf_ind1_aga[256], dblpf_ind2_aga[256];
struct color_entry colors_for_drawing;
static struct color_entry direct_colors_for_drawing;
static xcolnr *p_acolors;
static xcolnr *p_xcolors;
/* The size of these arrays is pretty arbitrary; it was chosen to be "more
than enough". The coordinates used for indexing into these arrays are
almost, but not quite, Amiga coordinates (there's a constant offset). */
static union pixdata_u {
uae_u8 apixels[MAX_PIXELS_PER_LINE * 2];
uae_u16 apixels_w[MAX_PIXELS_PER_LINE * 2 / sizeof (uae_u16)];
uae_u32 apixels_l[MAX_PIXELS_PER_LINE * 2 / sizeof (uae_u32)];
} pixdata;
uae_u16 spixels[2 * MAX_SPR_PIXELS];
/* Eight bits for every pixel. */
union sps_union spixstate;
static uae_u16 ham_linebuf[MAX_PIXELS_PER_LINE * 2];
static uae_u8 *xlinebuffer;
static int *amiga2aspect_line_map, *native2amiga_line_map;
static uae_u8 **row_map;
static uae_u8 row_tmp[MAX_PIXELS_PER_LINE * 32 / 8];
static int max_drawn_amiga_line;
/* line_draw_funcs: pfield_do_linetoscr, pfield_do_fill_line, decode_ham */
typedef void (*line_draw_func)(int, int, bool);
static bool screenlocked = false;
static int next_line_to_render = 0;
static int linestate_first_undecided = 0;
static bool nextline_as_previous = false;
uae_u8 line_data[(MAXVPOS + 2) * 2][MAX_PLANES * MAX_WORDS_PER_LINE * 2];
/* The visible window: VISIBLE_LEFT_BORDER contains the left border of the visible
area, VISIBLE_RIGHT_BORDER the right border. These are in window coordinates. */
static int visible_left_border, visible_right_border;
static int linetoscr_x_adjust_pixbytes, linetoscr_x_adjust_pixels;
static int thisframe_y_adjust;
static int thisframe_y_adjust_real, max_ypos_thisframe, min_ypos_for_screen;
#define MAX_STOP 30000
/* These are generated by the drawing code from the line_decisions array for
each line that needs to be drawn. These are basically extracted out of
bit fields in the hardware registers. */
static int bplehb, bplham, bpldualpf, bpldualpfpri, bpldualpf2of, bplplanecnt, ecsshres;
static int bplbypass;
static int bplres;
static int plf1pri, plf2pri, bplxor, bpland;
static uae_u32 plf_sprite_mask;
static uae_u32 plf_sprite_mask_n16;
static int sbasecol[2] = { 16, 16 };
static int hposblank;
bool picasso_requested_on, picasso_requested_forced_on, picasso_on;
int inhibit_frame;
int framecnt = 0;
STATIC_INLINE void count_frame (void)
{
framecnt++;
if (framecnt > currprefs.gfx_framerate)
framecnt = 0;
if (inhibit_frame)
framecnt = 1;
}
STATIC_INLINE int xshift (int x, int shift)
{
if (shift < 0)
return x >> (-shift);
else
return x << shift;
}
int coord_native_to_amiga_x (int x)
{
x += visible_left_border;
x = xshift (x, 1 - lores_shift);
return x + 2*DISPLAY_LEFT_SHIFT - 2*DIW_DDF_OFFSET;
}
int coord_native_to_amiga_y (int y)
{
return native2amiga_line_map[y] + thisframe_y_adjust - minfirstline;
}
STATIC_INLINE int res_shift_from_window (int x)
{
if (res_shift >= 0)
return x >> res_shift;
return x << -res_shift;
}
STATIC_INLINE int res_shift_from_amiga (int x)
{
if (res_shift >= 0)
return x >> res_shift;
return x << -res_shift;
}
static struct decision *dp_for_drawing;
static struct draw_info *dip_for_drawing;
/*
* Screen update macros/functions
*/
/* The important positions in the line: where do we start drawing the left border,
where do we start drawing the playfield, where do we start drawing the right border.
All of these are forced into the visible window (VISIBLE_LEFT_BORDER .. VISIBLE_RIGHT_BORDER).
PLAYFIELD_START and PLAYFIELD_END are in window coordinates. */
static int playfield_start, playfield_end;
static int real_playfield_start, real_playfield_end;
static int sprite_playfield_start;
static bool may_require_hard_way;
static int native_ddf_left, native_ddf_right;
static int pixels_offset;
static int src_pixel;
/* How many pixels in window coordinates which are to the left of the left border. */
static int unpainted;
STATIC_INLINE xcolnr getbgc (bool blank)
{
return (blank || hposblank || ce_is_borderblank(colors_for_drawing.extra)) ? 0 : colors_for_drawing.acolors[0];
}
static void set_res_shift(int shift)
{
int old = res_shift;
res_shift = shift;
if (res_shift != old)
pfield_set_linetoscr();
}
/* Initialize the variables necessary for drawing a line.
* This involves setting up start/stop positions and display window
* borders. */
static void pfield_init_linetoscr (bool border)
{
/* First, get data fetch start/stop in DIW coordinates. */
int ddf_left = dp_for_drawing->plfleft * 2 + DIW_DDF_OFFSET;
int ddf_right = dp_for_drawing->plfright * 2 + DIW_DDF_OFFSET;
int leftborderhidden;
bool expanded = false;
if (border)
ddf_left = DISPLAY_LEFT_SHIFT;
/* Compute datafetch start/stop in pixels; native display coordinates. */
native_ddf_left = coord_hw_to_window_x (ddf_left);
native_ddf_right = coord_hw_to_window_x (ddf_right);
if (native_ddf_left < 0)
native_ddf_left = 0;
if (native_ddf_right < native_ddf_left)
native_ddf_right = native_ddf_left;
int linetoscr_diw_start = dp_for_drawing->diwfirstword;
int linetoscr_diw_end = dp_for_drawing->diwlastword;
/* Perverse cases happen. */
if (linetoscr_diw_end < linetoscr_diw_start)
linetoscr_diw_end = linetoscr_diw_start;
set_res_shift(lores_shift - bplres);
playfield_start = linetoscr_diw_start;
playfield_end = linetoscr_diw_end;
if (playfield_start < native_ddf_left)
playfield_start = native_ddf_left;
if (playfield_end > native_ddf_right)
playfield_end = native_ddf_right;
if (playfield_start < visible_left_border)
playfield_start = visible_left_border;
if (playfield_start > visible_right_border)
playfield_start = visible_right_border;
if (playfield_end < visible_left_border)
playfield_end = visible_left_border;
if (playfield_end > visible_right_border)
playfield_end = visible_right_border;
real_playfield_start = playfield_start;
sprite_playfield_start = playfield_start;
real_playfield_end = playfield_end;
// Sprite hpos don't include DIW_DDF_OFFSET and can appear 1 lores pixel
// before first bitplane pixel appears.
// This means "bordersprite" condition is possible under OCS/ECS too. Argh!
if (dip_for_drawing->nr_sprites) {
if (!ce_is_borderblank(colors_for_drawing.extra)) {
/* bordersprite off or not supported: sprites are visible until diw_end */
if (playfield_end < linetoscr_diw_end && MAX_STOP > playfield_end) {
playfield_end = linetoscr_diw_end;
}
int left = coord_hw_to_window_x (dp_for_drawing->plfleft * 2);
if (left < visible_left_border)
left = visible_left_border;
if (left < playfield_start && left >= linetoscr_diw_start) {
playfield_start = left;
}
} else {
sprite_playfield_start = 0;
if (playfield_end < linetoscr_diw_end && MAX_STOP > playfield_end) {
playfield_end = linetoscr_diw_end;
}
}
}
// if BPLCON4 is non-zero: it will affect background color until end of DIW.
if (dp_for_drawing->xor_seen) {
if (playfield_end < linetoscr_diw_end && MAX_STOP > playfield_end) {
playfield_end = linetoscr_diw_end;
expanded = true;
}
}
may_require_hard_way = false;
if (dp_for_drawing->bordersprite_seen && !ce_is_borderblank(colors_for_drawing.extra) && dip_for_drawing->nr_sprites) {
int min = visible_right_border, max = visible_left_border, i;
for (i = 0; i < dip_for_drawing->nr_sprites; i++) {
int x;
x = curr_sprite_entries[dip_for_drawing->first_sprite_entry + i].pos;
if (x < min)
min = x;
// include max extra pixels, sprite may be 2x or 4x size: 4x - 1.
x = curr_sprite_entries[dip_for_drawing->first_sprite_entry + i].max + (4 - 1);
if (x > max)
max = x;
}
min = coord_hw_to_window_x (min >> sprite_buffer_res) + (DIW_DDF_OFFSET << lores_shift);
max = coord_hw_to_window_x (max >> sprite_buffer_res) + (DIW_DDF_OFFSET << lores_shift);
if (min < playfield_start)
playfield_start = min;
if (playfield_start < visible_left_border)
playfield_start = visible_left_border;
if (max > playfield_end)
playfield_end = max;
if (playfield_end > visible_right_border)
playfield_end = visible_right_border;
sprite_playfield_start = 0;
may_require_hard_way = true;
}
unpainted = visible_left_border < playfield_start ? 0 : visible_left_border - playfield_start;
unpainted = res_shift_from_window (unpainted);
int first_x = sprite_first_x;
int last_x = sprite_last_x;
if (first_x < last_x) {
if (dp_for_drawing->bordersprite_seen && !ce_is_borderblank(colors_for_drawing.extra)) {
if (first_x > visible_left_border)
first_x = visible_left_border;
if (last_x < visible_right_border)
last_x = visible_right_border;
}
if (first_x < 0)
first_x = 0;
if (last_x > MAX_PIXELS_PER_LINE - 2)
last_x = MAX_PIXELS_PER_LINE - 2;
if (first_x < last_x)
memset (spritepixels + first_x, 0, sizeof (struct spritepixelsbuf) * (last_x - first_x + 1));
}
sprite_last_x = 0;
sprite_first_x = MAX_PIXELS_PER_LINE - 1;
/* Now, compute some offsets. */
ddf_left -= DISPLAY_LEFT_SHIFT;
if (ddf_left < 0)
ddf_left = 0;
pixels_offset = MAX_PIXELS_PER_LINE - (ddf_left << bplres);
ddf_left <<= bplres;
leftborderhidden = playfield_start - native_ddf_left;
src_pixel = MAX_PIXELS_PER_LINE + res_shift_from_window (leftborderhidden);
if (dip_for_drawing->nr_sprites == 0 && !expanded)
return;
/* We need to clear parts of apixels. */
if (linetoscr_diw_start < native_ddf_left) {
int size = res_shift_from_window (native_ddf_left - linetoscr_diw_start);
linetoscr_diw_start = native_ddf_left;
memset (pixdata.apixels + MAX_PIXELS_PER_LINE - size, 0, size);
}
if (linetoscr_diw_end > native_ddf_right) {
int pos = res_shift_from_window (native_ddf_right - native_ddf_left);
int size = res_shift_from_window (linetoscr_diw_end - native_ddf_right);
if (pos + size > MAX_PIXELS_PER_LINE)
size = MAX_PIXELS_PER_LINE - pos;
if (size > 0)
memset (pixdata.apixels + MAX_PIXELS_PER_LINE + pos, 0, size);
linetoscr_diw_start = native_ddf_left;
}
}
// erase sprite graphics in pixdata if they were outside of ddf
static void pfield_erase_hborder_sprites (void)
{
if (sprite_first_x < native_ddf_left) {
int size = res_shift_from_window (native_ddf_left - sprite_first_x);
memset (pixdata.apixels + MAX_PIXELS_PER_LINE - size, 0, size);
}
if (sprite_last_x > native_ddf_right) {
int pos = res_shift_from_window (native_ddf_right - native_ddf_left);
int size = res_shift_from_window (sprite_last_x - native_ddf_right);
if (pos + size > MAX_PIXELS_PER_LINE)
size = MAX_PIXELS_PER_LINE - pos;
if (size > 0)
memset (pixdata.apixels + MAX_PIXELS_PER_LINE + pos, 0, size);
}
}
// erase whole viewable area if sprite in upper or lower border
static void pfield_erase_vborder_sprites (void)
{
if (visible_right_border <= visible_left_border)
return;
int pos = 0;
int size = 0;
if (visible_left_border < native_ddf_left) {
size = res_shift_from_window (native_ddf_left - visible_left_border);
pos = -size;
}
if (visible_right_border > native_ddf_left)
size += res_shift_from_window (visible_right_border - native_ddf_left);
memset (pixdata.apixels + MAX_PIXELS_PER_LINE - pos, 0, size);
}
STATIC_INLINE void fill_line_16 (uae_u8 *buf, int start, int stop, bool blank)
{
uae_u16 *b = (uae_u16 *)buf;
unsigned int i;
unsigned int rem = 0;
xcolnr col = getbgc (blank);
if (((uintptr_t)&b[start]) & 3)
b[start++] = (uae_u16) col;
if (start >= stop)
return;
if (((uintptr_t)&b[stop]) & 3) {
rem++;
stop--;
}
for (i = start; i < stop; i += 2) {
uae_u32 *b2 = (uae_u32 *)&b[i];
*b2 = col;
}
if (rem)
b[stop] = (uae_u16)col;
}
static void pfield_do_fill_line (int start, int stop, bool blank)
{
if (stop <= start)
return;
fill_line_16 (xlinebuffer, start, stop, blank);
}
static void fill_line2 (int startpos, int len)
{
int shift;
int nints, nrem;
int *start;
xcolnr val;
shift = 1;
nints = len >> (2 - shift);
nrem = nints & 7;
nints &= ~7;
start = (int *)(((uae_u8*)xlinebuffer) + (startpos << shift));
val = getbgc (false);
for (; nints > 0; nints -= 8, start += 8) {
*start = val;
*(start+1) = val;
*(start+2) = val;
*(start+3) = val;
*(start+4) = val;
*(start+5) = val;
*(start+6) = val;
*(start+7) = val;
}
switch (nrem) {
case 7:
*start++ = val;
case 6:
*start++ = val;
case 5:
*start++ = val;
case 4:
*start++ = val;
case 3:
*start++ = val;
case 2:
*start++ = val;
case 1:
*start = val;
}
}
STATIC_INLINE void fill_line_border (int lineno)
{
int lastpos = visible_left_border;
int endpos = visible_left_border + gfxvidinfo.drawbuffer.outwidth;
// full hblank
if (hposblank) {
hposblank = 3;
fill_line2(lastpos, gfxvidinfo.drawbuffer.outwidth);
return;
}
// hblank not visible
if (0 <= lastpos && MAX_STOP >= endpos) {
fill_line2(lastpos, gfxvidinfo.drawbuffer.outwidth);
return;
}
if (lastpos < endpos) {
pfield_do_fill_line(lastpos, endpos, true);
}
}
static uae_u8 render_sprites (int pos, int dualpf, uae_u8 apixel, int aga)
{
struct spritepixelsbuf *spb = &spritepixels[pos];
unsigned int v = spb->data;
int *shift_lookup = bpldualpfpri ? dblpf_ms2 : dblpf_ms1;
int maskshift, plfmask;
/* The value in the shift lookup table is _half_ the shift count we
need. This is because we can't shift 32 bits at once (undefined
behaviour in C). */
if(dualpf) {
maskshift = shift_lookup[apixel];
plfmask = (plf_sprite_mask >> maskshift) >> maskshift;
v &= ~plfmask;
} else {
if(apixel)
v &= plf_sprite_mask_n16;
}
/* Extra 1 sprite pixel at DDFSTRT is only possible if at least 1 plane is active */
if ((bplplanecnt > 0 || pos >= sprite_playfield_start) && (v != 0)) {
unsigned int col;
if (spb->attach) {
int offs = sprite_bit[v];
if (spb->stdata & offs) {
if (aga)
col = sprite_col_at[v] + sbasecol[1];
else
col = sprite_col_at[v] + 16;
return col;
}
}
if (aga) {
int offs = sprite_bit[v];
if (offs & 0x55)
col = sprite_col_nat[v] + sbasecol[0];
else
col = sprite_col_nat[v] + sbasecol[1];
} else {
col = sprite_col_nat[v] + 16;
}
return col;
}
return 0;
}
#include "linetoscr.cpp"
/* ECS SuperHires special cases */
#define PUTBPIX(x) buf[dpix] = (x);
STATIC_INLINE uae_u32 shsprite (int dpix, uae_u32 spix_val, uae_u32 v, int spr)
{
uae_u8 sprcol;
uae_u16 scol;
if (!spr)
return v;
sprcol = render_sprites (dpix, 0, spix_val, 0);
if (!sprcol)
return v;
/* good enough for now.. */
scol = colors_for_drawing.color_regs_ecs[sprcol] & 0xccc;
scol |= scol >> 2;
return xcolors[scol];
}
static int NOINLINE linetoscr_16_sh_func(int spix, int dpix, int stoppos, int spr)
{
uae_u16 *buf = (uae_u16 *) xlinebuffer;
while (dpix < stoppos) {
uae_u16 spix_val1, spix_val2;
uae_u16 v;
int off;
spix_val1 = pixdata.apixels[spix++];
spix_val2 = pixdata.apixels[spix++];
off = ((spix_val2 & 3) * 4) + (spix_val1 & 3) + ((spix_val1 | spix_val2) & 16);
v = (colors_for_drawing.color_regs_ecs[off] & 0xccc) << 0;
v |= v >> 2;
PUTBPIX(shsprite (dpix, spix_val1, xcolors[v], spr));
dpix++;
v = (colors_for_drawing.color_regs_ecs[off] & 0x333) << 2;
v |= v >> 2;
PUTBPIX(shsprite (dpix, spix_val2, xcolors[v], spr));
dpix++;
}
return spix;
}
static int linetoscr_16_sh_spr(int spix, int dpix, int stoppos)
{
return linetoscr_16_sh_func(spix, dpix, stoppos, true);
}
static int linetoscr_16_sh(int spix, int dpix, int stoppos)
{
return linetoscr_16_sh_func(spix, dpix, stoppos, false);
}
static int NOINLINE linetoscr_16_shrink1_sh_func(int spix, int dpix, int stoppos, int spr)
{
uae_u16 *buf = (uae_u16 *) xlinebuffer;
while (dpix < stoppos) {
uae_u16 spix_val1, spix_val2;
uae_u16 v;
int off;
spix_val1 = pixdata.apixels[spix++];
spix_val2 = pixdata.apixels[spix++];
off = ((spix_val2 & 3) * 4) + (spix_val1 & 3) + ((spix_val1 | spix_val2) & 16);
v = (colors_for_drawing.color_regs_ecs[off] & 0xccc);
v |= v >> 2;
PUTBPIX(shsprite (dpix, spix_val1, xcolors[v], spr));
dpix++;
}
return spix;
}
static int linetoscr_16_shrink1_sh_spr(int spix, int dpix, int stoppos)
{
return linetoscr_16_shrink1_sh_func(spix, dpix, stoppos, true);
}
static int linetoscr_16_shrink1_sh(int spix, int dpix, int stoppos)
{
return linetoscr_16_shrink1_sh_func(spix, dpix, stoppos, false);
}
static int NOINLINE linetoscr_16_shrink2_sh_func(int spix, int dpix, int stoppos, int spr)
{
uae_u16 *buf = (uae_u16 *) xlinebuffer;
while (dpix < stoppos) {
uae_u16 spix_val1, spix_val2;
uae_u16 v;
int off;
spix_val1 = pixdata.apixels[spix++];
spix_val2 = pixdata.apixels[spix++];
off = ((spix_val2 & 3) * 4) + (spix_val1 & 3) + ((spix_val1 | spix_val2) & 16);
v = (colors_for_drawing.color_regs_ecs[off] & 0xccc);
v |= v >> 2;
PUTBPIX(shsprite (dpix, spix_val1, xcolors[v], spr));
spix+=2;
dpix++;
}
return spix;
}
static int linetoscr_16_shrink2_sh_spr(int spix, int dpix, int stoppos)
{
return linetoscr_16_shrink2_sh_func(spix, dpix, stoppos, true);
}
static int linetoscr_16_shrink2_sh(int spix, int dpix, int stoppos)
{
return linetoscr_16_shrink2_sh_func(spix, dpix, stoppos, false);
}
typedef int(*call_linetoscr)(int spix, int dpix, int dpix_end);
static call_linetoscr pfield_do_linetoscr_normal;
static call_linetoscr pfield_do_linetoscr_sprite;
static call_linetoscr pfield_do_linetoscr_spriteonly;
static void pfield_do_linetoscr(int start, int stop, bool blank)
{
src_pixel = pfield_do_linetoscr_normal(src_pixel, start, stop);
}
static void pfield_do_linetoscr_spr(int start, int stop, bool blank)
{
src_pixel = pfield_do_linetoscr_sprite(src_pixel, start, stop);
}
static int pfield_do_nothing(int a, int b, int c)
{
return a;
}
static void pfield_set_linetoscr (void)
{
p_acolors = colors_for_drawing.acolors;
p_xcolors = xcolors;
bpland = 0xff;
if (bplbypass) {
p_acolors = direct_colors_for_drawing.acolors;
}
spritepixels = spritepixels_buffer;
pfield_do_linetoscr_spriteonly = pfield_do_nothing;
if (currprefs.chipset_mask & CSMASK_AGA) {
if (res_shift == 0) {
pfield_do_linetoscr_normal = linetoscr_16_aga;
pfield_do_linetoscr_sprite = linetoscr_16_aga_spr;
pfield_do_linetoscr_spriteonly = linetoscr_16_aga_spronly;
} else if (res_shift == 2) {
} else if (res_shift == 1) {
pfield_do_linetoscr_normal = linetoscr_16_stretch1_aga;
pfield_do_linetoscr_sprite = linetoscr_16_stretch1_aga_spr;
pfield_do_linetoscr_spriteonly = linetoscr_16_stretch1_aga_spronly;
} else if (res_shift == -1) {
pfield_do_linetoscr_normal = linetoscr_16_shrink1_aga;
pfield_do_linetoscr_sprite = linetoscr_16_shrink1_aga_spr;
pfield_do_linetoscr_spriteonly = linetoscr_16_shrink1_aga_spronly;
} else if (res_shift == -2) {
pfield_do_linetoscr_normal = linetoscr_16_shrink2_aga;
pfield_do_linetoscr_sprite = linetoscr_16_shrink2_aga_spr;
pfield_do_linetoscr_spriteonly = linetoscr_16_shrink2_aga_spronly;
}
}
if (!(currprefs.chipset_mask & CSMASK_AGA) && ecsshres) {
if (res_shift == 0) {
pfield_do_linetoscr_normal = linetoscr_16_sh;
pfield_do_linetoscr_sprite = linetoscr_16_sh_spr;
} else if (res_shift == -1) {
pfield_do_linetoscr_normal = linetoscr_16_shrink1_sh;
pfield_do_linetoscr_sprite = linetoscr_16_shrink1_sh_spr;
} else if (res_shift == -2) {
pfield_do_linetoscr_normal = linetoscr_16_shrink2_sh;
pfield_do_linetoscr_sprite = linetoscr_16_shrink2_sh_spr;
}
}
if (!(currprefs.chipset_mask & CSMASK_AGA) && !ecsshres) {
if (res_shift == 0) {
pfield_do_linetoscr_normal = linetoscr_16;
pfield_do_linetoscr_sprite = linetoscr_16_spr;
} else if (res_shift == 2) {
} else if (res_shift == 1) {
pfield_do_linetoscr_normal = linetoscr_16_stretch1;
pfield_do_linetoscr_sprite = linetoscr_16_stretch1_spr;
} else if (res_shift == -1) {
pfield_do_linetoscr_normal = linetoscr_16_shrink1;
pfield_do_linetoscr_sprite = linetoscr_16_shrink1_spr;
}
}
}
// left or right AGA border sprite
static void pfield_do_linetoscr_bordersprite_aga (int start, int stop, bool blank)
{
if (blank) {
pfield_do_fill_line (start, stop, blank);
return;
}
pfield_do_linetoscr_spriteonly(src_pixel, start, stop);
}
static void dummy_worker (int start, int stop, bool blank)
{
}
#ifdef ARMV6T2
STATIC_INLINE int DECODE_HAM8_1(int col, int pv)
{
__asm__ (
"ubfx %[pv], %[pv], #3, #5 \n\t"
"bfi %[col], %[pv], #0, #5 \n\t"
: [col] "+r" (col) , [pv] "+r" (pv) );
return col;
}
STATIC_INLINE int DECODE_HAM8_2(int col, int pv)
{
__asm__ (
"ubfx %[pv], %[pv], #3, #5 \n\t"
"bfi %[col], %[pv], #11, #5 \n\t"
: [col] "+r" (col) , [pv] "+r" (pv) );
return col;
}
STATIC_INLINE int DECODE_HAM8_3(int col, int pv)
{
__asm__ (
"ubfx %[pv], %[pv], #2, #6 \n\t"
"bfi %[col], %[pv], #5, #6 \n\t"
: [col] "+r" (col) , [pv] "+r" (pv) );
return col;
}
STATIC_INLINE int DECODE_HAM6_1(int col, int pv)
{
__asm__ (
"bfi %[col], %[pv], #1, #4 \n\t"
: [col] "+r" (col) : [pv] "r" (pv) );
return (col);
}
STATIC_INLINE int DECODE_HAM6_2(int col, int pv)
{
__asm__ (
"bfi %[col], %[pv], #12, #4 \n\t"
: [col] "+r" (col) : [pv] "r" (pv) );
return (col);
}
STATIC_INLINE int DECODE_HAM6_3(int col, int pv)
{
__asm__ (
"bfi %[col], %[pv], #7, #4 \n\t"
: [col] "+r" (col) : [pv] "r" (pv) );
return (col);
}
#endif
static int ham_decode_pixel;
static uae_u16 ham_lastcolor;
/* Decode HAM in the invisible portion of the display (left of VISIBLE_LEFT_BORDER),
* but don't draw anything in. This is done to prepare HAM_LASTCOLOR for later,
* when decode_ham runs.
*
*/
static void init_ham_decoding (void)
{
int unpainted_amiga = unpainted;
ham_decode_pixel = src_pixel;
ham_lastcolor = colors_for_drawing.acolors[0];
if (!bplham) {
if (unpainted_amiga > 0) {
int pv = pixdata.apixels[ham_decode_pixel + unpainted_amiga - 1];
if (aga_mode)
ham_lastcolor = colors_for_drawing.acolors[pv ^ bplxor];
else
ham_lastcolor = colors_for_drawing.acolors[pv];
}
} else if (aga_mode) {
if (bplplanecnt >= 7) { /* AGA mode HAM8 */
while (unpainted_amiga-- > 0) {
int pv = pixdata.apixels[ham_decode_pixel++] ^ bplxor;
switch (pv & 0x3)
{
case 0x0: ham_lastcolor = colors_for_drawing.acolors[pv >> 2]; break;
#ifdef ARMV6T2
case 0x1: ham_lastcolor = DECODE_HAM8_1(ham_lastcolor, pv); break;
case 0x2: ham_lastcolor = DECODE_HAM8_2(ham_lastcolor, pv); break;
case 0x3: ham_lastcolor = DECODE_HAM8_3(ham_lastcolor, pv); break;
#else
case 0x1: ham_lastcolor &= 0xFFFF03; ham_lastcolor |= (pv & 0xFC); break;
case 0x2: ham_lastcolor &= 0x03FFFF; ham_lastcolor |= (pv & 0xFC) << 16; break;
case 0x3: ham_lastcolor &= 0xFF03FF; ham_lastcolor |= (pv & 0xFC) << 8; break;
#endif
}
}
} else { /* AGA mode HAM6 */
while (unpainted_amiga-- > 0) {
int pv = pixdata.apixels[ham_decode_pixel++] ^ bplxor;
uae_u32 pc = ((pv & 0xf) << 0) | ((pv & 0xf) << 4);
switch (pv & 0x30)
{
case 0x00: ham_lastcolor = colors_for_drawing.acolors[pv]; break;
#ifdef ARMV6T2
case 0x10: ham_lastcolor = DECODE_HAM8_1(ham_lastcolor, pc); break;
case 0x20: ham_lastcolor = DECODE_HAM8_2(ham_lastcolor, pc); break;
case 0x30: ham_lastcolor = DECODE_HAM8_3(ham_lastcolor, pc); break;
#else
case 0x10: ham_lastcolor &= 0xFFFF00; ham_lastcolor |= (pc & 0xF) << 4; break;
case 0x20: ham_lastcolor &= 0x00FFFF; ham_lastcolor |= (pc & 0xF) << 20; break;
case 0x30: ham_lastcolor &= 0xFF00FF; ham_lastcolor |= (pc & 0xF) << 12; break;
#endif
}
}
}
} else {
/* OCS/ECS mode HAM6 */
while (unpainted_amiga-- > 0) {
int pv = pixdata.apixels[ham_decode_pixel++];
switch (pv & 0x30)
{
case 0x00: ham_lastcolor = colors_for_drawing.acolors[pv]; break;
#ifdef ARMV6T2
case 0x10: ham_lastcolor = DECODE_HAM6_1(ham_lastcolor, pv); break;
case 0x20: ham_lastcolor = DECODE_HAM6_2(ham_lastcolor, pv); break;
case 0x30: ham_lastcolor = DECODE_HAM6_3(ham_lastcolor, pv); break;
#else
case 0x10: ham_lastcolor &= 0xFF0; ham_lastcolor |= (pv & 0xF); break;
case 0x20: ham_lastcolor &= 0x0FF; ham_lastcolor |= (pv & 0xF) << 8; break;
case 0x30: ham_lastcolor &= 0xF0F; ham_lastcolor |= (pv & 0xF) << 4; break;
#endif
}
}
}
}
static void decode_ham (int pix, int stoppos, bool blank)
{
int todraw_amiga = res_shift_from_window (stoppos - pix);
if (!bplham) {
while (todraw_amiga-- > 0) {
int pv = pixdata.apixels[ham_decode_pixel];
if (aga_mode)
ham_lastcolor = colors_for_drawing.acolors[pv ^ bplxor];
else
ham_lastcolor = colors_for_drawing.acolors[pv];
ham_linebuf[ham_decode_pixel++] = ham_lastcolor;
}
} else if (aga_mode) {
if (bplplanecnt >= 7) { /* AGA mode HAM8 */
while (todraw_amiga-- > 0) {
int pv = pixdata.apixels[ham_decode_pixel] ^ bplxor;
switch (pv & 0x3)
{
case 0x0: ham_lastcolor = colors_for_drawing.acolors[pv >> 2]; break;
#ifdef ARMV6T2
case 0x1: ham_lastcolor = DECODE_HAM8_1(ham_lastcolor, pv); break;
case 0x2: ham_lastcolor = DECODE_HAM8_2(ham_lastcolor, pv); break;
case 0x3: ham_lastcolor = DECODE_HAM8_3(ham_lastcolor, pv); break;
#else
case 0x1: ham_lastcolor &= 0xFFFF03; ham_lastcolor |= (pv & 0xFC); break;
case 0x2: ham_lastcolor &= 0x03FFFF; ham_lastcolor |= (pv & 0xFC) << 16; break;
case 0x3: ham_lastcolor &= 0xFF03FF; ham_lastcolor |= (pv & 0xFC) << 8; break;
#endif
}
ham_linebuf[ham_decode_pixel++] = ham_lastcolor;
}
} else { /* AGA mode HAM6 */
while (todraw_amiga-- > 0) {
int pv = pixdata.apixels[ham_decode_pixel] ^ bplxor;
uae_u32 pc = ((pv & 0xf) << 0) | ((pv & 0xf) << 4);
switch (pv & 0x30)
{
case 0x00: ham_lastcolor = colors_for_drawing.acolors[pv]; break;
#ifdef ARMV6T2
case 0x10: ham_lastcolor = DECODE_HAM8_1(ham_lastcolor, pc); break;
case 0x20: ham_lastcolor = DECODE_HAM8_2(ham_lastcolor, pc); break;
case 0x30: ham_lastcolor = DECODE_HAM8_3(ham_lastcolor, pc); break;
#else
case 0x10: ham_lastcolor &= 0xFFFF00; ham_lastcolor |= (pc & 0xF) << 4; break;
case 0x20: ham_lastcolor &= 0x00FFFF; ham_lastcolor |= (pc & 0xF) << 20; break;
case 0x30: ham_lastcolor &= 0xFF00FF; ham_lastcolor |= (pc & 0xF) << 12; break;
#endif
}
ham_linebuf[ham_decode_pixel++] = ham_lastcolor;
}
}
} else {
/* OCS/ECS mode HAM6 */
while (todraw_amiga-- > 0) {
int pv = pixdata.apixels[ham_decode_pixel];
switch (pv & 0x30)
{
case 0x00: ham_lastcolor = colors_for_drawing.acolors[pv]; break;
#ifdef ARMV6T2
case 0x10: ham_lastcolor = DECODE_HAM6_1(ham_lastcolor, pv); break;
case 0x20: ham_lastcolor = DECODE_HAM6_2(ham_lastcolor, pv); break;
case 0x30: ham_lastcolor = DECODE_HAM6_3(ham_lastcolor, pv); break;
#else
case 0x10: ham_lastcolor &= 0xFF0; ham_lastcolor |= (pv & 0xF); break;
case 0x20: ham_lastcolor &= 0x0FF; ham_lastcolor |= (pv & 0xF) << 8; break;
case 0x30: ham_lastcolor &= 0xF0F; ham_lastcolor |= (pv & 0xF) << 4; break;
#endif
}
ham_linebuf[ham_decode_pixel++] = ham_lastcolor;
}
}
}
static void gen_pfield_tables (void)
{
int i;
for (i = 0; i < 256; i++) {
int plane1 = ((i >> 0) & 1) | ((i >> 1) & 2) | ((i >> 2) & 4) | ((i >> 3) & 8);
int plane2 = ((i >> 1) & 1) | ((i >> 2) & 2) | ((i >> 3) & 4) | ((i >> 4) & 8);
dblpf_2nd1[i] = plane1 == 0 && plane2 != 0;
dblpf_2nd2[i] = plane2 != 0;
dblpf_ind1_aga[i] = plane1 == 0 ? plane2 : plane1;
dblpf_ind2_aga[i] = plane2 == 0 ? plane1 : plane2;
dblpf_ms1[i] = plane1 == 0 ? (plane2 == 0 ? 16 : 8) : 0;
dblpf_ms2[i] = plane2 == 0 ? (plane1 == 0 ? 16 : 0) : 8;
if (plane2 > 0)
plane2 += 8;
dblpf_ind1[i] = i >= 128 ? i & 0x7F : (plane1 == 0 ? plane2 : plane1);
dblpf_ind2[i] = i >= 128 ? i & 0x7F : (plane2 == 0 ? plane1 : plane2);
// Hack for OCS/ECS-only dualplayfield chipset bug.
// If PF2P2 is invalid (>5), playfield color becomes transparent but
// playfield still hides playfield under it! (if plfpri is set)
if (i & 64) {
dblpf_ind2[i] = 0;
dblpf_ind1[i] = 0;
}
clxtab[i] = ((((i & 3) && (i & 12)) << 9)
| (((i & 3) && (i & 48)) << 10)
| (((i & 3) && (i & 192)) << 11)
| (((i & 12) && (i & 48)) << 12)
| (((i & 12) && (i & 192)) << 13)
| (((i & 48) && (i & 192)) << 14));
}
for(i=0; i<65536; ++i)
{
sprite_col_nat[i] =
(i & 0x0003) ? ((i >> 0) & 3) + 0 :
(i & 0x000C) ? ((i >> 2) & 3) + 0 :
(i & 0x0030) ? ((i >> 4) & 3) + 4 :
(i & 0x00C0) ? ((i >> 6) & 3) + 4 :
(i & 0x0300) ? ((i >> 8) & 3) + 8 :
(i & 0x0C00) ? ((i >> 10) & 3) + 8 :
(i & 0x3000) ? ((i >> 12) & 3) + 12 :
(i & 0xC000) ? ((i >> 14) & 3) + 12 : 0;
sprite_col_at[i] =
(i & 0x000F) ? ((i >> 0) & 0x000F) :
(i & 0x00F0) ? ((i >> 4) & 0x000F) :
(i & 0x0F00) ? ((i >> 8) & 0x000F) :
(i & 0xF000) ? ((i >> 12) & 0x000F) : 0;
sprite_bit[i] =
(i & 0x0003) ? 0x01 :
(i & 0x000C) ? 0x02 :
(i & 0x0030) ? 0x04 :
(i & 0x00C0) ? 0x08 :
(i & 0x0300) ? 0x10 :
(i & 0x0C00) ? 0x20 :
(i & 0x3000) ? 0x40 :
(i & 0xC000) ? 0x80 : 0;
}
}
/* When looking at this function and the ones that inline it, bear in mind
what an optimizing compiler will do with this code. All callers of this
function only pass in constant arguments (except for E). This means
that many of the if statements will go away completely after inlining. */
STATIC_INLINE void draw_sprites_1 (struct sprite_entry *e, const int dualpf, const int has_attach)
{
uae_u16 *buf = spixels + e->first_pixel;
uae_u8 *stbuf = spixstate.bytes + e->first_pixel;
int spr_pos, pos;
int epos = e->pos;
int emax = e->max;
buf -= epos;
stbuf -= epos;
spr_pos = epos + ((DIW_DDF_OFFSET - DISPLAY_LEFT_SHIFT) << sprite_buffer_res);
if (spr_pos < sprite_first_x)
sprite_first_x = spr_pos;
for (pos = epos; pos < emax; pos++, spr_pos++) {
if (spr_pos >= 0 && spr_pos < MAX_PIXELS_PER_LINE) {
spritepixels[spr_pos].data = buf[pos];
spritepixels[spr_pos].stdata = stbuf[pos];
spritepixels[spr_pos].attach = has_attach;
}
}
if (spr_pos > sprite_last_x)
sprite_last_x = spr_pos;
}
/* See comments above. Do not touch if you don't know what's going on.
* (We do _not_ want the following to be inlined themselves). */
/* lores bitplane, lores sprites */
static void NOINLINE draw_sprites_normal_sp_nat (struct sprite_entry *e) { draw_sprites_1 (e, 0, 0); }
static void NOINLINE draw_sprites_normal_dp_nat (struct sprite_entry *e) { draw_sprites_1 (e, 1, 0); }
static void NOINLINE draw_sprites_normal_sp_at (struct sprite_entry *e) { draw_sprites_1 (e, 0, 1); }
static void NOINLINE draw_sprites_normal_dp_at (struct sprite_entry *e) { draw_sprites_1 (e, 1, 1); }
/* not very optimized */
STATIC_INLINE void draw_sprites_aga (struct sprite_entry *e, int aga)
{
draw_sprites_1 (e, bpldualpf, e->has_attached);
}
STATIC_INLINE void draw_sprites_ecs (struct sprite_entry *e)
{
if (e->has_attached) {
if (bpldualpf)
draw_sprites_normal_dp_at (e);
else
draw_sprites_normal_sp_at (e);
} else {
if (bpldualpf)
draw_sprites_normal_dp_nat (e);
else
draw_sprites_normal_sp_nat (e);
}
}
/* clear possible bitplane data outside DIW area */
static void clear_bitplane_border_aga (void)
{
int len, shift = res_shift;
uae_u8 v = 0;
if (shift < 0) {
shift = -shift;
len = (real_playfield_start - playfield_start) << shift;
memset (pixdata.apixels + pixels_offset + (playfield_start << shift), v, len);
len = (playfield_end - real_playfield_end) << shift;
memset (pixdata.apixels + pixels_offset + (real_playfield_end << shift), v, len);
} else {
len = (real_playfield_start - playfield_start) >> shift;
memset (pixdata.apixels + pixels_offset + (playfield_start >> shift), v, len);
len = (playfield_end - real_playfield_end) >> shift;
memset (pixdata.apixels + pixels_offset + (real_playfield_end >> shift), v, len);
}
}
static void weird_bitplane_fix (int start, int end)
{
int sh = lores_shift;
uae_u8 *p = pixdata.apixels + pixels_offset;
start >>= sh;
end >>= sh;
if (bplplanecnt == 5 && !bpldualpf) {
/* emulate OCS/ECS only undocumented "SWIV" hardware feature */
for (int i = start; i < end; i++) {
if (p[i] & 16)
p[i] = 16;
}
} else if (bpldualpf && bpldualpfpri) {
/* in dualplayfield mode this feature is even more strange.. */
for (int i = start; i < end; i++) {
if (p[i] & (2 | 8 | 32))
p[i] |= 0x40;
}
} else if (bpldualpf && !bpldualpfpri) {
for (int i = start; i < end; i++) {
p[i] &= ~(2 | 8 | 32);
}
}
}
/* We use the compiler's inlining ability to ensure that PLANES is in effect a compile time
constant. That will cause some unnecessary code to be optimized away.
Don't touch this if you don't know what you are doing. */
#define MERGE(a,b,mask,shift) do {\
uae_u32 tmp = mask & (a ^ (b >> shift)); \
a ^= tmp; \
b ^= (tmp << shift); \
} while (0)
#define GETLONG(P) (*(uae_u32 *)P)
#define DATA_POINTER(n) (line_data[lineno] + (n) * MAX_WORDS_PER_LINE * 2)
#if defined(USE_ARMNEON) && !defined(ANDROID)
// FIXME: these neon helper functions caused text rel problem on android
#ifdef __cplusplus
extern "C" {
#endif
void ARM_doline_n1(uae_u32 *pixels, int wordcount, int lineno);
void NEON_doline_n2(uae_u32 *pixels, int wordcount, int lineno);
void NEON_doline_n3(uae_u32 *pixels, int wordcount, int lineno);
void NEON_doline_n4(uae_u32 *pixels, int wordcount, int lineno);
void NEON_doline_n6(uae_u32 *pixels, int wordcount, int lineno);
void NEON_doline_n8(uae_u32 *pixels, int wordcount, int lineno);
#ifdef __cplusplus
}
#endif
static void pfield_doline_n0 (uae_u32 *pixels, int wordcount, int lineno)
{
memset(pixels, 0, wordcount << 5);
}
#define MERGE_0(a,b,mask,shift) {\
uae_u32 tmp = mask & (b>>shift); \
a = tmp; \
b ^= (tmp << shift); \
}
static void pfield_doline_n5 (uae_u32 *pixels, int wordcount, int lineno)
{
uae_u8 *real_bplpt[5];
real_bplpt[0] = DATA_POINTER (0);
real_bplpt[1] = DATA_POINTER (1);
real_bplpt[2] = DATA_POINTER (2);
real_bplpt[3] = DATA_POINTER (3);
real_bplpt[4] = DATA_POINTER (4);
while (wordcount-- > 0) {
uae_u32 b0,b1,b2,b3,b4,b5,b6,b7;
b3 = GETLONG ((uae_u32 *)real_bplpt[4]); real_bplpt[4] += 4;
b4 = GETLONG ((uae_u32 *)real_bplpt[3]); real_bplpt[3] += 4;
b5 = GETLONG ((uae_u32 *)real_bplpt[2]); real_bplpt[2] += 4;
b6 = GETLONG ((uae_u32 *)real_bplpt[1]); real_bplpt[1] += 4;
b7 = GETLONG ((uae_u32 *)real_bplpt[0]); real_bplpt[0] += 4;
MERGE_0(b2, b3, 0x55555555, 1);
MERGE (b4, b5, 0x55555555, 1);
MERGE (b6, b7, 0x55555555, 1);
MERGE_0(b0, b2, 0x33333333, 2);
MERGE_0(b1, b3, 0x33333333, 2);
MERGE (b4, b6, 0x33333333, 2);
MERGE (b5, b7, 0x33333333, 2);
MERGE (b0, b4, 0x0f0f0f0f, 4);
MERGE (b1, b5, 0x0f0f0f0f, 4);
MERGE (b2, b6, 0x0f0f0f0f, 4);
MERGE (b3, b7, 0x0f0f0f0f, 4);
MERGE (b0, b1, 0x00ff00ff, 8);
MERGE (b2, b3, 0x00ff00ff, 8);
MERGE (b4, b5, 0x00ff00ff, 8);
MERGE (b6, b7, 0x00ff00ff, 8);
MERGE (b0, b2, 0x0000ffff, 16);
do_put_mem_long(pixels, b0);
do_put_mem_long(pixels + 4, b2);
MERGE (b1, b3, 0x0000ffff, 16);
do_put_mem_long(pixels + 2, b1);
do_put_mem_long(pixels + 6, b3);
MERGE (b4, b6, 0x0000ffff, 16);
do_put_mem_long(pixels + 1, b4);
do_put_mem_long(pixels + 5, b6);
MERGE (b5, b7, 0x0000ffff, 16);
do_put_mem_long(pixels + 3, b5);
do_put_mem_long(pixels + 7, b7);
pixels += 8;
}
}
static void pfield_doline_n7 (uae_u32 *pixels, int wordcount, int lineno)
{
uae_u8 *real_bplpt[7];
real_bplpt[0] = DATA_POINTER (0);
real_bplpt[1] = DATA_POINTER (1);
real_bplpt[2] = DATA_POINTER (2);
real_bplpt[3] = DATA_POINTER (3);
real_bplpt[4] = DATA_POINTER (4);
real_bplpt[5] = DATA_POINTER (5);
real_bplpt[6] = DATA_POINTER (6);
while (wordcount-- > 0) {
uae_u32 b0,b1,b2,b3,b4,b5,b6,b7;
b1 = GETLONG ((uae_u32 *)real_bplpt[6]); real_bplpt[6] += 4;
b2 = GETLONG ((uae_u32 *)real_bplpt[5]); real_bplpt[5] += 4;
b3 = GETLONG ((uae_u32 *)real_bplpt[4]); real_bplpt[4] += 4;
b4 = GETLONG ((uae_u32 *)real_bplpt[3]); real_bplpt[3] += 4;
b5 = GETLONG ((uae_u32 *)real_bplpt[2]); real_bplpt[2] += 4;
b6 = GETLONG ((uae_u32 *)real_bplpt[1]); real_bplpt[1] += 4;
b7 = GETLONG ((uae_u32 *)real_bplpt[0]); real_bplpt[0] += 4;
MERGE_0(b0, b1, 0x55555555, 1);
MERGE (b2, b3, 0x55555555, 1);
MERGE (b4, b5, 0x55555555, 1);
MERGE (b6, b7, 0x55555555, 1);
MERGE (b0, b2, 0x33333333, 2);
MERGE (b1, b3, 0x33333333, 2);
MERGE (b4, b6, 0x33333333, 2);
MERGE (b5, b7, 0x33333333, 2);
MERGE (b0, b4, 0x0f0f0f0f, 4);
MERGE (b1, b5, 0x0f0f0f0f, 4);
MERGE (b2, b6, 0x0f0f0f0f, 4);
MERGE (b3, b7, 0x0f0f0f0f, 4);
MERGE (b0, b1, 0x00ff00ff, 8);
MERGE (b2, b3, 0x00ff00ff, 8);
MERGE (b4, b5, 0x00ff00ff, 8);
MERGE (b6, b7, 0x00ff00ff, 8);
MERGE (b0, b2, 0x0000ffff, 16);
do_put_mem_long(pixels, b0);
do_put_mem_long(pixels + 4, b2);
MERGE (b1, b3, 0x0000ffff, 16);
do_put_mem_long(pixels + 2, b1);
do_put_mem_long(pixels + 6, b3);
MERGE (b4, b6, 0x0000ffff, 16);
do_put_mem_long(pixels + 1, b4);
do_put_mem_long(pixels + 5, b6);
MERGE (b5, b7, 0x0000ffff, 16);
do_put_mem_long(pixels + 3, b5);
do_put_mem_long(pixels + 7, b7);
pixels += 8;
}
}
typedef void (*pfield_doline_func)(uae_u32 *, int, int);
static pfield_doline_func pfield_doline_n[9]={
pfield_doline_n0, ARM_doline_n1, NEON_doline_n2, NEON_doline_n3,
NEON_doline_n4, pfield_doline_n5, NEON_doline_n6, pfield_doline_n7,
NEON_doline_n8
};
#else
static uae_u8 *real_bplpt[8];
STATIC_INLINE void pfield_doline_1 (uae_u32 *pixels, int wordcount, int planes)
{
while (wordcount-- > 0) {
uae_u32 b0,b1,b2,b3,b4,b5,b6,b7;
b0 = 0, b1 = 0, b2 = 0, b3 = 0, b4 = 0, b5 = 0, b6 = 0, b7 = 0;
switch (planes) {
case 8: b0 = GETLONG (real_bplpt[7]); real_bplpt[7] += 4;
case 7: b1 = GETLONG (real_bplpt[6]); real_bplpt[6] += 4;
case 6: b2 = GETLONG (real_bplpt[5]); real_bplpt[5] += 4;
case 5: b3 = GETLONG (real_bplpt[4]); real_bplpt[4] += 4;
case 4: b4 = GETLONG (real_bplpt[3]); real_bplpt[3] += 4;
case 3: b5 = GETLONG (real_bplpt[2]); real_bplpt[2] += 4;
case 2: b6 = GETLONG (real_bplpt[1]); real_bplpt[1] += 4;
case 1: b7 = GETLONG (real_bplpt[0]); real_bplpt[0] += 4;
}
MERGE (b0, b1, 0x55555555, 1);
MERGE (b2, b3, 0x55555555, 1);
MERGE (b4, b5, 0x55555555, 1);
MERGE (b6, b7, 0x55555555, 1);
MERGE (b0, b2, 0x33333333, 2);
MERGE (b1, b3, 0x33333333, 2);
MERGE (b4, b6, 0x33333333, 2);
MERGE (b5, b7, 0x33333333, 2);
MERGE (b0, b4, 0x0f0f0f0f, 4);
MERGE (b1, b5, 0x0f0f0f0f, 4);
MERGE (b2, b6, 0x0f0f0f0f, 4);
MERGE (b3, b7, 0x0f0f0f0f, 4);
MERGE (b0, b1, 0x00ff00ff, 8);
MERGE (b2, b3, 0x00ff00ff, 8);
MERGE (b4, b5, 0x00ff00ff, 8);
MERGE (b6, b7, 0x00ff00ff, 8);
MERGE (b0, b2, 0x0000ffff, 16);
do_put_mem_long (pixels, b0);
do_put_mem_long (pixels + 4, b2);
MERGE (b1, b3, 0x0000ffff, 16);
do_put_mem_long (pixels + 2, b1);
do_put_mem_long (pixels + 6, b3);
MERGE (b4, b6, 0x0000ffff, 16);
do_put_mem_long (pixels + 1, b4);
do_put_mem_long (pixels + 5, b6);
MERGE (b5, b7, 0x0000ffff, 16);
do_put_mem_long (pixels + 3, b5);
do_put_mem_long (pixels + 7, b7);
pixels += 8;
}
}
/* See above for comments on inlining. These functions should _not_
be inlined themselves. */
static void NOINLINE pfield_doline_n1 (uae_u32 *data, int count) { pfield_doline_1 (data, count, 1); }
static void NOINLINE pfield_doline_n2 (uae_u32 *data, int count) { pfield_doline_1 (data, count, 2); }
static void NOINLINE pfield_doline_n3 (uae_u32 *data, int count) { pfield_doline_1 (data, count, 3); }
static void NOINLINE pfield_doline_n4 (uae_u32 *data, int count) { pfield_doline_1 (data, count, 4); }
static void NOINLINE pfield_doline_n5 (uae_u32 *data, int count) { pfield_doline_1 (data, count, 5); }
static void NOINLINE pfield_doline_n6 (uae_u32 *data, int count) { pfield_doline_1 (data, count, 6); }
static void NOINLINE pfield_doline_n7 (uae_u32 *data, int count) { pfield_doline_1 (data, count, 7); }
static void NOINLINE pfield_doline_n8 (uae_u32 *data, int count) { pfield_doline_1 (data, count, 8); }
#endif /* USE_ARMNEON */
static void pfield_doline (int lineno)
{
int wordcount = dp_for_drawing->plflinelen;
uae_u32 *data = pixdata.apixels_l + MAX_PIXELS_PER_LINE / 4;
#if defined(USE_ARMNEON) && !defined(ANDROID)
pfield_doline_n[bplplanecnt](data, wordcount, lineno);
#else
real_bplpt[0] = DATA_POINTER (0);
real_bplpt[1] = DATA_POINTER (1);
real_bplpt[2] = DATA_POINTER (2);
real_bplpt[3] = DATA_POINTER (3);
real_bplpt[4] = DATA_POINTER (4);
real_bplpt[5] = DATA_POINTER (5);
real_bplpt[6] = DATA_POINTER (6);
real_bplpt[7] = DATA_POINTER (7);
switch (bplplanecnt) {
default: break;
case 0: memset (data, 0, wordcount * 32); break;
case 1: pfield_doline_n1 (data, wordcount); break;
case 2: pfield_doline_n2 (data, wordcount); break;
case 3: pfield_doline_n3 (data, wordcount); break;
case 4: pfield_doline_n4 (data, wordcount); break;
case 5: pfield_doline_n5 (data, wordcount); break;
case 6: pfield_doline_n6 (data, wordcount); break;
case 7: pfield_doline_n7 (data, wordcount); break;
case 8: pfield_doline_n8 (data, wordcount); break;
}
#endif /* USE_ARMNEON */
}
void init_row_map (void)
{
static uae_u8 *oldbufmem;
static int oldheight, oldpitch;
int i;
if (!row_map) {
row_map = xmalloc(uae_u8*, max_uae_height + 1);
}
if (oldbufmem && oldbufmem == gfxvidinfo.drawbuffer.bufmem &&
oldheight == gfxvidinfo.drawbuffer.outheight &&
oldpitch == gfxvidinfo.drawbuffer.rowbytes)
return;
int j = oldheight == 0 ? max_uae_height : oldheight;
for (i = gfxvidinfo.drawbuffer.outheight; i < max_uae_height + 1 && i < j + 1; i++) {
row_map[i] = row_tmp;
}
for (i = 0, j = 0; i < gfxvidinfo.drawbuffer.outheight; i++, j += gfxvidinfo.drawbuffer.rowbytes) {
row_map[i] = gfxvidinfo.drawbuffer.bufmem + j;
}
oldbufmem = gfxvidinfo.drawbuffer.bufmem;
oldheight = gfxvidinfo.drawbuffer.outheight;
oldpitch = gfxvidinfo.drawbuffer.rowbytes;
}
static void init_aspect_maps(void)
{
int i, h;
linedbl = currprefs.gfx_vresolution;
int maxl = (MAXVPOS + 1) << linedbl;
min_ypos_for_screen = minfirstline << linedbl;
max_drawn_amiga_line = -1;
h = gfxvidinfo.drawbuffer.outheight;
if (h == 0)
/* Do nothing if the gfx driver hasn't initialized the screen yet */
return;
if (native2amiga_line_map)
xfree(native2amiga_line_map);
if (amiga2aspect_line_map)
xfree(amiga2aspect_line_map);
/* At least for this array the +1 is necessary. */
amiga2aspect_line_map = xmalloc(int, (MAXVPOS + 1) * 2 + 1);
native2amiga_line_map = xmalloc(int, h);
for (i = 0; i < maxl; i++) {
int v = i - min_ypos_for_screen;
if (v >= h && max_drawn_amiga_line < 0)
max_drawn_amiga_line = v;
if (i < min_ypos_for_screen || v >= h)
v = -1;
amiga2aspect_line_map[i] = v;
}
if (max_drawn_amiga_line < 0)
max_drawn_amiga_line = maxl - min_ypos_for_screen;
for (i = 0; i < h; i++)
native2amiga_line_map[i] = -1;
for (i = maxl - 1; i >= min_ypos_for_screen; i--) {
if (amiga2aspect_line_map[i] == -1)
continue;
for (int j = amiga2aspect_line_map[i]; j < h && native2amiga_line_map[j] == -1; j++)
native2amiga_line_map[j] = (i + VERTICAL_OFFSET) >> linedbl;
}
}
/*
* One drawing frame has been finished. Tell the graphics code about it.
*/
STATIC_INLINE void do_flush_screen ()
{
unlockscr ();
screenlocked = false;
}
/* We only save hardware registers during the hardware frame. Now, when
* drawing the frame, we expand the data into a slightly more useful
* form. */
static void pfield_expand_dp_bplcon (void)
{
bool pfield_mode_changed = false;
bplres = dp_for_drawing->bplres;
bplplanecnt = dp_for_drawing->nr_planes;
bplham = dp_for_drawing->ham_seen;
if (aga_mode) {
bplehb = (dp_for_drawing->bplcon0 & 0x7010) == 0x6000;
bpldualpf2of = (dp_for_drawing->bplcon3 >> 10) & 7;
sbasecol[0] = ((dp_for_drawing->bplcon4 >> 4) & 15) << 4;
sbasecol[1] = ((dp_for_drawing->bplcon4 >> 0) & 15) << 4;
bplxor = dp_for_drawing->bplcon4 >> 8;
} else
bplehb = (dp_for_drawing->bplcon0 & 0xFC00) == 0x6000 || (dp_for_drawing->bplcon0 & 0xFC00) == 0x7000;
if ((currprefs.chipset_mask & CSMASK_ECS_DENISE) && (dp_for_drawing->bplcon2 & 0x0200))
bplehb = 0;
int oecsshres = ecsshres;
ecsshres = bplres == RES_SUPERHIRES && (currprefs.chipset_mask & CSMASK_ECS_DENISE) && !(currprefs.chipset_mask & CSMASK_AGA);
pfield_mode_changed = oecsshres != ecsshres;
plf1pri = dp_for_drawing->bplcon2 & 7;
plf2pri = (dp_for_drawing->bplcon2 >> 3) & 7;
plf_sprite_mask = 0xFFFF0000 << (4 * plf2pri);
plf_sprite_mask |= (0x0000FFFF << (4 * plf1pri)) & 0xFFFF;
plf_sprite_mask_n16 = ~(plf_sprite_mask >> 16);
bpldualpf = (dp_for_drawing->bplcon0 & 0x400) == 0x400;
bpldualpfpri = (dp_for_drawing->bplcon2 & 0x40) == 0x40;
// BYPASS: HAM and EHB select bits are ignored
if (bplbypass != (dp_for_drawing->bplcon0 & 0x20) != 0) {
bpland = 0xff;
bplbypass = (dp_for_drawing->bplcon0 & 0x20) != 0;
pfield_mode_changed = true;
}
if (bplbypass) {
if (bplham && bplplanecnt == 6)
bpland = 0x0f;
if (bplham && bplplanecnt == 8)
bpland = 0xfc;
bplham = 0;
if (bplehb)
bpland = 31;
bplehb = 0;
}
if (pfield_mode_changed)
pfield_set_linetoscr();
}
static bool isham (uae_u16 bplcon0)
{
int p = GET_PLANES (bplcon0);
if (!(bplcon0 & 0x800))
return 0;
if (aga_mode) {
// AGA only has 6 or 8 plane HAM
if (p == 6 || p == 8)
return 1;
} else {
// OCS/ECS also supports 5 plane HAM
if (GET_RES_DENISE (bplcon0) > 0)
return 0;
if (p >= 5)
return 1;
}
return 0;
}
static void pfield_expand_dp_bplconx (int regno, int v)
{
if (regno == 0xffff) {
hposblank = 1;
return;
}
regno -= 0x1000;
switch (regno)
{
case 0x100: // BPLCON0
dp_for_drawing->bplcon0 = v;
dp_for_drawing->bplres = GET_RES_DENISE(v);
dp_for_drawing->nr_planes = GET_PLANES(v);
dp_for_drawing->ham_seen = isham (v);
break;
case 0x104: // BPLCON2
dp_for_drawing->bplcon2 = v;
break;
case 0x106: // BPLCON3
dp_for_drawing->bplcon3 = v;
break;
case 0x10c: // BPLCON4
dp_for_drawing->bplcon4 = v;
break;
}
pfield_expand_dp_bplcon();
set_res_shift(lores_shift - bplres);
}
static int drawing_color_matches;
static enum { color_match_acolors, color_match_full } color_match_type;
/* Set up colors_for_drawing to the state at the beginning of the currently drawn
line. Try to avoid copying color tables around whenever possible. */
static void adjust_drawing_colors (int ctable, int need_full)
{
if (drawing_color_matches != ctable || need_full < 0) {
if (need_full) {
color_reg_cpy (&colors_for_drawing, curr_color_tables + ctable);
color_match_type = color_match_full;
} else {
memcpy (colors_for_drawing.acolors, curr_color_tables[ctable].acolors,
sizeof colors_for_drawing.acolors);
colors_for_drawing.extra = curr_color_tables[ctable].extra;
color_match_type = color_match_acolors;
}
drawing_color_matches = ctable;
} else if (need_full && color_match_type != color_match_full) {
color_reg_cpy (&colors_for_drawing, &curr_color_tables[ctable]);
color_match_type = color_match_full;
}
}
static void playfield_hard_way(line_draw_func worker_pfield, int first, int last)
{
if (first < real_playfield_start) {
int next = last < real_playfield_start ? last : real_playfield_start;
int diff = next - first;
pfield_do_linetoscr_bordersprite_aga(first, next, false);
if (res_shift >= 0)
diff >>= res_shift;
else
diff <<= res_shift;
src_pixel += diff;
first = next;
}
(*worker_pfield)(first, last < real_playfield_end ? last : real_playfield_end, false);
if (last > real_playfield_end)
pfield_do_linetoscr_bordersprite_aga(real_playfield_end, last, false);
}
STATIC_INLINE void do_color_changes (line_draw_func worker_border, line_draw_func worker_pfield)
{
int i;
int lastpos = visible_left_border;
int endpos = visible_right_border;
for (i = dip_for_drawing->first_color_change; i <= dip_for_drawing->last_color_change; i++) {
int regno = curr_color_changes[i].regno;
unsigned int value = curr_color_changes[i].value;
int nextpos, nextpos_in_range;
if (i == dip_for_drawing->last_color_change)
nextpos = endpos;
else
nextpos = shres_coord_hw_to_window_x (curr_color_changes[i].linepos);
nextpos_in_range = nextpos;
if (nextpos > endpos)
nextpos_in_range = endpos;
// left border (hblank end to playfield start)
if (nextpos_in_range > lastpos && lastpos < playfield_start) {
int t = nextpos_in_range <= playfield_start ? nextpos_in_range : playfield_start;
(*worker_border) (lastpos, t, false);
lastpos = t;
}
// playfield
if (nextpos_in_range > lastpos && lastpos >= playfield_start && lastpos < playfield_end) {
int t = nextpos_in_range <= playfield_end ? nextpos_in_range : playfield_end;
if (plf2pri > 5 && !(currprefs.chipset_mask & CSMASK_AGA))
weird_bitplane_fix (lastpos, t);
if (bplxor && may_require_hard_way && worker_pfield != pfield_do_linetoscr_bordersprite_aga)
playfield_hard_way(worker_pfield, lastpos, t);
else
(*worker_pfield) (lastpos, t, false);
lastpos = t;
}
// right border (playfield end to hblank start)
if (nextpos_in_range > lastpos && lastpos >= playfield_end) {
(*worker_border) (lastpos, nextpos_in_range, false);
lastpos = nextpos_in_range;
}
if (regno >= 0x1000) {
pfield_expand_dp_bplconx (regno, value);
} else if (regno >= 0 && !(value & COLOR_CHANGE_MASK)) {
color_reg_set (&colors_for_drawing, regno, value);
colors_for_drawing.acolors[regno] = getxcolor (value);
} else if (regno == 0 && (value & COLOR_CHANGE_MASK)) {
if (value & COLOR_CHANGE_BRDBLANK) {
colors_for_drawing.extra &= ~(1 << CE_BORDERBLANK);
colors_for_drawing.extra &= ~(1 << CE_BORDERSPRITE);
colors_for_drawing.extra |= (value & 1) != 0 ? (1 << CE_BORDERBLANK) : 0;
colors_for_drawing.extra |= (value & 3) == 2 ? (1 << CE_BORDERSPRITE) : 0;
}
}
if (lastpos >= endpos)
break;
}
}
STATIC_INLINE bool is_color_changes(struct draw_info *di)
{
int regno = curr_color_changes[di->first_color_change].regno;
int changes = di->nr_color_changes;
return changes > 1 || (changes == 1 && regno != 0xffff && regno != -1);
}
static void pfield_draw_line(int lineno, int gfx_ypos, int follow_ypos)
{
int border = 0;
int do_double = 0;
bool have_color_changes;
dp_for_drawing = line_decisions + lineno;
dip_for_drawing = curr_drawinfo + lineno;
if (currprefs.gfx_vresolution && !interlace_seen) {
if (nextline_as_previous) {
nextline_as_previous = false;
return;
}
nextline_as_previous = true;
if (follow_ypos >= 0)
do_double = 1;
}
if (dp_for_drawing->plfleft < 0)
border = 1;
have_color_changes = is_color_changes(dip_for_drawing);
xlinebuffer = row_map[gfx_ypos];
xlinebuffer -= linetoscr_x_adjust_pixbytes;
if (border == 0) {
pfield_expand_dp_bplcon();
pfield_init_linetoscr(false);
pfield_doline(lineno);
adjust_drawing_colors(dp_for_drawing->ctable, dp_for_drawing->ham_seen || bplehb || ecsshres);
/* The problem is that we must call decode_ham() BEFORE we do the sprites. */
if (dp_for_drawing->ham_seen) {
int ohposblank = hposblank;
uae_u8 b0 = dp_for_drawing->bplcon0;
uae_u8 b2 = dp_for_drawing->bplcon2;
uae_u8 b3 = dp_for_drawing->bplcon3;
uae_u8 b4 = dp_for_drawing->bplcon4;
init_ham_decoding();
do_color_changes(dummy_worker, decode_ham);
if (have_color_changes) {
// do_color_changes() did color changes and register changes, restore them.
adjust_drawing_colors(dp_for_drawing->ctable, -1);
dp_for_drawing->bplcon0 = b0;
dp_for_drawing->bplcon2 = b2;
dp_for_drawing->bplcon3 = b3;
dp_for_drawing->bplcon4 = b4;
pfield_expand_dp_bplcon();
}
hposblank = ohposblank;
ham_decode_pixel = src_pixel;
bplham = dp_for_drawing->ham_at_start;
}
if (dip_for_drawing->nr_sprites) {
int i;
if (ce_is_bordersprite(colors_for_drawing.extra) && dp_for_drawing->bordersprite_seen && !ce_is_borderblank(colors_for_drawing.extra))
clear_bitplane_border_aga();
for (i = 0; i < dip_for_drawing->nr_sprites; i++) {
if (currprefs.chipset_mask & CSMASK_AGA)
draw_sprites_aga(curr_sprite_entries + dip_for_drawing->first_sprite_entry + i, 1);
else
draw_sprites_ecs(curr_sprite_entries + dip_for_drawing->first_sprite_entry + i);
}
}
if (dip_for_drawing->nr_sprites && ce_is_bordersprite(colors_for_drawing.extra) && !ce_is_borderblank(colors_for_drawing.extra) && dp_for_drawing->bordersprite_seen)
do_color_changes(pfield_do_linetoscr_bordersprite_aga, pfield_do_linetoscr_spr);
else
do_color_changes(pfield_do_fill_line, dip_for_drawing->nr_sprites ? pfield_do_linetoscr_spr : pfield_do_linetoscr);
if (do_double) {
memcpy(row_map[follow_ypos], row_map[gfx_ypos], gfxvidinfo.drawbuffer.pixbytes * gfxvidinfo.drawbuffer.outwidth);
}
if (dip_for_drawing->nr_sprites)
pfield_erase_hborder_sprites();
}
else { // border > 0: top or bottom border
bool dosprites = false;
adjust_drawing_colors(dp_for_drawing->ctable, 0);
if (dp_for_drawing->bordersprite_seen && !ce_is_borderblank(colors_for_drawing.extra) && dip_for_drawing->nr_sprites) {
dosprites = true;
pfield_expand_dp_bplcon();
pfield_init_linetoscr(true);
pfield_erase_vborder_sprites();
}
if (!dosprites && !have_color_changes) {
if (dp_for_drawing->plfleft < -1) {
// blanked border line
int tmp = hposblank;
hposblank = 1;
fill_line_border(lineno);
hposblank = tmp;
}
else {
// normal border line
fill_line_border(lineno);
}
if (do_double) {
xlinebuffer = row_map[follow_ypos] - linetoscr_x_adjust_pixbytes;
fill_line_border(lineno);
}
return;
}
if (dosprites) {
for (int i = 0; i < dip_for_drawing->nr_sprites; i++)
draw_sprites_aga(curr_sprite_entries + dip_for_drawing->first_sprite_entry + i, 1);
do_color_changes(pfield_do_linetoscr_bordersprite_aga, pfield_do_linetoscr_bordersprite_aga);
}
else {
playfield_start = visible_right_border;
playfield_end = visible_right_border;
do_color_changes(pfield_do_fill_line, pfield_do_fill_line);
}
if (do_double) {
memcpy(row_map[follow_ypos], row_map[gfx_ypos], gfxvidinfo.drawbuffer.pixbytes * gfxvidinfo.drawbuffer.outwidth);
}
}
}
static void center_image(void)
{
const int deltaToBorder = (gfxvidinfo.drawbuffer.outwidth >> currprefs.gfx_resolution) - 320;
visible_left_border = 73 - (deltaToBorder >> 1);
visible_right_border = 393 + (deltaToBorder >> 1);
visible_left_border <<= lores_shift;
visible_right_border <<= lores_shift;
linetoscr_x_adjust_pixels = visible_left_border;
linetoscr_x_adjust_pixbytes = linetoscr_x_adjust_pixels * gfxvidinfo.drawbuffer.pixbytes;
int max_drawn_amiga_line_tmp = max_drawn_amiga_line;
if (max_drawn_amiga_line_tmp > gfxvidinfo.drawbuffer.outheight)
max_drawn_amiga_line_tmp = gfxvidinfo.drawbuffer.outheight;
max_drawn_amiga_line_tmp >>= linedbl;
#ifdef PANDORA
thisframe_y_adjust = minfirstline + currprefs.pandora_vertical_offset;
#else
thisframe_y_adjust = minfirstline + VERTICAL_OFFSET;
#endif
/* Make sure the value makes sense */
if (thisframe_y_adjust + max_drawn_amiga_line_tmp > maxvpos + maxvpos / 2)
thisframe_y_adjust = maxvpos + maxvpos / 2 - max_drawn_amiga_line_tmp;
if (thisframe_y_adjust < 0)
thisframe_y_adjust = 0;
thisframe_y_adjust_real = thisframe_y_adjust << linedbl;
max_ypos_thisframe = (maxvpos_display - minfirstline + 1) << linedbl;
}
static void init_drawing_frame (void)
{
lores_reset();
init_hardware_for_drawing_frame();
linestate_first_undecided = 0;
nextline_as_previous = false;
center_image();
drawing_color_matches = -1;
}
static void draw_status_line(int line, int statusy)
{
xlinebuffer = row_map[line];
uae_u8 *buf = xlinebuffer;
draw_status_line_single(buf, statusy, gfxvidinfo.drawbuffer.outwidth);
}
static void partial_draw_frame(void)
{
if (framecnt == 0) {
if (!screenlocked) {
if (!lockscr())
return;
screenlocked = true;
}
struct vidbuffer *vb = &gfxvidinfo.drawbuffer;
for (; next_line_to_render < max_ypos_thisframe; ++next_line_to_render) {
const int i1 = next_line_to_render + min_ypos_for_screen;
const int line = next_line_to_render + thisframe_y_adjust_real;
const int whereline = amiga2aspect_line_map[i1];
const int wherenext = amiga2aspect_line_map[i1 + 1];
if (whereline >= vb->outheight || line >= linestate_first_undecided)
break;
if (whereline < 0)
continue;
hposblank = 0;
pfield_draw_line(line, whereline, wherenext);
}
}
}
void halt_draw_frame(void)
{
if(screenlocked) {
unlockscr();
screenlocked = false;
}
}
static void finish_drawing_frame (void)
{
int i;
struct vidbuffer *vb = &gfxvidinfo.drawbuffer;
if (!screenlocked) {
if (!lockscr())
return;
screenlocked = true;
}
for (i = next_line_to_render; i < max_ypos_thisframe; i++) {
const int i1 = i + min_ypos_for_screen;
const int line = i + thisframe_y_adjust_real;
const int whereline = amiga2aspect_line_map[i1];
const int wherenext = amiga2aspect_line_map[i1 + 1];
if (whereline >= vb->outheight || line >= linestate_first_undecided)
break;
if (whereline < 0)
continue;
hposblank = 0;
pfield_draw_line(line, whereline, wherenext);
}
if (currprefs.leds_on_screen) {
for (i = 0; i < TD_TOTAL_HEIGHT; i++) {
int line = gfxvidinfo.drawbuffer.outheight - TD_TOTAL_HEIGHT + i;
draw_status_line(line, i);
}
}
if (currprefs.cs_cd32fmv) {
if (cd32_fmv_active) {
cd32_fmv_genlock(vb, &gfxvidinfo.drawbuffer);
}
}
do_flush_screen();
next_line_to_render = 0;
}
void check_prefs_picasso(void)
{
#ifdef PICASSO96
if (picasso_on)
picasso_refresh();
if (picasso_requested_on == picasso_on && !picasso_requested_forced_on)
return;
picasso_requested_forced_on = false;
picasso_on = picasso_requested_on;
if (!picasso_on)
clear_inhibit_frame(IHF_PICASSO);
else
set_inhibit_frame(IHF_PICASSO);
gfx_set_picasso_state(picasso_on);
picasso_enablescreen(picasso_requested_on);
notice_new_xcolors();
count_frame();
#endif
}
bool vsync_handle_check(void)
{
const int changed = check_prefs_changed_gfx();
if (changed) {
reset_drawing();
notice_new_xcolors();
}
device_check_config();
return changed != 0;
}
void vsync_handle_redraw(void)
{
if (framecnt == 0) {
#ifdef USE_SDL1
if (render_tid) {
while (render_thread_busy)
sleep_millis(1);
write_comm_pipe_u32(render_pipe, RENDER_SIGNAL_FRAME_DONE, 1);
uae_sem_wait(&render_sem);
}
#elif USE_SDL2
finish_drawing_frame();
#endif
}
if (quit_program < 0) {
#ifdef USE_SDL1
if (render_tid) {
while (render_thread_busy)
sleep_millis(1);
write_comm_pipe_u32(render_pipe, RENDER_SIGNAL_QUIT, 1);
while (render_tid != 0) {
sleep_millis(10);
}
destroy_comm_pipe(render_pipe);
xfree(render_pipe);
render_pipe = 0;
uae_sem_destroy(&render_sem);
render_sem = 0;
}
#endif
quit_program = -quit_program;
set_inhibit_frame(IHF_QUIT_PROGRAM);
set_special(SPCFLAG_BRK | SPCFLAG_MODE_CHANGE);
return;
}
count_frame();
if (framecnt == 0)
init_drawing_frame();
gui_flicker_led(-1, 0, 0);
}
void hsync_record_line_state(int lineno)
{
if (framecnt != 0)
return;
linestate_first_undecided = lineno + 1;
#ifdef USE_SDL1
if (render_tid && linestate_first_undecided > 3 && !render_thread_busy) {
if (currprefs.gfx_vresolution) {
if (!(linestate_first_undecided & 0x3e))
write_comm_pipe_u32(render_pipe, RENDER_SIGNAL_PARTIAL, 1);
}
else if (!(linestate_first_undecided & 0x1f))
write_comm_pipe_u32(render_pipe, RENDER_SIGNAL_PARTIAL, 1);
}
#endif
}
bool notice_interlace_seen(bool lace)
{
bool changed = false;
// non-lace to lace switch (non-lace active at least one frame)?
if (lace) {
if (interlace_seen == 0) {
changed = true;
}
interlace_seen = currprefs.gfx_vresolution ? 1 : -1;
}
else {
if (interlace_seen) {
changed = true;
}
interlace_seen = 0;
}
return changed;
}
void reset_drawing(void)
{
lores_reset();
linestate_first_undecided = 0;
nextline_as_previous = false;
init_aspect_maps();
init_row_map();
memset(spixels, 0, sizeof spixels);
memset(&spixstate, 0, sizeof spixstate);
init_drawing_frame();
pfield_set_linetoscr();
}
static void gen_direct_drawing_table(void)
{
// BYPASS color table
for (int i = 0; i < 256; i++) {
uae_u32 v = (i << 16) | (i << 8) | i;
direct_colors_for_drawing.acolors[i] = CONVERT_RGB(v);
}
}
#ifdef USE_SDL1
static void *render_thread(void *unused)
{
for (;;) {
render_thread_busy = false;
uae_u32 signal = read_comm_pipe_u32_blocking(render_pipe);
render_thread_busy = true;
switch (signal) {
case RENDER_SIGNAL_PARTIAL:
partial_draw_frame();
break;
case RENDER_SIGNAL_FRAME_DONE:
finish_drawing_frame();
uae_sem_post(&render_sem);
break;
case RENDER_SIGNAL_QUIT:
render_tid = nullptr;
return nullptr;
}
}
}
#endif
void drawing_init(void)
{
gen_pfield_tables();
gen_direct_drawing_table();
#ifdef USE_SDL1
if (render_pipe == 0) {
render_pipe = xmalloc(smp_comm_pipe, 1);
init_comm_pipe(render_pipe, 20, 1);
}
if (render_sem == 0) {
uae_sem_init(&render_sem, 0, 0);
}
if (render_tid == 0 && render_pipe != 0 && render_sem != 0) {
uae_start_thread(_T("render"), render_thread, NULL, &render_tid);
}
#endif
#ifdef PICASSO96
if (!isrestore()) {
picasso_on = 0;
picasso_requested_on = 0;
gfx_set_picasso_state(0);
}
#endif
xlinebuffer = gfxvidinfo.drawbuffer.bufmem;
inhibit_frame = 0;
reset_drawing();
}