S2636 drawing optimisation (nw)

This commit is contained in:
Vas Crabb 2015-12-11 23:03:17 +11:00
parent 5702bef80d
commit e883c98c93

View File

@ -4,49 +4,46 @@
Signetics 2636 Programmable Video Interface Signetics 2636 Programmable Video Interface
This emulation is pretty low-level. For the most part it models This emulation is pretty low-level. For the most part it models
the kinds of counters and flags you'd use if you were actually the kinds of counters and flags you'd use if you were actually
implementing it in programmable logic or on silicon. It even implementing it in programmable logic or on silicon. I'm sure
renders pixels sequentially without multiple passes or needing to there are potential performance improvements in the drawing and
backtrack. This works pretty well, but it probably isn't the most collision detection code.
efficient way to do things. I'm sure there are potential
performance improvements in the drawing and collision detection
code.
At present the entire internal space of 256 bytes can be read and At present the entire internal space of 256 bytes can be read and
written. This isn't accurate as some registers are read- or write- written. This isn't accurate as some registers are read- or write-
only, and there are several unused blocks in the address range. We only, and there are several unused blocks in the address range. We
should be returning some fixed value on attempt to read unreadable should be returning some fixed value on attempt to read unreadable
locations. locations.
This implementation really needs proper display timing information This implementation really needs proper display timing information
information to work properly. Audio pitch will be wrong if the information to work properly. Audio pitch will be wrong if the
screen's scan time is inaccurate. Positioning objects depends on screen's scan time is inaccurate. Positioning objects depends on
the screen's visible area representing the pixel clock periods the screen's visible area representing the pixel clock periods
periods between blanking pulses. You also need to call the line periods between blanking pulses. You also need to call the line
rendering function at appropriate times if something expects to see rendering function at appropriate times if something expects to see
collision or completion flags. collision or completion flags.
The crude "offset" system for adjusting the position of the image The crude "offset" system for adjusting the position of the image
has been maintained, but it's clearly inadequate for what games are has been maintained, but it's clearly inadequate for what games are
doing with it, and it will inevitably lead to bugs. If an object doing with it, and it will inevitably lead to bugs. If an object
is positioned outside the visible area using the offset mechanism, is positioned outside the visible area using the offset mechanism,
it won't be displayed and neither will its duplicates. it won't be displayed and neither will its duplicates.
If what's happening in the games right now is correct, the hardware If what's happening in the games right now is correct, the hardware
must be feeding something other than the actual vertical and must be feeding something other than the actual vertical and
horizontal blanking signals to the S2623(s) so they start drawing horizontal blanking signals to the S2623(s) so they start drawing
inside the blanking region and have advanced to the desired inside the blanking region and have advanced to the desired
location on reaching the visible area. Implementing this properly location on reaching the visible area. Implementing this properly
would require decoupling the S2623 from the screen and giving it would require decoupling the S2623 from the screen and giving it
some other means to determine when it believes the blanking periods some other means to determine when it believes the blanking periods
begin and end. begin and end.
Sorry, analog input isn't currently supported, and neither is Sorry, analog input isn't currently supported, and neither is
interrupt request/acknowledge. I have't got to them yet, and I'm interrupt request/acknowledge. I have't got to them yet, and I'm
still not sure whether reading a status register clears a pending still not sure whether reading a status register clears a pending
interrupt. The address decoding features still aren't implemented interrupt. The address decoding features still aren't implemented
but they'd be pretty hard to fit in the MAME framework. but they'd be pretty hard to fit in the MAME framework.
ADDRESS MAP ADDRESS MAP
@ -227,8 +224,10 @@ void s2636_device::render_first_line()
void s2636_device::render_next_line() void s2636_device::render_next_line()
{ {
// pre-clear the line for convenience
rectangle const &vis_area = m_screen->visible_area(); rectangle const &vis_area = m_screen->visible_area();
UINT16 *const row = &m_bitmap.pix16(m_screen_line); UINT16 *const row = &m_bitmap.pix16(m_screen_line);
m_bitmap.plot_box(0, m_screen_line, m_bitmap.width(), 1, 0);
if ((vis_area.min_y > m_screen_line) || (vis_area.max_y < m_screen_line)) if ((vis_area.min_y > m_screen_line) || (vis_area.max_y < m_screen_line))
{ {
@ -237,10 +236,6 @@ void s2636_device::render_next_line()
m_registers[REG_VBL_COL_OBJ] |= 0x40; m_registers[REG_VBL_COL_OBJ] |= 0x40;
m_vrst = true; m_vrst = true;
} }
for (int screen_col = 0; screen_col < m_bitmap.width(); screen_col++)
{
row[screen_col] = 0;
}
} }
else else
{ {
@ -265,16 +260,8 @@ void s2636_device::render_next_line()
} }
// work out what object pixels belong in this line // work out what object pixels belong in this line
UINT16 obj_clr[OBJ_COUNT];
int obj_h_cnt[OBJ_COUNT];
int obj_inc[OBJ_COUNT];
UINT8 obj_bits[OBJ_COUNT];
for (int i = 0; i < OBJ_COUNT; i++) for (int i = 0; i < OBJ_COUNT; i++)
{ {
obj_clr[i] = object_color(i) | 0x08;
obj_h_cnt[i] = m_registers[OFFS_OBJ[i] + (m_obj_dup[i] ? OFFS_HCB : OFFS_HC)] + m_x_offset;
obj_inc[i] = 1 << (3 - object_scale(i));
// repurpose counter and set flag when we've skipped enough lines // repurpose counter and set flag when we've skipped enough lines
if (!m_obj_cnt[i]) if (!m_obj_cnt[i])
{ {
@ -284,9 +271,22 @@ void s2636_device::render_next_line()
if (m_obj_disp[i]) if (m_obj_disp[i])
{ {
int const obj_inc = 1 << (3 - object_scale(i));
m_obj_cnt[i] -= obj_inc;
// fetch appropriate line from object // fetch appropriate line from object
m_obj_cnt[i] -= obj_inc[i]; UINT8 const obj_bits = m_registers[OFFS_OBJ[i] + OBJ_HEIGHT - 1 - (m_obj_cnt[i] >> 3)];
obj_bits[i] = m_registers[OFFS_OBJ[i] + OBJ_HEIGHT - 1 - (m_obj_cnt[i] >> 3)]; UINT16 const obj_clr = object_color(i) | 0x08 | (0x10 << i);
// blit it to the line ignoring intermediate pixels
int const obj_h_cnt = m_registers[OFFS_OBJ[i] + (m_obj_dup[i] ? OFFS_HCB : OFFS_HC)] + m_x_offset;
for (int x = 0, screen_col = vis_area.min_x + (obj_h_cnt * m_divider); (OBJ_WIDTH << 3) > x && (vis_area.max_x >= screen_col); )
{
bool const bit = bool((obj_bits << (x >> 3)) & 0x80);
if (bit && (vis_area.min_x <= screen_col)) row[screen_col] |= obj_clr;
x += obj_inc;
screen_col += m_divider;
}
// if that's the last line of the object, flag completion and prepare for duplicates // if that's the last line of the object, flag completion and prepare for duplicates
if (!m_obj_cnt[i]) if (!m_obj_cnt[i])
@ -301,7 +301,25 @@ void s2636_device::render_next_line()
{ {
// count down lines to display object // count down lines to display object
m_obj_cnt[i]--; m_obj_cnt[i]--;
obj_bits[i] = 0x00; }
}
// let's take a look at the score display
UINT16 const bg_clr = m_registers[REG_BG_ENB_CLR] & 0x07;
int const score_row = m_vis_line - m_y_offset - SCORE_START_Y[m_registers[REG_SCORE_FMT] & 0x01];
if ((0 <= score_row) && (SCORE_HEIGHT > score_row))
{
int const (&score_start_x)[SCORE_DIGITS] = SCORE_START_X[(m_registers[REG_SCORE_FMT] >> 1) & 0x01];
for (int i = 0; i < SCORE_DIGITS; i++)
{
UINT16 score_bits = SCORE_FONT[score_digit(i)][score_row >> 2];
int screen_col = vis_area.min_x + ((score_start_x[i] + m_x_offset) * m_divider);
while (score_bits && (vis_area.max_x >= screen_col))
{
if (score_bits & 0x0001) row[screen_col] |= bg_clr | 0x08;
score_bits >>= 1;
screen_col += m_divider;
}
} }
} }
@ -315,69 +333,17 @@ void s2636_device::render_next_line()
UINT8 const bg_hbar_bits = m_registers[bg_hbar_offs]; UINT8 const bg_hbar_bits = m_registers[bg_hbar_offs];
bool const bg_hbar_stretch = bool(bg_hbar_bits & (1 << ((((bg_row % 40) >= 20) ? 3 : 0) + (((bg_row % 20) >= 11) ? 2 : ((bg_row % 20) >= 2) ? 1 : 0)))); bool const bg_hbar_stretch = bool(bg_hbar_bits & (1 << ((((bg_row % 40) >= 20) ? 3 : 0) + (((bg_row % 20) >= 11) ? 2 : ((bg_row % 20) >= 2) ? 1 : 0))));
int const bg_hbar_width = bg_hbar_stretch ? 8 : (0xc0 == (bg_hbar_bits & 0xc0)) ? 4 : (0x40 == (bg_hbar_bits & 0xc0)) ? 2 : 1; int const bg_hbar_width = bg_hbar_stretch ? 8 : (0xc0 == (bg_hbar_bits & 0xc0)) ? 4 : (0x40 == (bg_hbar_bits & 0xc0)) ? 2 : 1;
UINT16 const bg_clr = m_registers[REG_BG_ENB_CLR] & 0x07;
UINT16 const scrn_clr = bg_enable ? ((m_registers[REG_BG_ENB_CLR] >> 4) & 0x07) : 0x00; UINT16 const scrn_clr = bg_enable ? ((m_registers[REG_BG_ENB_CLR] >> 4) & 0x07) : 0x00;
// let's take a look at the score display
int const (&score_start_x)[SCORE_DIGITS] = SCORE_START_X[(m_registers[REG_SCORE_FMT] >> 1) & 0x01];
int const score_row = m_vis_line - m_y_offset - SCORE_START_Y[m_registers[REG_SCORE_FMT] & 0x01];
bool const score_draw = (0 <= score_row) && (SCORE_HEIGHT > score_row);
UINT16 score_bits[SCORE_DIGITS];
for (int i = 0; i < SCORE_DIGITS; i++)
score_bits[i] = score_draw ? SCORE_FONT[score_digit(i)][score_row >> 2] : 0x0000;
// clear leading horizontal blanking area
m_bitmap.plot_box(0, m_screen_line, m_bitmap.width(), 1, 0);
bool obj_vis[4] = { false, false, false, false };
for (int screen_col = vis_area.min_x, x = 0; vis_area.max_x >= screen_col; x++) for (int screen_col = vis_area.min_x, x = 0; vis_area.max_x >= screen_col; x++)
{ {
// render objects
bool obj[4];
for (int i = 0; i < OBJ_COUNT; i++)
{
if (!obj_h_cnt[i])
{
obj_h_cnt[i] = OBJ_WIDTH << 3;
obj_vis[i] = true;
}
if (obj_vis[i])
{
obj_h_cnt[i] -= obj_inc[i];
obj[i] = bool(obj_bits[i] & (1U << (obj_h_cnt[i] >> 3)));
if (obj[i]) row[screen_col] |= obj_clr[i];
if (!obj_h_cnt[i])
{
obj_h_cnt[i] = -1;
obj_vis[i] = 0;
}
}
else
{
obj_h_cnt[i]--;
obj[i] = false;
}
}
// check object-object collisions // check object-object collisions
if (obj[0] && obj[1]) m_registers[REG_VBL_COL_OBJ] |= 0x20; if ((row[screen_col] & 0x10) && (row[screen_col] & 0x20)) m_registers[REG_VBL_COL_OBJ] |= 0x20;
if (obj[0] && obj[2]) m_registers[REG_VBL_COL_OBJ] |= 0x10; if ((row[screen_col] & 0x10) && (row[screen_col] & 0x40)) m_registers[REG_VBL_COL_OBJ] |= 0x10;
if (obj[0] && obj[3]) m_registers[REG_VBL_COL_OBJ] |= 0x08; if ((row[screen_col] & 0x10) && (row[screen_col] & 0x80)) m_registers[REG_VBL_COL_OBJ] |= 0x08;
if (obj[1] && obj[2]) m_registers[REG_VBL_COL_OBJ] |= 0x04; if ((row[screen_col] & 0x20) && (row[screen_col] & 0x40)) m_registers[REG_VBL_COL_OBJ] |= 0x04;
if (obj[1] && obj[3]) m_registers[REG_VBL_COL_OBJ] |= 0x02; if ((row[screen_col] & 0x20) && (row[screen_col] & 0x80)) m_registers[REG_VBL_COL_OBJ] |= 0x02;
if (obj[2] && obj[3]) m_registers[REG_VBL_COL_OBJ] |= 0x01; if ((row[screen_col] & 0x40) && (row[screen_col] & 0x80)) m_registers[REG_VBL_COL_OBJ] |= 0x01;
// render scores
if (score_draw)
{
for (int i = 0; i < SCORE_DIGITS; i++)
{
int const score_col = x - m_x_offset - score_start_x[i];
bool const score = bool(score_bits[i] & (1U << score_col));
if ((0 <= score_col) && (SCORE_WIDTH > score_col) && score)
row[screen_col] |= bg_clr | 0x08;
}
}
// work out if the background hits this pixel // work out if the background hits this pixel
int const bg_col = x - m_x_offset - BG_START_X; int const bg_col = x - m_x_offset - BG_START_X;
@ -385,10 +351,10 @@ void s2636_device::render_next_line()
if (bg_draw && (0 <= bg_col) && (BG_WIDTH > bg_col) && bg && (bg_hbar_width > (bg_col & 0x07))) if (bg_draw && (0 <= bg_col) && (BG_WIDTH > bg_col) && bg && (bg_hbar_width > (bg_col & 0x07)))
{ {
// do object-background collisions // do object-background collisions
if (obj[0]) m_registers[REG_COL_BG_CMPL] |= 0x80; if (row[screen_col] & 0x10) m_registers[REG_COL_BG_CMPL] |= 0x80;
if (obj[1]) m_registers[REG_COL_BG_CMPL] |= 0x40; if (row[screen_col] & 0x20) m_registers[REG_COL_BG_CMPL] |= 0x40;
if (obj[2]) m_registers[REG_COL_BG_CMPL] |= 0x20; if (row[screen_col] & 0x40) m_registers[REG_COL_BG_CMPL] |= 0x20;
if (obj[3]) m_registers[REG_COL_BG_CMPL] |= 0x10; if (row[screen_col] & 0x80) m_registers[REG_COL_BG_CMPL] |= 0x10;
if (!(row[screen_col] & 0x08)) row[screen_col] = bg_clr; if (!(row[screen_col] & 0x08)) row[screen_col] = bg_clr;
} }
else if (!(row[screen_col] & 0x08)) else if (!(row[screen_col] & 0x08))
@ -397,10 +363,8 @@ void s2636_device::render_next_line()
row[screen_col] = scrn_clr; row[screen_col] = scrn_clr;
} }
// advance the screen column // clear collision crud and deal with pixel clock divider ratio
screen_col++; row[screen_col++] &= 0x0f;
// deal with pixel clock divider ratio
for (int i = 1; (i < m_divider) && (vis_area.max_x >= screen_col); i++, screen_col++) for (int i = 1; (i < m_divider) && (vis_area.max_x >= screen_col); i++, screen_col++)
{ {
row[screen_col] = row[screen_col - 1]; row[screen_col] = row[screen_col - 1];