summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/mos6560.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/mos6560.cpp')
-rw-r--r--src/devices/sound/mos6560.cpp969
1 files changed, 969 insertions, 0 deletions
diff --git a/src/devices/sound/mos6560.cpp b/src/devices/sound/mos6560.cpp
new file mode 100644
index 00000000000..eb4ee0a5c30
--- /dev/null
+++ b/src/devices/sound/mos6560.cpp
@@ -0,0 +1,969 @@
+// license:BSD-3-Clause
+// copyright-holders:Peter Trauner
+/***************************************************************************
+
+ MOS 6560 / 6561 Video Interface Chip
+
+
+ Original code by PeT (mess@utanet.at), 1999
+
+
+ 2010 FP: converted to a device and merged the video & sound components
+
+ TODO:
+ - plenty of cleanups!
+ - investigate attckufo chip features (no invert mode, no multicolor, 16 col chars)
+ - investigate why some vic20 carts crash emulation
+
+****************************************************************************
+
+ Original notes:
+
+ 2 Versions
+ 6560 NTSC
+ 6561 PAL
+ 14 bit addr bus
+ 12 bit data bus
+ (16 8 bit registers)
+ alternates with MOS 6502 on the address bus
+ fetch 8 bit characternumber and 4 bit color
+ high bit of 4 bit color value determines:
+ 0: 2 color mode
+ 1: 4 color mode
+ than fetch characterbitmap for characternumber
+ 2 color mode:
+ set bit in characterbitmap gives pixel in color of the lower 3 color bits
+ cleared bit gives pixel in backgroundcolor
+ 4 color mode:
+ 2 bits in the characterbitmap are viewed together
+ 00: backgroundcolor
+ 11: colorram
+ 01: helpercolor
+ 10: framecolor
+ advance to next character in videorram until line is full
+ repeat this 8 or 16 lines, before moving to next line in videoram
+ screen ratio ntsc, pal 4/3
+
+ pal version:
+ can contain greater visible areas
+ expects other sync position (so ntsc modules may be displayed at
+ the upper left corner of the tv screen)
+ pixel ratio seems to be different on pal and ntsc
+
+ commodore vic20 notes
+ 6560 address line 13 is connected inverted to address line 15 of the board
+ 1 K 4 bit ram at 0x9400 is additional connected as 4 higher bits
+ of the 6560 (colorram) without decoding the 6560 address line a8..a13
+
+*****************************************************************************/
+
+
+#include "emu.h"
+#include "sound/mos6560.h"
+
+
+/*****************************************************************************
+ PARAMETERS
+*****************************************************************************/
+
+#define VERBOSE_LEVEL 0
+#define DBG_LOG(N,M,A) \
+ do { \
+ if(VERBOSE_LEVEL >= N) \
+ { \
+ if( M ) \
+ logerror("%11.6f: %-24s", machine().time().as_double(), (char*) M ); \
+ logerror A; \
+ } \
+ } while (0)
+
+
+/* 2008-05 FP: lightpen code needs to read input port from vc20.c */
+
+#define LIGHTPEN_BUTTON ((!m_lightpen_button_cb.isnull()) ? m_lightpen_button_cb(0) : 0)
+#define LIGHTPEN_X_VALUE ((!m_lightpen_x_cb.isnull()) ? m_lightpen_x_cb(0) : 0)
+#define LIGHTPEN_Y_VALUE ((!m_lightpen_y_cb.isnull()) ? m_lightpen_y_cb(0) : 0)
+
+/* lightpen delivers values from internal counters
+ * they do not start with the visual area or frame area */
+#define MOS6560_X_BEGIN 38
+#define MOS6560_Y_BEGIN -6 /* first 6 lines after retrace not for lightpen! */
+#define MOS6561_X_BEGIN 38
+#define MOS6561_Y_BEGIN -6
+#define MOS656X_X_BEGIN ((m_variant == TYPE_6561) ? MOS6561_X_BEGIN : MOS6560_X_BEGIN)
+#define MOS656X_Y_BEGIN ((m_variant == TYPE_6561) ? MOS6561_Y_BEGIN : MOS6560_Y_BEGIN)
+
+#define MOS656X_MAME_XPOS ((m_variant == TYPE_6561) ? MOS6561_MAME_XPOS : MOS6560_MAME_XPOS)
+#define MOS656X_MAME_YPOS ((m_variant == TYPE_6561) ? MOS6561_MAME_YPOS : MOS6560_MAME_YPOS)
+
+/* lightpen behaviour in pal or mono multicolor not tested */
+#define MOS656X_X_VALUE ((LIGHTPEN_X_VALUE + MOS656X_X_BEGIN + MOS656X_MAME_XPOS)/2)
+#define MOS656X_Y_VALUE ((LIGHTPEN_Y_VALUE + MOS656X_Y_BEGIN + MOS656X_MAME_YPOS)/2)
+
+#define MOS656X_VRETRACERATE ((m_variant == TYPE_6561) ? MOS6561_VRETRACERATE : MOS6560_VRETRACERATE)
+
+/* ntsc 1 - 8 */
+/* pal 5 - 19 */
+#define XPOS (((int)m_reg[0] & 0x7f) * 4)
+#define YPOS ((int)m_reg[1] * 2)
+
+/* ntsc values >= 31 behave like 31 */
+/* pal value >= 32 behave like 32 */
+#define CHARS_X ((int)m_reg[2] & 0x7f)
+#define CHARS_Y (((int)m_reg[3] & 0x7e) >> 1)
+
+/* colorram and backgroundcolor are changed */
+#define INVERTED (!(m_reg[0x0f] & 8))
+
+#define CHARGENADDR (((int)m_reg[5] & 0x0f) << 10)
+#define VIDEOADDR ((((int)m_reg[5] & 0xf0) << (10 - 4)) | (((int)m_reg[2] & 0x80) << (9-7)))
+#define VIDEORAMSIZE (YSIZE * XSIZE)
+#define CHARGENSIZE (256 * HEIGHTPIXEL)
+
+#define HELPERCOLOR (m_reg[0x0e] >> 4)
+#define BACKGROUNDCOLOR (m_reg[0x0f] >> 4)
+#define FRAMECOLOR (m_reg[0x0f] & 0x07)
+
+
+// VICE palette
+static const rgb_t PALETTE_MOS[] =
+{
+ rgb_t(0x00, 0x00, 0x00),
+ rgb_t(0xff, 0xff, 0xff),
+ rgb_t(0xf0, 0x00, 0x00),
+ rgb_t(0x00, 0xf0, 0xf0),
+
+ rgb_t(0x60, 0x00, 0x60),
+ rgb_t(0x00, 0xa0, 0x00),
+ rgb_t(0x00, 0x00, 0xf0),
+ rgb_t(0xd0, 0xd0, 0x00),
+
+ rgb_t(0xc0, 0xa0, 0x00),
+ rgb_t(0xff, 0xa0, 0x00),
+ rgb_t(0xf0, 0x80, 0x80),
+ rgb_t(0x00, 0xff, 0xff),
+
+ rgb_t(0xff, 0x00, 0xff),
+ rgb_t(0x00, 0xff, 0x00),
+ rgb_t(0x00, 0xa0, 0xff),
+ rgb_t(0xff, 0xff, 0x00)
+};
+
+
+
+/*****************************************************************************
+ IMPLEMENTATION
+*****************************************************************************/
+
+inline UINT8 mos6560_device::read_videoram(offs_t offset)
+{
+ m_last_data = space(AS_0).read_byte(offset & 0x3fff);
+
+ return m_last_data;
+}
+
+inline UINT8 mos6560_device::read_colorram(offs_t offset)
+{
+ return space(AS_1).read_byte(offset & 0x3ff);
+}
+
+/*-------------------------------------------------
+ draw_character
+-------------------------------------------------*/
+
+void mos6560_device::draw_character( int ybegin, int yend, int ch, int yoff, int xoff, UINT16 *color )
+{
+ int y, code;
+
+ for (y = ybegin; y <= yend; y++)
+ {
+ code = read_videoram((m_chargenaddr + ch * m_charheight + y) & 0x3fff);
+
+ m_bitmap.pix32(y + yoff, xoff + 0) = PALETTE_MOS[color[code >> 7]];
+ m_bitmap.pix32(y + yoff, xoff + 1) = PALETTE_MOS[color[(code >> 6) & 1]];
+ m_bitmap.pix32(y + yoff, xoff + 2) = PALETTE_MOS[color[(code >> 5) & 1]];
+ m_bitmap.pix32(y + yoff, xoff + 3) = PALETTE_MOS[color[(code >> 4) & 1]];
+ m_bitmap.pix32(y + yoff, xoff + 4) = PALETTE_MOS[color[(code >> 3) & 1]];
+ m_bitmap.pix32(y + yoff, xoff + 5) = PALETTE_MOS[color[(code >> 2) & 1]];
+ m_bitmap.pix32(y + yoff, xoff + 6) = PALETTE_MOS[color[(code >> 1) & 1]];
+ m_bitmap.pix32(y + yoff, xoff + 7) = PALETTE_MOS[color[code & 1]];
+ }
+}
+
+
+/*-------------------------------------------------
+ draw_character_multi
+-------------------------------------------------*/
+
+void mos6560_device::draw_character_multi( int ybegin, int yend, int ch, int yoff, int xoff, UINT16 *color )
+{
+ int y, code;
+
+ for (y = ybegin; y <= yend; y++)
+ {
+ code = read_videoram((m_chargenaddr + ch * m_charheight + y) & 0x3fff);
+
+ m_bitmap.pix32(y + yoff, xoff + 0) =
+ m_bitmap.pix32(y + yoff, xoff + 1) = PALETTE_MOS[color[code >> 6]];
+ m_bitmap.pix32(y + yoff, xoff + 2) =
+ m_bitmap.pix32(y + yoff, xoff + 3) = PALETTE_MOS[color[(code >> 4) & 3]];
+ m_bitmap.pix32(y + yoff, xoff + 4) =
+ m_bitmap.pix32(y + yoff, xoff + 5) = PALETTE_MOS[color[(code >> 2) & 3]];
+ m_bitmap.pix32(y + yoff, xoff + 6) =
+ m_bitmap.pix32(y + yoff, xoff + 7) = PALETTE_MOS[color[code & 3]];
+ }
+}
+
+
+/*-------------------------------------------------
+ drawlines - draw a certain numer of lines
+-------------------------------------------------*/
+
+void mos6560_device::drawlines( int first, int last )
+{
+ int line, vline;
+ int offs, yoff, xoff, ybegin, yend, i, j;
+ int attr, ch;
+
+ m_lastline = last;
+ if (first >= last)
+ return;
+
+ for (line = first; (line < m_ypos) && (line < last); line++)
+ {
+ for (j = 0; j < m_total_xsize; j++)
+ m_bitmap.pix32(line, j) = PALETTE_MOS[m_framecolor];
+ }
+
+ for (vline = line - m_ypos; (line < last) && (line < m_ypos + m_ysize);)
+ {
+ if (m_matrix8x16)
+ {
+ offs = (vline >> 4) * m_chars_x;
+ yoff = (vline & ~0xf) + m_ypos;
+ ybegin = vline & 0xf;
+ yend = (vline + 0xf < last - m_ypos) ? 0xf : ((last - line) & 0xf) + ybegin;
+ }
+ else
+ {
+ offs = (vline >> 3) * m_chars_x;
+ yoff = (vline & ~7) + m_ypos;
+ ybegin = vline & 7;
+ yend = (vline + 7 < last - m_ypos) ? 7 : ((last - line) & 7) + ybegin;
+ }
+
+ if (m_xpos > 0)
+ {
+ for (i = ybegin; i <= yend; i++)
+ for (j = 0; j < m_xpos; j++)
+ m_bitmap.pix32(yoff + i, j) = PALETTE_MOS[m_framecolor];
+ }
+
+ for (xoff = m_xpos; (xoff < m_xpos + m_xsize) && (xoff < m_total_xsize); xoff += 8, offs++)
+ {
+ ch = read_videoram((m_videoaddr + offs) & 0x3fff);
+
+ attr = (read_colorram((m_videoaddr + offs) & 0x3fff)) & 0xf;
+
+ if (m_variant == TYPE_ATTACK_UFO)
+ {
+ /* the mos6560 variant used in attckufo only has only one draw mode */
+ m_mono[1] = attr;
+ draw_character(ybegin, yend, ch, yoff, xoff, m_mono);
+ }
+ else if (m_inverted)
+ {
+ if (attr & 8)
+ {
+ m_multiinverted[0] = attr & 7;
+ draw_character_multi(ybegin, yend, ch, yoff, xoff, m_multiinverted);
+ }
+ else
+ {
+ m_monoinverted[0] = attr;
+ draw_character(ybegin, yend, ch, yoff, xoff, m_monoinverted);
+ }
+ }
+ else
+ {
+ if (attr & 8)
+ {
+ m_multi[2] = attr & 7;
+ draw_character_multi(ybegin, yend, ch, yoff, xoff, m_multi);
+ }
+ else
+ {
+ m_mono[1] = attr;
+ draw_character(ybegin, yend, ch, yoff, xoff, m_mono);
+ }
+ }
+ }
+
+ if (xoff < m_total_xsize)
+ {
+ for (i = ybegin; i <= yend; i++)
+ for (j = xoff; j < m_total_xsize; j++)
+ m_bitmap.pix32(yoff + i, j) = PALETTE_MOS[m_framecolor];
+ }
+
+ if (m_matrix8x16)
+ {
+ vline = (vline + 16) & ~0xf;
+ line = vline + m_ypos;
+ }
+ else
+ {
+ vline = (vline + 8) & ~7;
+ line = vline + m_ypos;
+ }
+ }
+
+ for (; line < last; line++)
+ for (j = 0; j < m_total_xsize; j++)
+ m_bitmap.pix32(line, j) = PALETTE_MOS[m_framecolor];
+}
+
+
+/*-------------------------------------------------
+ mos6560_port_w - write to regs
+-------------------------------------------------*/
+
+WRITE8_MEMBER( mos6560_device::write )
+{
+ DBG_LOG(1, "mos6560_port_w", ("%.4x:%.2x\n", offset, data));
+
+ switch (offset)
+ {
+ case 0xa:
+ case 0xb:
+ case 0xc:
+ case 0xd:
+ case 0xe:
+ soundport_w(offset, data);
+ break;
+ }
+
+ if (m_reg[offset] != data)
+ {
+ switch (offset)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 5:
+ case 0xe:
+ case 0xf:
+ drawlines(m_lastline, m_rasterline);
+ break;
+ }
+ m_reg[offset] = data;
+
+ switch (offset)
+ {
+ case 0:
+ if ((m_variant != TYPE_ATTACK_UFO))
+ m_xpos = XPOS;
+ break;
+ case 1:
+ if ((m_variant != TYPE_ATTACK_UFO))
+ m_ypos = YPOS;
+ break;
+ case 2:
+ /* ntsc values >= 31 behave like 31 */
+ /* pal value >= 32 behave like 32 */
+ m_chars_x = CHARS_X;
+ m_videoaddr = VIDEOADDR;
+ m_xsize = CHARS_X * 8;
+ break;
+ case 3:
+ if ((m_variant != TYPE_ATTACK_UFO))
+ {
+ m_matrix8x16 = data & 0x01;
+ m_charheight = m_matrix8x16 ? 16 : 8;
+ }
+ m_chars_y = CHARS_Y;
+ m_ysize = CHARS_Y * m_charheight;
+ break;
+ case 5:
+ m_chargenaddr = CHARGENADDR;
+ m_videoaddr = VIDEOADDR;
+ break;
+ case 0xe:
+ m_multi[3] = m_multiinverted[3] = m_helpercolor = HELPERCOLOR;
+ break;
+ case 0xf:
+ if ((m_variant != TYPE_ATTACK_UFO))
+ m_inverted = INVERTED;
+ m_multi[1] = m_multiinverted[1] = m_framecolor = FRAMECOLOR;
+ m_mono[0] = m_monoinverted[1] = m_multi[0] = m_multiinverted[2] = m_backgroundcolor = BACKGROUNDCOLOR;
+ break;
+ }
+ }
+}
+
+/*-------------------------------------------------
+ mos6560_port_r - read from regs
+-------------------------------------------------*/
+
+READ8_MEMBER( mos6560_device::read )
+{
+ int val;
+
+ switch (offset)
+ {
+ case 3:
+ val = ((m_rasterline & 1) << 7) | (m_reg[offset] & 0x7f);
+ break;
+ case 4: /*rasterline */
+ drawlines(m_lastline, m_rasterline);
+ val = (m_rasterline / 2) & 0xff;
+ break;
+ case 6: /*lightpen horizontal */
+ case 7: /*lightpen vertical */
+#ifdef UNUSED_FUNCTION
+ if (LIGHTPEN_BUTTON && ((machine().time().as_double() - m_lightpenreadtime) * MOS656X_VRETRACERATE >= 1))
+ {
+ /* only 1 update each frame */
+ /* and diode must recognize light */
+ if (1)
+ {
+ m_reg[6] = MOS656X_X_VALUE;
+ m_reg[7] = MOS656X_Y_VALUE;
+ }
+ m_lightpenreadtime = machine().time().as_double();
+ }
+#endif
+ val = m_reg[offset];
+ break;
+ case 8: /* poti 1 */
+ val = m_read_potx(0);
+ break;
+ case 9: /* poti 2 */
+ val = m_read_poty(0);
+ break;
+ default:
+ val = m_reg[offset];
+ break;
+ }
+ DBG_LOG(3, "mos6560_port_r", ("%.4x:%.2x\n", offset, val));
+ return val;
+}
+
+WRITE_LINE_MEMBER( mos6560_device::lp_w )
+{
+ // TODO
+}
+
+UINT8 mos6560_device::bus_r()
+{
+ return m_last_data;
+}
+
+/*-------------------------------------------------
+ mos6560_raster_interrupt_gen
+-------------------------------------------------*/
+
+void mos6560_device::raster_interrupt_gen()
+{
+ m_rasterline++;
+ if (m_rasterline >= m_total_lines)
+ {
+ m_rasterline = 0;
+ drawlines(m_lastline, m_total_lines);
+ m_lastline = 0;
+ }
+}
+
+
+/*-------------------------------------------------
+ mos6560_video_update - copy the VIC bitmap to
+ main screen bitmap
+-------------------------------------------------*/
+
+UINT32 mos6560_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect);
+
+ return 0;
+}
+
+/*****************************************************************************
+ SOUND IMPLEMENTATION
+*****************************************************************************/
+
+/*
+ * assumed model:
+ * each write to a ton/noise generated starts it new
+ * each generator behaves like an timer
+ * when it reaches 0, the next samplevalue is given out
+ */
+
+/*
+ * noise channel
+ * based on a document by diku0748@diku.dk (Asger Alstrup Nielsen)
+ *
+ * 23 bit shift register
+ * initial value (0x7ffff8)
+ * after shift bit 0 is set to bit 22 xor bit 17
+ * dac sample bit22 bit20 bit16 bit13 bit11 bit7 bit4 bit2(lsb)
+ *
+ * emulation:
+ * allocate buffer for 5 sec sampledata (fastest played frequency)
+ * and fill this buffer in init with the required sample
+ * fast turning off channel, immediate change of frequency
+ */
+
+#define NOISE_BUFFER_SIZE_SEC 5
+
+#define TONE1_ON (m_reg[0x0a] & 0x80)
+#define TONE2_ON (m_reg[0x0b] & 0x80)
+#define TONE3_ON (m_reg[0x0c] & 0x80)
+#define NOISE_ON (m_reg[0x0d] & 0x80)
+#define VOLUME (m_reg[0x0e] & 0x0f)
+
+#define TONE_FREQUENCY_MIN (clock()/256/128)
+
+#define TONE1_VALUE (8 * (128 - ((m_reg[0x0a] + 1) & 0x7f)))
+#define TONE1_FREQUENCY (clock()/32/TONE1_VALUE)
+
+#define TONE2_VALUE (4 * (128 - ((m_reg[0x0b] + 1) & 0x7f)))
+#define TONE2_FREQUENCY (clock()/32/TONE2_VALUE)
+
+#define TONE3_VALUE (2 * (128 - ((m_reg[0x0c] + 1) & 0x7f)))
+#define TONE3_FREQUENCY (clock()/32/TONE3_VALUE)
+
+#define NOISE_VALUE (32 * (128 - ((m_reg[0x0d] + 1) & 0x7f)))
+#define NOISE_FREQUENCY (clock()/NOISE_VALUE)
+
+#define NOISE_FREQUENCY_MAX (clock()/32/1)
+
+
+/*-------------------------------------------------
+ mos6560_soundport_w - write to regs
+-------------------------------------------------*/
+
+void mos6560_device::soundport_w( int offset, int data )
+{
+ int old = m_reg[offset];
+ m_channel->update();
+
+ switch (offset)
+ {
+ case 0x0a:
+ m_reg[offset] = data;
+ if (!(old & 0x80) && TONE1_ON)
+ {
+ m_tone1pos = 0;
+ m_tone1samples = machine().sample_rate() / TONE1_FREQUENCY;
+ if (m_tone1samples == 0)
+ m_tone1samples = 1;
+ }
+ DBG_LOG(1, "mos6560", ("tone1 %.2x %d\n", data, TONE1_FREQUENCY));
+ break;
+ case 0x0b:
+ m_reg[offset] = data;
+ if (!(old & 0x80) && TONE2_ON)
+ {
+ m_tone2pos = 0;
+ m_tone2samples = machine().sample_rate() / TONE2_FREQUENCY;
+ if (m_tone2samples == 0)
+ m_tone2samples = 1;
+ }
+ DBG_LOG(1, "mos6560", ("tone2 %.2x %d\n", data, TONE2_FREQUENCY));
+ break;
+ case 0x0c:
+ m_reg[offset] = data;
+ if (!(old & 0x80) && TONE3_ON)
+ {
+ m_tone3pos = 0;
+ m_tone3samples = machine().sample_rate() / TONE3_FREQUENCY;
+ if (m_tone3samples == 0)
+ m_tone3samples = 1;
+ }
+ DBG_LOG(1, "mos6560", ("tone3 %.2x %d\n", data, TONE3_FREQUENCY));
+ break;
+ case 0x0d:
+ m_reg[offset] = data;
+ if (NOISE_ON)
+ {
+ m_noisesamples = (int) ((double) NOISE_FREQUENCY_MAX * machine().sample_rate()
+ * NOISE_BUFFER_SIZE_SEC / NOISE_FREQUENCY);
+ DBG_LOG (1, "mos6560", ("noise %.2x %d sample:%d\n",
+ data, NOISE_FREQUENCY, m_noisesamples));
+ if ((double) m_noisepos / m_noisesamples >= 1.0)
+ {
+ m_noisepos = 0;
+ }
+ }
+ else
+ {
+ m_noisepos = 0;
+ }
+ break;
+ case 0x0e:
+ m_reg[offset] = (old & ~0x0f) | (data & 0x0f);
+ DBG_LOG (3, "mos6560", ("volume %d\n", data & 0x0f));
+ break;
+ }
+}
+
+
+/*****************************************************************************
+ DEVICE INTERFACE
+*****************************************************************************/
+
+/*-------------------------------------------------
+ mos6560_sound_start - start audio emulation
+ (to be called at device start)
+-------------------------------------------------*/
+
+void mos6560_device::sound_start()
+{
+ int i;
+
+ m_channel = machine().sound().stream_alloc(*this, 0, 1, machine().sample_rate());
+
+ /* buffer for fastest played sample for 5 second so we have enough data for min 5 second */
+ m_noisesize = NOISE_FREQUENCY_MAX * NOISE_BUFFER_SIZE_SEC;
+ m_noise = auto_alloc_array(machine(), INT8, m_noisesize);
+ {
+ int noiseshift = 0x7ffff8;
+ char data;
+
+ for (i = 0; i < m_noisesize; i++)
+ {
+ data = 0;
+ if (noiseshift & 0x400000)
+ data |= 0x80;
+ if (noiseshift & 0x100000)
+ data |= 0x40;
+ if (noiseshift & 0x010000)
+ data |= 0x20;
+ if (noiseshift & 0x002000)
+ data |= 0x10;
+ if (noiseshift & 0x000800)
+ data |= 0x08;
+ if (noiseshift & 0x000080)
+ data |= 0x04;
+ if (noiseshift & 0x000010)
+ data |= 0x02;
+ if (noiseshift & 0x000004)
+ data |= 0x01;
+ m_noise[i] = data;
+ if (((noiseshift & 0x400000) == 0) != ((noiseshift & 0x002000) == 0))
+ noiseshift = (noiseshift << 1) | 1;
+ else
+ noiseshift <<= 1;
+ }
+ }
+ m_tonesize = machine().sample_rate() / TONE_FREQUENCY_MIN;
+
+ if (m_tonesize > 0)
+ {
+ m_tone = auto_alloc_array(machine(), INT16, m_tonesize);
+
+ for (i = 0; i < m_tonesize; i++)
+ {
+ m_tone[i] = (INT16)(sin (2 * M_PI * i / m_tonesize) * 127 + 0.5);
+ }
+ }
+ else
+ {
+ m_tone = NULL;
+ }
+}
+
+
+const device_type MOS6560 = &device_creator<mos6560_device>;
+const device_type MOS6561 = &device_creator<mos6561_device>;
+const device_type MOS656X_ATTACK_UFO = &device_creator<mos656x_attack_ufo_device>;
+
+// default address maps
+static ADDRESS_MAP_START( mos6560_videoram_map, AS_0, 8, mos6560_device )
+ AM_RANGE(0x0000, 0x3fff) AM_RAM
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( mos6560_colorram_map, AS_1, 8, mos6560_device )
+ AM_RANGE(0x000, 0x3ff) AM_RAM
+ADDRESS_MAP_END
+
+mos6560_device::mos6560_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 variant, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_memory_interface(mconfig, *this),
+ device_sound_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_variant(variant),
+ m_videoram_space_config("videoram", ENDIANNESS_LITTLE, 8, 14, 0, NULL, *ADDRESS_MAP_NAME(mos6560_videoram_map)),
+ m_colorram_space_config("colorram", ENDIANNESS_LITTLE, 8, 10, 0, NULL, *ADDRESS_MAP_NAME(mos6560_colorram_map)),
+ m_read_potx(*this),
+ m_read_poty(*this)
+{
+}
+
+mos6560_device::mos6560_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MOS6560, "MOS6560", tag, owner, clock, "mos6560", __FILE__),
+ device_memory_interface(mconfig, *this),
+ device_sound_interface(mconfig, *this),
+ device_video_interface(mconfig, *this),
+ m_variant(TYPE_6560),
+ m_videoram_space_config("videoram", ENDIANNESS_LITTLE, 8, 14, 0, NULL, *ADDRESS_MAP_NAME(mos6560_videoram_map)),
+ m_colorram_space_config("colorram", ENDIANNESS_LITTLE, 8, 10, 0, NULL, *ADDRESS_MAP_NAME(mos6560_colorram_map)),
+ m_read_potx(*this),
+ m_read_poty(*this)
+{
+}
+
+mos6561_device::mos6561_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ :mos6560_device(mconfig, MOS6561, "MOS6561", tag, owner, clock, TYPE_6561, "mos6561", __FILE__) { }
+
+mos656x_attack_ufo_device::mos656x_attack_ufo_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ :mos6560_device(mconfig, MOS656X_ATTACK_UFO, "MOS656X", tag, owner, clock, TYPE_ATTACK_UFO, "mos656x_attack_ufo", __FILE__) { }
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+const address_space_config *mos6560_device::memory_space_config(address_spacenum spacenum) const
+{
+ switch (spacenum)
+ {
+ case AS_0: return &m_videoram_space_config;
+ case AS_1: return &m_colorram_space_config;
+ default: return NULL;
+ }
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void mos6560_device::device_start()
+{
+ m_screen->register_screen_bitmap(m_bitmap);
+
+ // resolve callbacks
+ m_read_potx.resolve_safe(0xff);
+ m_read_poty.resolve_safe(0xff);
+
+ switch (m_variant)
+ {
+ case TYPE_6560:
+ m_total_xsize = MOS6560_XSIZE;
+ m_total_ysize = MOS6560_YSIZE;
+ m_total_lines = MOS6560_LINES;
+ m_total_vretracerate = MOS6560_VRETRACERATE;
+ break;
+
+ case TYPE_ATTACK_UFO:
+ m_total_xsize = 23 * 8;
+ m_total_ysize = 22 * 8;
+ m_total_lines = MOS6560_LINES;
+ m_total_vretracerate = MOS6560_VRETRACERATE;
+ break;
+
+ case TYPE_6561:
+ m_total_xsize = MOS6561_XSIZE;
+ m_total_ysize = MOS6561_YSIZE;
+ m_total_lines = MOS6561_LINES;
+ m_total_vretracerate = MOS6561_VRETRACERATE;
+ break;
+ }
+
+ // allocate timers
+ m_line_timer = timer_alloc(TIMER_LINE);
+ m_line_timer->adjust(m_screen->scan_period(), 0, m_screen->scan_period());
+
+ // initialize sound
+ sound_start();
+
+ // state saving
+ save_item(NAME(m_lightpenreadtime));
+ save_item(NAME(m_rasterline));
+ save_item(NAME(m_lastline));
+
+ save_item(NAME(m_charheight));
+ save_item(NAME(m_matrix8x16));
+ save_item(NAME(m_inverted));
+ save_item(NAME(m_chars_x));
+ save_item(NAME(m_chars_y));
+ save_item(NAME(m_xsize));
+ save_item(NAME(m_ysize));
+ save_item(NAME(m_xpos));
+ save_item(NAME(m_ypos));
+ save_item(NAME(m_chargenaddr));
+ save_item(NAME(m_videoaddr));
+
+ save_item(NAME(m_backgroundcolor));
+ save_item(NAME(m_framecolor));
+ save_item(NAME(m_helpercolor));
+
+ save_item(NAME(m_reg));
+
+ save_item(NAME(m_mono));
+ save_item(NAME(m_monoinverted));
+ save_item(NAME(m_multi));
+ save_item(NAME(m_multiinverted));
+
+ save_item(NAME(m_last_data));
+
+ save_item(NAME(m_tone1pos));
+ save_item(NAME(m_tone2pos));
+ save_item(NAME(m_tone3pos));
+ save_item(NAME(m_tone1samples));
+ save_item(NAME(m_tone2samples));
+ save_item(NAME(m_tone3samples));
+ save_item(NAME(m_noisepos));
+ save_item(NAME(m_noisesamples));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void mos6560_device::device_reset()
+{
+ m_lightpenreadtime = 0.0;
+ m_rasterline = 0;
+ m_lastline = 0;
+
+ memset(m_reg, 0, 16);
+
+ m_charheight = 8;
+ m_matrix8x16 = 0;
+ m_inverted = 0;
+ m_chars_x = 0;
+ m_chars_y = 0;
+ m_xsize = 0;
+ m_ysize = 0;
+ m_xpos = 0;
+ m_ypos = 0;
+ m_chargenaddr = 0;
+ m_videoaddr = 0;
+
+ m_backgroundcolor = 0;
+ m_framecolor = 0;
+ m_helpercolor = 0;
+
+ m_mono[0] = 0;
+ m_mono[1] = 0;
+ m_monoinverted[0] = 0;
+ m_monoinverted[1] = 0;
+ m_multi[0] = 0;
+ m_multi[1] = 0;
+ m_multi[2] = 0;
+ m_multi[3] = 0;
+ m_multiinverted[0] = 0;
+ m_multiinverted[1] = 0;
+ m_multiinverted[2] = 0;
+ m_multiinverted[3] = 0;
+
+ m_last_data = 0;
+
+ m_tone1pos = 0;
+ m_tone2pos = 0;
+ m_tone3pos = 0;
+ m_tone1samples = 1;
+ m_tone2samples = 1;
+ m_tone3samples = 1;
+ m_noisepos = 0;
+ m_noisesamples = 1;
+}
+
+
+//-------------------------------------------------
+// device_timer - handler timer events
+//-------------------------------------------------
+
+void mos6560_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case TIMER_LINE:
+ raster_interrupt_gen();
+ break;
+ }
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle a stream update
+//-------------------------------------------------
+
+void mos6560_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ int i, v;
+ stream_sample_t *buffer = outputs[0];
+
+ for (i = 0; i < samples; i++)
+ {
+ v = 0;
+ if (TONE1_ON /*||(m_tone1pos != 0) */ )
+ {
+ v += m_tone[m_tone1pos * m_tonesize / m_tone1samples];
+ m_tone1pos++;
+#if 0
+ m_tone1pos %= m_tone1samples;
+#else
+ if (m_tone1pos >= m_tone1samples)
+ {
+ m_tone1pos = 0;
+ m_tone1samples = machine().sample_rate() / TONE1_FREQUENCY;
+ if (m_tone1samples == 0)
+ m_tone1samples = 1;
+ }
+#endif
+ }
+ if (TONE2_ON /*||(m_tone2pos != 0) */ )
+ {
+ v += m_tone[m_tone2pos * m_tonesize / m_tone2samples];
+ m_tone2pos++;
+#if 0
+ m_tone2pos %= m_tone2samples;
+#else
+ if (m_tone2pos >= m_tone2samples)
+ {
+ m_tone2pos = 0;
+ m_tone2samples = machine().sample_rate() / TONE2_FREQUENCY;
+ if (m_tone2samples == 0)
+ m_tone2samples = 1;
+ }
+#endif
+ }
+ if (TONE3_ON /*||(m_tone3pos != 0) */ )
+ {
+ v += m_tone[m_tone3pos * m_tonesize / m_tone3samples];
+ m_tone3pos++;
+#if 0
+ m_tone3pos %= m_tone3samples;
+#else
+ if (m_tone3pos >= m_tone3samples)
+ {
+ m_tone3pos = 0;
+ m_tone3samples = machine().sample_rate() / TONE3_FREQUENCY;
+ if (m_tone3samples == 0)
+ m_tone3samples = 1;
+ }
+#endif
+ }
+ if (NOISE_ON)
+ {
+ v += m_noise[(int) ((double) m_noisepos * m_noisesize / m_noisesamples)];
+ m_noisepos++;
+ if ((double) m_noisepos / m_noisesamples >= 1.0)
+ {
+ m_noisepos = 0;
+ }
+ }
+ v = (v * VOLUME) << 2;
+ if (v > 32767)
+ buffer[i] = 32767;
+ else if (v < -32767)
+ buffer[i] = -32767;
+ else
+ buffer[i] = v;
+ }
+}