summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/acorn_vidc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/acorn_vidc.cpp')
-rw-r--r--src/devices/machine/acorn_vidc.cpp340
1 files changed, 297 insertions, 43 deletions
diff --git a/src/devices/machine/acorn_vidc.cpp b/src/devices/machine/acorn_vidc.cpp
index 4ad88d5d4df..94ddfb8b3c8 100644
--- a/src/devices/machine/acorn_vidc.cpp
+++ b/src/devices/machine/acorn_vidc.cpp
@@ -30,6 +30,7 @@
// device type definition
DEFINE_DEVICE_TYPE(ACORN_VIDC10, acorn_vidc10_device, "acorn_vidc10", "Acorn VIDC10")
DEFINE_DEVICE_TYPE(ACORN_VIDC10_LCD, acorn_vidc10_lcd_device, "acorn_vidc10_lcd", "Acorn VIDC10 with LCD monitor")
+DEFINE_DEVICE_TYPE(ARM_VIDC20, arm_vidc20_device, "arm_vidc20", "ARM VIDC20")
//**************************************************************************
@@ -51,25 +52,23 @@ void acorn_vidc10_device::regs_map(address_map &map)
}
-acorn_vidc10_device::acorn_vidc10_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+acorn_vidc10_device::acorn_vidc10_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock)
: device_t(mconfig, type, tag, owner, clock)
, device_memory_interface(mconfig, *this)
, device_palette_interface(mconfig, *this)
, device_video_interface(mconfig, *this)
- , m_space_config("regs_space", ENDIANNESS_LITTLE, 32, 8, 0, address_map_constructor(FUNC(acorn_vidc10_device::regs_map), this))
+ , m_bpp_mode(0)
+ , m_crtc_interlace(0)
+ , m_sound_frequency_latch(0)
+ , m_sound_mode(false)
+ , m_dac(*this, "dac%u", 0)
, m_lspeaker(*this, "lspeaker")
, m_rspeaker(*this, "rspeaker")
- , m_dac(*this, "dac%u", 0)
, m_vblank_cb(*this)
, m_sound_drq_cb(*this)
, m_pixel_clock(0)
- , m_bpp_mode(0)
- , m_crtc_interlace(0)
, m_cursor_enable(false)
- , m_sound_frequency_latch(0)
, m_sound_frequency_test_bit(false)
- , m_sound_mode(false)
-
{
std::fill(std::begin(m_crtc_regs), std::end(m_crtc_regs), 0);
std::fill(std::begin(m_stereo_image), std::end(m_stereo_image), 0);
@@ -78,12 +77,21 @@ acorn_vidc10_device::acorn_vidc10_device(const machine_config &mconfig, device_t
acorn_vidc10_device::acorn_vidc10_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: acorn_vidc10_device(mconfig, ACORN_VIDC10, tag, owner, clock)
{
+ m_space_config = address_space_config("regs_space", ENDIANNESS_LITTLE, 32, 8, 0, address_map_constructor(FUNC(acorn_vidc10_device::regs_map), this));
+ m_pal_4bpp_base = 0x100;
+ m_pal_cursor_base = 0x10;
+ m_pal_border_base = 0x110;
}
-acorn_vidc10_lcd_device::acorn_vidc10_lcd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+acorn_vidc10_lcd_device::acorn_vidc10_lcd_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: acorn_vidc10_device(mconfig, ACORN_VIDC10_LCD, tag, owner, clock)
{
+ m_space_config = address_space_config("regs_space", ENDIANNESS_LITTLE, 32, 8, 0, address_map_constructor(FUNC(acorn_vidc10_lcd_device::regs_map), this));
+ // TODO: confirm being identical to raster version
+ m_pal_4bpp_base = 0x100;
+ m_pal_cursor_base = 0x10;
+ m_pal_border_base = 0x110;
}
device_memory_interface::space_config_vector acorn_vidc10_device::memory_space_config() const
@@ -118,7 +126,7 @@ void acorn_vidc10_lcd_device::device_add_mconfig(machine_config &config)
// TODO: verify !Configure with automatic type detection, there must be an ID telling this is a LCD machine.
}
-uint32_t acorn_vidc10_device::palette_entries() const
+u32 acorn_vidc10_device::palette_entries() const
{
return 0x100+0x10+4; // 8bpp + 1/2/4bpp + 2bpp for cursor
}
@@ -160,8 +168,9 @@ void acorn_vidc10_device::device_start()
save_item(NAME(m_sound_frequency_test_bit));
save_item(NAME(m_cursor_enable));
save_pointer(NAME(m_crtc_regs), CRTC_VCER+1);
- m_data_vram = auto_alloc_array_clear(machine(), u8, m_data_vram_size);
- m_cursor_vram = auto_alloc_array_clear(machine(), u8, m_cursor_vram_size);
+ save_pointer(NAME(m_crtc_raw_horz), 2);
+ m_data_vram = make_unique_clear<u8[]>(m_data_vram_size);
+ m_cursor_vram = make_unique_clear<u8[]>(m_cursor_vram_size);
save_pointer(NAME(m_data_vram), m_data_vram_size);
save_pointer(NAME(m_cursor_vram), m_cursor_vram_size);
save_pointer(NAME(m_stereo_image), m_sound_max_channels);
@@ -174,8 +183,8 @@ void acorn_vidc10_device::device_start()
// TODO: manual mentions a format difference between VIDC10 revisions
for (int rawval = 0; rawval < 256; rawval++)
{
- uint8_t chord = rawval >> 5;
- uint8_t point = (rawval & 0x1e) >> 1;
+ u8 chord = rawval >> 5;
+ u8 point = (rawval & 0x1e) >> 1;
bool sign = rawval & 1;
int16_t result = ((16+point)<<chord)-16;
@@ -184,6 +193,8 @@ void acorn_vidc10_device::device_start()
m_ulaw_lookup[rawval] = result*8;
}
+
+ // saved for debugging purposes
save_pointer(NAME(m_ulaw_lookup), 256);
}
@@ -195,8 +206,6 @@ void acorn_vidc10_device::device_start()
void acorn_vidc10_device::device_reset()
{
m_cursor_enable = false;
- memset(m_data_vram, 0, m_data_vram_size);
- memset(m_cursor_vram, 0, m_cursor_vram_size);
memset(m_stereo_image, 4, m_sound_max_channels);
for (int ch=0;ch<m_sound_max_channels;ch++)
refresh_stereo_image(ch);
@@ -232,9 +241,15 @@ inline void acorn_vidc10_device::screen_vblank_line_update()
m_video_timer->adjust((vline > 2) ? screen().time_until_pos(vline) : attotime::never);
}
-void acorn_vidc10_device::screen_dynamic_res_change()
+u32 acorn_vidc10_device::get_pixel_clock()
{
const int32_t pixel_rate[4] = { 8000000, 12000000, 16000000, 24000000};
+ return pixel_rate[m_pixel_clock];
+}
+
+inline void acorn_vidc10_device::screen_dynamic_res_change()
+{
+ const u32 pixel_clock = get_pixel_clock();
// sanity checks
if (m_crtc_regs[CRTC_HCR] <= 1 || m_crtc_regs[CRTC_VCR] <= 1)
@@ -267,7 +282,7 @@ void acorn_vidc10_device::screen_dynamic_res_change()
m_vidc_vblank_time);
#endif
- attoseconds_t const refresh = HZ_TO_ATTOSECONDS(pixel_rate[m_pixel_clock]) * m_crtc_regs[CRTC_HCR] * m_crtc_regs[CRTC_VCR];
+ attoseconds_t const refresh = HZ_TO_ATTOSECONDS(pixel_clock) * m_crtc_regs[CRTC_HCR] * m_crtc_regs[CRTC_VCR];
screen().configure(m_crtc_regs[CRTC_HCR], m_crtc_regs[CRTC_VCR] * (m_crtc_interlace+1), visarea, refresh);
}
@@ -279,13 +294,13 @@ void acorn_vidc10_device::screen_dynamic_res_change()
WRITE32_MEMBER( acorn_vidc10_device::write )
{
// TODO: check against mem_mask not 32-bit wide
- uint8_t reg = data >> 24;
- uint32_t val = data & 0xffffff;
-
+ u8 reg = data >> 24;
+ u32 val = data & 0xffffff;
+
this->space(AS_IO).write_dword(reg, val);
}
-inline void acorn_vidc10_device::update_4bpp_palette(uint16_t index, uint32_t paldata)
+inline void acorn_vidc10_device::update_4bpp_palette(u16 index, u32 paldata)
{
int r,g,b;
@@ -331,20 +346,34 @@ WRITE32_MEMBER( acorn_vidc10_device::control_w )
//m_test_mode = (data & 0xc100) != 0xc100;
//todo: vga/svga modes sets 0x1000?
+ m_crtc_regs[CRTC_HDSR] = convert_crtc_hdisplay(0);
+ m_crtc_regs[CRTC_HDER] = convert_crtc_hdisplay(1);
screen_vblank_line_update();
screen_dynamic_res_change();
}
+inline u32 acorn_vidc10_device::convert_crtc_hdisplay(u8 index)
+{
+ const u8 x_step[4] = { 19, 11, 7, 5 };
+ return (m_crtc_raw_horz[index]*2)+x_step[m_bpp_mode];
+}
+
WRITE32_MEMBER( acorn_vidc10_device::crtc_w )
{
switch(offset)
{
- case CRTC_HCR: m_crtc_regs[CRTC_HCR] = ((data >> 14)<<1)+2; break;
-// case CRTC_HSWR: m_crtc_regs[CRTC_HSWR] = (data >> 14)+1; break;
- case CRTC_HBSR: m_crtc_regs[CRTC_HBSR] = ((data >> 14)<<1)+1; break;
- case CRTC_HDSR: m_crtc_regs[CRTC_HDSR] = (data >> 14); break;
- case CRTC_HDER: m_crtc_regs[CRTC_HDER] = (data >> 14); break;
- case CRTC_HBER: m_crtc_regs[CRTC_HBER] = ((data >> 14)<<1)+1; break;
+ case CRTC_HCR: m_crtc_regs[CRTC_HCR] = ((data >> 14)<<1)+2; break;
+// case CRTC_HSWR: m_crtc_regs[CRTC_HSWR] = (data >> 14)+1; break;
+ case CRTC_HBSR: m_crtc_regs[CRTC_HBSR] = ((data >> 14)<<1)+1; break;
+ case CRTC_HDSR:
+ m_crtc_raw_horz[0] = (data >> 14);
+ m_crtc_regs[CRTC_HDSR] = convert_crtc_hdisplay(0);
+ break;
+ case CRTC_HDER:
+ m_crtc_raw_horz[1] = (data >> 14);
+ m_crtc_regs[CRTC_HDER] = convert_crtc_hdisplay(1);
+ break;
+ case CRTC_HBER: m_crtc_regs[CRTC_HBER] = ((data >> 14)<<1)+1; break;
case CRTC_HCSR: m_crtc_regs[CRTC_HCSR] = ((data >> 13) & 0x7ff) + 6; return;
// case CRTC_HIR: // ...
@@ -370,7 +399,7 @@ WRITE32_MEMBER( acorn_vidc10_device::crtc_w )
screen_dynamic_res_change();
}
-inline void acorn_vidc10_device::refresh_stereo_image(uint8_t channel)
+inline void acorn_vidc10_device::refresh_stereo_image(u8 channel)
{
/*
-111 full right
@@ -393,7 +422,7 @@ inline void acorn_vidc10_device::refresh_stereo_image(uint8_t channel)
WRITE32_MEMBER( acorn_vidc10_device::stereo_image_w )
{
- uint8_t channel = (offset + 7) & 0x7;
+ u8 channel = (offset + 7) & 0x7;
m_stereo_image[channel] = data & 0x7;
refresh_stereo_image(channel);
}
@@ -410,8 +439,8 @@ WRITE32_MEMBER( acorn_vidc10_device::sound_frequency_w )
// MEMC comms
//**************************************************************************
-void acorn_vidc10_device::write_dac(uint8_t channel, uint8_t data)
-{
+void acorn_vidc10_device::write_dac(u8 channel, u8 data)
+{
int16_t res;
res = m_ulaw_lookup[data];
m_dac[channel & 7]->write(res);
@@ -424,7 +453,7 @@ void acorn_vidc10_device::refresh_sound_frequency()
{
// TODO: Range is between 3 and 256 usecs
double sndhz = 1e6 / ((m_sound_frequency_latch & 0xff) + 2);
- sndhz /= 8.0;
+ sndhz /= m_sound_internal_divider;
m_sound_timer->adjust(attotime::zero, 0, attotime::from_hz(sndhz));
//printf("VIDC: audio DMA start, sound freq %d, sndhz = %f\n", (m_crtc_regs[0xc0] & 0xff)-2, sndhz);
}
@@ -436,9 +465,9 @@ void acorn_vidc10_device::refresh_sound_frequency()
// Screen Update / VBlank / HBlank
//**************************************************************************
-void acorn_vidc10_device::draw(bitmap_rgb32 &bitmap, const rectangle &cliprect, u8 *vram, uint8_t bpp, int xstart, int ystart, int xsize, int ysize, bool is_cursor)
+void acorn_vidc10_device::draw(bitmap_rgb32 &bitmap, const rectangle &cliprect, u8 *vram, u8 bpp, int xstart, int ystart, int xsize, int ysize, bool is_cursor)
{
- const u16 pen_base = (bpp == 3 ? 0 : 0x100) + (is_cursor == true ? 0x10 : 0);
+ const u16 pen_base = (bpp == 3 ? 0 : m_pal_4bpp_base) + (is_cursor == true ? m_pal_cursor_base : 0);
const u16 pen_masks[4] = { 1, 3, 0xf, 0xff };
const u16 pen_mask = pen_masks[bpp];
const u16 xchar_size = 1 << (3 - bpp);
@@ -476,14 +505,13 @@ u32 acorn_vidc10_device::screen_update(screen_device &screen, bitmap_rgb32 &bitm
int xstart,ystart,xend,yend;
int xsize,ysize;
int calc_dxs = 0,calc_dxe = 0;
- const uint8_t x_step[4] = { 19, 11, 7, 5 };
/* border color */
- bitmap.fill(pen(0x110), cliprect);
+ bitmap.fill(pen(m_pal_border_base), cliprect);
/* define X display area through BPP mode register */
- calc_dxs = (m_crtc_regs[CRTC_HDSR]*2)+x_step[m_bpp_mode & 3];
- calc_dxe = (m_crtc_regs[CRTC_HDER]*2)+x_step[m_bpp_mode & 3];
+ calc_dxs = m_crtc_regs[CRTC_HDSR];
+ calc_dxe = m_crtc_regs[CRTC_HDER];
/* now calculate display clip rectangle start/end areas */
xstart = (calc_dxs)-m_crtc_regs[CRTC_HBSR];
@@ -501,7 +529,7 @@ u32 acorn_vidc10_device::screen_update(screen_device &screen, bitmap_rgb32 &bitm
if (xsize <= 0 || ysize <= 0)
return 0;
- draw(bitmap, cliprect, m_data_vram, m_bpp_mode, xstart, ystart, xsize, ysize, false);
+ draw(bitmap, cliprect, m_data_vram.get(), m_bpp_mode, xstart, ystart, xsize, ysize, false);
if (m_cursor_enable == true)
{
xstart = m_crtc_regs[CRTC_HCSR] - m_crtc_regs[CRTC_HBSR];
@@ -509,7 +537,7 @@ u32 acorn_vidc10_device::screen_update(screen_device &screen, bitmap_rgb32 &bitm
xsize = 32;
ysize = m_crtc_regs[CRTC_VCER] - m_crtc_regs[CRTC_VCSR];
if (ysize > 0)
- draw(bitmap, cliprect, m_cursor_vram, 1, xstart, ystart, xsize, ysize, true);
+ draw(bitmap, cliprect, m_cursor_vram.get(), 1, xstart, ystart, xsize, ysize, true);
}
return 0;
@@ -518,6 +546,232 @@ u32 acorn_vidc10_device::screen_update(screen_device &screen, bitmap_rgb32 &bitm
READ_LINE_MEMBER(acorn_vidc10_device::flyback_r )
{
int vert_pos = screen().vpos();
- bool flyback = (vert_pos <= m_crtc_regs[CRTC_VDSR] || vert_pos >= m_crtc_regs[CRTC_VDER]);
- return flyback;
+ if (vert_pos <= m_crtc_regs[CRTC_VDSR] * (m_crtc_interlace+1))
+ return true;
+
+ if (vert_pos >= m_crtc_regs[CRTC_VDER] * (m_crtc_interlace+1))
+ return true;
+
+ return false;
+}
+
+// VIDC20
+
+void arm_vidc20_device::regs_map(address_map &map)
+{
+ map(0x00, 0x0f).w(FUNC(arm_vidc20_device::vidc20_pal_data_display_w));
+ map(0x10, 0x1f).w(FUNC(arm_vidc20_device::vidc20_pal_data_index_w));
+ map(0x40, 0x7f).w(FUNC(arm_vidc20_device::vidc20_pal_data_cursor_w));
+ map(0x80, 0x9f).w(FUNC(arm_vidc20_device::vidc20_crtc_w));
+ map(0xb0, 0xb0).w(FUNC(arm_vidc20_device::vidc20_sound_frequency_w));
+ map(0xb1, 0xb1).w(FUNC(arm_vidc20_device::vidc20_sound_control_w));
+ map(0xd0, 0xdf).w(FUNC(arm_vidc20_device::fsynreg_w));
+ map(0xe0, 0xef).w(FUNC(arm_vidc20_device::vidc20_control_w));
+}
+
+arm_vidc20_device::arm_vidc20_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : acorn_vidc10_device(mconfig, ARM_VIDC20, tag, owner, clock)
+{
+ m_space_config = address_space_config("regs_space", ENDIANNESS_LITTLE, 32, 8, -2, address_map_constructor(FUNC(arm_vidc20_device::regs_map), this));
+ m_pal_4bpp_base = 0x000;
+ m_pal_cursor_base = 0x100;
+ m_pal_border_base = 0x100;
+}
+
+
+void arm_vidc20_device::device_add_mconfig(machine_config &config)
+{
+ acorn_vidc10_device::device_add_mconfig(config);
+ // ...
+ // TODO: for simplicity we may as well add separate DACs for 32-bit mode
}
+
+void arm_vidc20_device::device_config_complete()
+{
+ if (!has_screen())
+ return;
+
+ if (!screen().refresh_attoseconds())
+ screen().set_raw(clock() * 2 / 3, 1024,0,735, 624/2,0,292); // RiscOS 3 default screen settings
+
+ if (!screen().has_screen_update())
+ screen().set_screen_update(*this, FUNC(arm_vidc20_device::screen_update));
+}
+
+u32 arm_vidc20_device::palette_entries() const
+{
+ return 0x100+4; // 8bpp + 2bpp for cursor
+}
+
+void arm_vidc20_device::device_start()
+{
+ acorn_vidc10_device::device_start();
+
+ save_item(NAME(m_vco_r_modulo));
+ save_item(NAME(m_vco_v_modulo));
+ save_item(NAME(m_pal_data_index));
+ save_item(NAME(m_dac_serial_mode));
+ save_item(NAME(m_pixel_source));
+ save_item(NAME(m_pixel_rate));
+}
+
+void arm_vidc20_device::device_reset()
+{
+ acorn_vidc10_device::device_reset();
+
+ // TODO: sensible defaults
+ m_vco_r_modulo = 1;
+ m_vco_v_modulo = 1;
+}
+
+void arm_vidc20_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ acorn_vidc10_device::device_timer(timer, id, param, ptr);
+}
+
+inline void arm_vidc20_device::update_8bpp_palette(u16 index, u32 paldata)
+{
+ int r,g,b;
+
+ // TODO: ext hookup, supremacy plus other stuff according to the manual
+// ext = (paldata & 0x0f000000) >> 24;
+ b = (paldata & 0x00ff0000) >> 16;
+ g = (paldata & 0x0000ff00) >> 8;
+ r = (paldata & 0x000000ff) >> 0;
+
+ set_pen_color(index, r, g, b );
+ screen().update_partial(screen().vpos());
+}
+
+WRITE32_MEMBER(arm_vidc20_device::vidc20_pal_data_display_w)
+{
+ u8 ext_data = offset & 0xf;
+ update_8bpp_palette(m_pal_data_index, (ext_data<<24) | data);
+ m_pal_data_index ++;
+ m_pal_data_index &= 0xff;
+}
+
+WRITE32_MEMBER( arm_vidc20_device::vidc20_pal_data_index_w )
+{
+ m_pal_data_index = data & 0xff;
+}
+
+WRITE32_MEMBER( arm_vidc20_device::vidc20_pal_data_cursor_w )
+{
+ u8 ext_data = offset & 0xf;
+ u8 cursor_pal_index = (offset >> 4) & 3;
+ update_8bpp_palette(m_pal_cursor_base + cursor_pal_index, (ext_data<<24) | data);
+}
+
+u32 arm_vidc20_device::get_pixel_clock()
+{
+ // RCLK source: passes thru a r-modulus and a phase frequency (PCOMP), the full story is interesting if you're into maths.
+ // TODO: for now we just multiply source clock by 2, enough for ssfindo.cpp games.
+ //printf("%d %02x %02x %d %d\n",this->clock(), 1 << m_pixel_rate, m_pixel_source, m_vco_v_modulo, m_vco_r_modulo);
+ if (m_pixel_source == 2) // RCLK
+ return (this->clock() << 1) >> m_pixel_rate;
+
+ // VCLK source is just an external connection
+ // TODO: get clock from outside world, understand how the modulos are really used,
+ // understand if SW do some VCO testing before setting CRTC params,
+ // if there isn't a monitor ID mechanism that copes with this
+ if (m_pixel_source == 0) // VCLK
+ return (25175000);
+
+ throw emu_fatalerror("%s unhandled pixel source %02x selected",this->tag(), m_pixel_source);
+}
+
+WRITE32_MEMBER(arm_vidc20_device::vidc20_crtc_w)
+{
+ if (offset & 0x8)
+ throw emu_fatalerror("%s accessing CRTC test register %02x, please call the ambulance",this->tag(),offset+0x80);
+
+ const u8 crtc_offset = (offset & 0x7) | ((offset & 0x10) >> 1);
+
+ switch(crtc_offset)
+ {
+ case CRTC_HCR: m_crtc_regs[CRTC_HCR] = (data&0x7ffc) + 8; break;
+ case CRTC_HSWR: m_crtc_regs[CRTC_HSWR] = (data&0x7ffe) + 8; break;
+ case CRTC_HBSR: m_crtc_regs[CRTC_HBSR] = (data&0x7ffe) + 12; break;
+ case CRTC_HDSR: m_crtc_regs[CRTC_HDSR] = (data&0x7ffe) + 18; break;
+ case CRTC_HDER: m_crtc_regs[CRTC_HDER] = (data&0x7ffe) + 18; break;
+ case CRTC_HBER: m_crtc_regs[CRTC_HBER] = (data&0x7ffe) + 12; break;
+ case CRTC_HCSR: m_crtc_regs[CRTC_HCSR] = (data&0x7fff) + 17; return;
+// case CRTC_HIR:
+ case CRTC_VCR: m_crtc_regs[CRTC_VCR] = (data&0x3fff) + 2; break;
+ case CRTC_VSWR: m_crtc_regs[CRTC_VSWR] = (data&0x3fff) + 1; break;
+ case CRTC_VBSR: m_crtc_regs[CRTC_VBSR] = (data&0x3fff) + 1; break;
+ case CRTC_VDSR: m_crtc_regs[CRTC_VDSR] = (data&0x3fff) + 1; break;
+ case CRTC_VDER:
+ m_crtc_regs[CRTC_VDER] = (data&0x3fff) + 1;
+ screen_vblank_line_update();
+ break;
+ case CRTC_VBER: m_crtc_regs[CRTC_VBER] = (data&0x3fff) + 1; break;
+ // TODO: bits 15-14 specific for duplex LCD mode
+ case CRTC_VCSR:
+ m_crtc_regs[CRTC_VCSR] = (data&0x3fff) + 1;
+ return;
+ case CRTC_VCER: m_crtc_regs[CRTC_VCER] = (data&0x3fff) + 1; return;
+ }
+
+ screen_dynamic_res_change();
+}
+
+WRITE32_MEMBER( arm_vidc20_device::fsynreg_w )
+{
+ m_vco_r_modulo = data & 0x3f;
+ m_vco_v_modulo = (data >> 8) & 0x3f;
+ // bits 15-14 and 7-6 are test bits
+
+ screen_dynamic_res_change();
+}
+
+WRITE32_MEMBER( arm_vidc20_device::vidc20_control_w )
+{
+ // ---- --00: VCLK
+ // ---- --01: HCLK
+ // ---- --10: RCLK ("recommended" 24 MHz)
+ // ---- --11: undefined, prolly same as RCLK
+ m_pixel_source = data & 3;
+ m_pixel_rate = (data & 0x1c) >> 2;
+ // (data & 0x700) >> 8 FIFO load
+ // BIT(data, 13) enables Duplex LCD mode
+ // BIT(data, 14) power down
+ // (data & 0xf0000) >> 16 test mode
+ m_bpp_mode = (data & 0xe0) >> 5;
+ m_crtc_interlace = BIT(data, 12);
+
+ screen_vblank_line_update();
+ screen_dynamic_res_change();
+}
+
+WRITE32_MEMBER( arm_vidc20_device::vidc20_sound_control_w )
+{
+ // TODO: VIDC10 mode, ext clock bit 0
+ m_dac_serial_mode = BIT(data, 1);
+}
+
+WRITE32_MEMBER( arm_vidc20_device::vidc20_sound_frequency_w )
+{
+ m_sound_frequency_latch = data & 0xff;
+ if (m_sound_mode == true)
+ refresh_sound_frequency();
+}
+
+void arm_vidc20_device::write_dac32(u8 channel, u16 data)
+{
+ m_dac[channel & 1]->write(data);
+}
+
+bool arm_vidc20_device::get_dac_mode()
+{
+ return m_dac_serial_mode;
+}
+
+u32 arm_vidc20_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
+{
+ // TODO: support for true color modes
+ return acorn_vidc10_device::screen_update(screen, bitmap, cliprect);
+}
+
+