summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--src/devices/machine/s2636.cpp212
1 files changed, 88 insertions, 124 deletions
diff --git a/src/devices/machine/s2636.cpp b/src/devices/machine/s2636.cpp
index b2105b30dce..70a9f165da7 100644
--- a/src/devices/machine/s2636.cpp
+++ b/src/devices/machine/s2636.cpp
@@ -4,49 +4,46 @@
Signetics 2636 Programmable Video Interface
- 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
- implementing it in programmable logic or on silicon. It even
- renders pixels sequentially without multiple passes or needing to
- backtrack. This works pretty well, but it probably isn't the most
- 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
- written. This isn't accurate as some registers are read- or write-
- only, and there are several unused blocks in the address range. We
- should be returning some fixed value on attempt to read unreadable
- locations.
-
- This implementation really needs proper display timing information
- information to work properly. Audio pitch will be wrong if the
- screen's scan time is inaccurate. Positioning objects depends on
- the screen's visible area representing the pixel clock periods
- periods between blanking pulses. You also need to call the line
- rendering function at appropriate times if something expects to see
- collision or completion flags.
-
- The crude "offset" system for adjusting the position of the image
- 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
- is positioned outside the visible area using the offset mechanism,
- it won't be displayed and neither will its duplicates.
-
- If what's happening in the games right now is correct, the hardware
- must be feeding something other than the actual vertical and
- horizontal blanking signals to the S2623(s) so they start drawing
- inside the blanking region and have advanced to the desired
- location on reaching the visible area. Implementing this properly
- would require decoupling the S2623 from the screen and giving it
- some other means to determine when it believes the blanking periods
- begin and end.
-
- Sorry, analog input isn't currently supported, and neither is
- interrupt request/acknowledge. I have't got to them yet, and I'm
- still not sure whether reading a status register clears a pending
- interrupt. The address decoding features still aren't implemented
- but they'd be pretty hard to fit in the MAME framework.
+ 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
+ implementing it in programmable logic or on silicon. 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
+ written. This isn't accurate as some registers are read- or write-
+ only, and there are several unused blocks in the address range. We
+ should be returning some fixed value on attempt to read unreadable
+ locations.
+
+ This implementation really needs proper display timing information
+ information to work properly. Audio pitch will be wrong if the
+ screen's scan time is inaccurate. Positioning objects depends on
+ the screen's visible area representing the pixel clock periods
+ periods between blanking pulses. You also need to call the line
+ rendering function at appropriate times if something expects to see
+ collision or completion flags.
+
+ The crude "offset" system for adjusting the position of the image
+ 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
+ is positioned outside the visible area using the offset mechanism,
+ it won't be displayed and neither will its duplicates.
+
+ If what's happening in the games right now is correct, the hardware
+ must be feeding something other than the actual vertical and
+ horizontal blanking signals to the S2623(s) so they start drawing
+ inside the blanking region and have advanced to the desired
+ location on reaching the visible area. Implementing this properly
+ would require decoupling the S2623 from the screen and giving it
+ some other means to determine when it believes the blanking periods
+ begin and end.
+
+ Sorry, analog input isn't currently supported, and neither is
+ interrupt request/acknowledge. I have't got to them yet, and I'm
+ still not sure whether reading a status register clears a pending
+ interrupt. The address decoding features still aren't implemented
+ but they'd be pretty hard to fit in the MAME framework.
ADDRESS MAP
@@ -227,8 +224,10 @@ void s2636_device::render_first_line()
void s2636_device::render_next_line()
{
+ // pre-clear the line for convenience
rectangle const &vis_area = m_screen->visible_area();
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))
{
@@ -237,10 +236,6 @@ void s2636_device::render_next_line()
m_registers[REG_VBL_COL_OBJ] |= 0x40;
m_vrst = true;
}
- for (int screen_col = 0; screen_col < m_bitmap.width(); screen_col++)
- {
- row[screen_col] = 0;
- }
}
else
{
@@ -265,16 +260,8 @@ void s2636_device::render_next_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++)
{
- 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
if (!m_obj_cnt[i])
{
@@ -284,9 +271,22 @@ void s2636_device::render_next_line()
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
- m_obj_cnt[i] -= obj_inc[i];
- obj_bits[i] = m_registers[OFFS_OBJ[i] + OBJ_HEIGHT - 1 - (m_obj_cnt[i] >> 3)];
+ UINT8 const obj_bits = 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 (!m_obj_cnt[i])
@@ -301,7 +301,25 @@ void s2636_device::render_next_line()
{
// count down lines to display object
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];
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;
- 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;
- // 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++)
{
- // 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
- if (obj[0] && obj[1]) m_registers[REG_VBL_COL_OBJ] |= 0x20;
- if (obj[0] && obj[2]) m_registers[REG_VBL_COL_OBJ] |= 0x10;
- if (obj[0] && obj[3]) m_registers[REG_VBL_COL_OBJ] |= 0x08;
- if (obj[1] && obj[2]) m_registers[REG_VBL_COL_OBJ] |= 0x04;
- if (obj[1] && obj[3]) m_registers[REG_VBL_COL_OBJ] |= 0x02;
- if (obj[2] && obj[3]) 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;
- }
- }
+ if ((row[screen_col] & 0x10) && (row[screen_col] & 0x20)) m_registers[REG_VBL_COL_OBJ] |= 0x20;
+ if ((row[screen_col] & 0x10) && (row[screen_col] & 0x40)) m_registers[REG_VBL_COL_OBJ] |= 0x10;
+ if ((row[screen_col] & 0x10) && (row[screen_col] & 0x80)) m_registers[REG_VBL_COL_OBJ] |= 0x08;
+ if ((row[screen_col] & 0x20) && (row[screen_col] & 0x40)) m_registers[REG_VBL_COL_OBJ] |= 0x04;
+ if ((row[screen_col] & 0x20) && (row[screen_col] & 0x80)) m_registers[REG_VBL_COL_OBJ] |= 0x02;
+ if ((row[screen_col] & 0x40) && (row[screen_col] & 0x80)) m_registers[REG_VBL_COL_OBJ] |= 0x01;
// work out if the background hits this pixel
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)))
{
// do object-background collisions
- if (obj[0]) m_registers[REG_COL_BG_CMPL] |= 0x80;
- if (obj[1]) m_registers[REG_COL_BG_CMPL] |= 0x40;
- if (obj[2]) m_registers[REG_COL_BG_CMPL] |= 0x20;
- if (obj[3]) m_registers[REG_COL_BG_CMPL] |= 0x10;
+ if (row[screen_col] & 0x10) m_registers[REG_COL_BG_CMPL] |= 0x80;
+ if (row[screen_col] & 0x20) m_registers[REG_COL_BG_CMPL] |= 0x40;
+ if (row[screen_col] & 0x40) m_registers[REG_COL_BG_CMPL] |= 0x20;
+ if (row[screen_col] & 0x80) m_registers[REG_COL_BG_CMPL] |= 0x10;
if (!(row[screen_col] & 0x08)) row[screen_col] = bg_clr;
}
else if (!(row[screen_col] & 0x08))
@@ -397,10 +363,8 @@ void s2636_device::render_next_line()
row[screen_col] = scrn_clr;
}
- // advance the screen column
- screen_col++;
-
- // deal with pixel clock divider ratio
+ // clear collision crud and deal with pixel clock divider ratio
+ row[screen_col++] &= 0x0f;
for (int i = 1; (i < m_divider) && (vis_area.max_x >= screen_col); i++, screen_col++)
{
row[screen_col] = row[screen_col - 1];