diff options
-rw-r--r-- | scripts/src/machine.lua | 12 | ||||
-rw-r--r-- | src/devices/machine/acorn_vidc.cpp | 340 | ||||
-rw-r--r-- | src/devices/machine/acorn_vidc.h | 129 | ||||
-rw-r--r-- | src/devices/machine/arm_iomd.cpp | 767 | ||||
-rw-r--r-- | src/devices/machine/arm_iomd.h | 211 | ||||
-rw-r--r-- | src/mame/drivers/riscpc.cpp | 933 | ||||
-rw-r--r-- | src/mame/drivers/ssfindo.cpp | 521 |
7 files changed, 1607 insertions, 1306 deletions
diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index e9a58631883..7d1eaef0e7b 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -82,7 +82,6 @@ if (MACHINES["ACORN_VIDC"]~=null) then } end - --------------------------------------------------- -- --@src/devices/machine/akiko.h,MACHINES["AKIKO"] = true @@ -95,6 +94,17 @@ if (MACHINES["AKIKO"]~=null) then } end +-------------------------------------------------- +-- +--@src/devices/machine/arm_iomd.h,MACHINES["ARM_IOMD"] = true +-------------------------------------------------- + +if (MACHINES["ARM_IOMD"]~=null) then + files { + MAME_DIR .. "src/devices/machine/arm_iomd.cpp", + MAME_DIR .. "src/devices/machine/arm_iomd.h", + } +end --------------------------------------------------- -- 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); +} + + diff --git a/src/devices/machine/acorn_vidc.h b/src/devices/machine/acorn_vidc.h index 83a9bcbc000..bfa09402ea4 100644 --- a/src/devices/machine/acorn_vidc.h +++ b/src/devices/machine/acorn_vidc.h @@ -34,7 +34,7 @@ class acorn_vidc10_device : public device_t, { public: // construction/destruction - acorn_vidc10_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + acorn_vidc10_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); // I/O operations DECLARE_WRITE32_MEMBER( write ); @@ -42,36 +42,60 @@ public: auto vblank() { return m_vblank_cb.bind(); } auto sound_drq() { return m_sound_drq_cb.bind(); } // MEMC comms - void write_vram(uint32_t offset, uint8_t data) { m_data_vram[offset & (m_data_vram_mask)] = data; } - void write_cram(uint32_t offset, uint8_t data) { m_cursor_vram[offset & (m_cursor_vram_mask)] = data; } - void write_dac(uint8_t channel, uint8_t data); - void clear_dac(uint8_t channel) { m_dac[channel & 7]->write(0); } + void write_vram(u32 offset, u8 data) { m_data_vram[offset & (m_data_vram_mask)] = data; } + void write_cram(u32 offset, u8 data) { m_cursor_vram[offset & (m_cursor_vram_mask)] = data; } + void write_dac(u8 channel, u8 data); + void clear_dac(u8 channel) { m_dac[channel & 7]->write(0); } void update_sound_mode(bool state) { m_sound_mode = state; refresh_sound_frequency(); } void set_cursor_enable(bool state) { m_cursor_enable = state; } - uint32_t get_cursor_size() { return (m_crtc_regs[CRTC_VCER] - m_crtc_regs[CRTC_VCSR]) * (32/4); } - + u32 get_cursor_size() { return (m_crtc_regs[CRTC_VCER] - m_crtc_regs[CRTC_VCSR]) * (32/4); } + protected: acorn_vidc10_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock); // device-level overrides //virtual void device_validity_check(validity_checker &valid) const override; virtual void device_add_mconfig(machine_config &config) override; - virtual uint32_t palette_entries() const override; + virtual u32 palette_entries() const override; virtual void device_config_complete() override; virtual void device_start() override; virtual void device_reset() override; virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; virtual space_config_vector memory_space_config() const override; u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); + virtual u32 get_pixel_clock(); -private: - const address_space_config m_space_config; - + address_space_config m_space_config; void regs_map(address_map &map); + enum { + CRTC_HCR = 0, CRTC_HSWR, CRTC_HBSR, CRTC_HDSR, CRTC_HDER, CRTC_HBER, CRTC_HCSR, CRTC_HIR, + CRTC_VCR, CRTC_VSWR, CRTC_VBSR, CRTC_VDSR, CRTC_VDER, CRTC_VBER, CRTC_VCSR, CRTC_VCER + }; + u32 m_crtc_regs[16]; + enum { + TIMER_VIDEO = 1, + TIMER_SOUND = 2 + }; + + inline void screen_vblank_line_update(); + inline void screen_dynamic_res_change(); + void refresh_sound_frequency(); + + u16 m_pal_4bpp_base; + u16 m_pal_cursor_base; + u16 m_pal_border_base; + const double m_sound_internal_divider = 8.0; + + u8 m_bpp_mode, m_crtc_interlace; + u8 m_sound_frequency_latch; + bool m_sound_mode; + + required_device_array<dac_16bit_r2r_twos_complement_device, 8> m_dac; + +private: required_device<speaker_device> m_lspeaker; required_device<speaker_device> m_rspeaker; - required_device_array<dac_16bit_r2r_twos_complement_device, 8> m_dac; devcb_write_line m_vblank_cb; devcb_write_line m_sound_drq_cb; @@ -81,51 +105,38 @@ private: DECLARE_WRITE32_MEMBER( crtc_w ); DECLARE_WRITE32_MEMBER( sound_frequency_w ); DECLARE_WRITE32_MEMBER( control_w ); - - uint8_t m_pixel_clock, m_bpp_mode, m_crtc_interlace; + + u8 m_pixel_clock; + bool m_cursor_enable; //bool m_flyback; - enum { - TIMER_VIDEO = 1, - TIMER_SOUND = 2 - }; emu_timer *m_video_timer; emu_timer *m_sound_timer; - - inline void screen_vblank_line_update(); - void screen_dynamic_res_change(); - - enum { - CRTC_HCR = 0, CRTC_HSWR, CRTC_HBSR, CRTC_HDSR, CRTC_HDER, CRTC_HBER, CRTC_HCSR, CRTC_HIR, - CRTC_VCR, CRTC_VSWR, CRTC_VBSR, CRTC_VDSR, CRTC_VDER, CRTC_VBER, CRTC_VCSR, CRTC_VCER - }; - uint32_t m_crtc_regs[16]; - u8 *m_data_vram; - u8 *m_cursor_vram; + + std::unique_ptr<u8[]> m_data_vram; + std::unique_ptr<u8[]> m_cursor_vram; // TODO: correct data vram size const u32 m_data_vram_mask = 0x1fffff; const u32 m_cursor_vram_mask = 0x7fff; const u32 m_data_vram_size = m_data_vram_mask+1; const u32 m_cursor_vram_size = m_cursor_vram_mask+1; - bool m_cursor_enable; - void draw(bitmap_rgb32 &bitmap, const rectangle &cliprect, u8 *vram, uint8_t bpp, int xstart, int ystart, int xsize, int ysize, bool is_cursor); - inline void update_4bpp_palette(uint16_t index, uint32_t paldata); + void draw(bitmap_rgb32 &bitmap, const rectangle &cliprect, u8 *vram, u8 bpp, int xstart, int ystart, int xsize, int ysize, bool is_cursor); + inline void update_4bpp_palette(u16 index, u32 paldata); + u32 m_crtc_raw_horz[2]; + inline u32 convert_crtc_hdisplay(u8 index); - u8 m_sound_frequency_latch; - bool m_sound_frequency_test_bit; - bool m_sound_mode; + bool m_sound_frequency_test_bit; u8 m_stereo_image[8]; const float m_sound_input_gain = 0.05f; const int m_sound_max_channels = 8; int16_t m_ulaw_lookup[256]; - void refresh_sound_frequency(); - inline void refresh_stereo_image(uint8_t channel); + inline void refresh_stereo_image(u8 channel); }; class acorn_vidc10_lcd_device : public acorn_vidc10_device { public: // construction/destruction - acorn_vidc10_lcd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + acorn_vidc10_lcd_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); protected: virtual void device_add_mconfig(machine_config &config) override; }; @@ -134,6 +145,48 @@ protected: DECLARE_DEVICE_TYPE(ACORN_VIDC10, acorn_vidc10_device) DECLARE_DEVICE_TYPE(ACORN_VIDC10_LCD, acorn_vidc10_lcd_device) +class arm_vidc20_device : public acorn_vidc10_device +{ +public: + // construction/destruction + arm_vidc20_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + + void write_dac32(u8 channel, u16 data); + bool get_dac_mode(); + +protected: + virtual void device_add_mconfig(machine_config &config) override; + void regs_map(address_map &map); + virtual u32 palette_entries() const override; + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_config_complete() override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); + virtual u32 get_pixel_clock() override; + + const double m_sound_internal_divider = 1.0; + +private: + DECLARE_WRITE32_MEMBER( vidc20_pal_data_display_w ); + DECLARE_WRITE32_MEMBER( vidc20_pal_data_index_w ); + DECLARE_WRITE32_MEMBER( vidc20_pal_data_cursor_w ); + DECLARE_WRITE32_MEMBER( vidc20_crtc_w ); + DECLARE_WRITE32_MEMBER( vidc20_control_w ); + DECLARE_WRITE32_MEMBER( vidc20_sound_frequency_w ); + DECLARE_WRITE32_MEMBER( vidc20_sound_control_w ); + DECLARE_WRITE32_MEMBER( fsynreg_w ); + + u8 m_pal_data_index; + inline void update_8bpp_palette(u16 index, u32 paldata); + bool m_dac_serial_mode; + u8 m_pixel_source; + u8 m_pixel_rate; + u8 m_vco_r_modulo; + u8 m_vco_v_modulo; +}; + +DECLARE_DEVICE_TYPE(ARM_VIDC20, arm_vidc20_device) //************************************************************************** diff --git a/src/devices/machine/arm_iomd.cpp b/src/devices/machine/arm_iomd.cpp new file mode 100644 index 00000000000..7cfa8d488f6 --- /dev/null +++ b/src/devices/machine/arm_iomd.cpp @@ -0,0 +1,767 @@ +// license:BSD-3-Clause +// copyright-holders:Angelo Salese +/************************************************************************************************** + + ARM IOMD device emulation + + ARM7 SoC or stand-alone device, upgraded version(s) of the IOC found in Acorn Archimedes. + + TODO: + - IOCR / IOLINES hookups can be further improved, also DDR bits needs verifying; + - word-boundary accesses for 8-bit ports; + - split into different types, add quick notes about where they diverges do in this header; + - keyboard/mouse interface hookup is wrong for PS/2 and unimplemented for quadrature. + I guess we can use connectors over a custom handling, with a terminal mock for testing it + without the overhead of everything else. + +**************************************************************************************************/ + +#include "emu.h" +#include "arm_iomd.h" + + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +// device type definition +DEFINE_DEVICE_TYPE(ARM_IOMD, arm_iomd_device, "arm_iomd", "ARM IOMD controller") +// TODO: ssfindo.cpp actually uses a Cirrus Logic 7500FE, is it rebadged? +DEFINE_DEVICE_TYPE(ARM7500FE_IOMD, arm7500fe_iomd_device, "arm_7500FE_SoC", "ARM 7500FE SoC") + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// arm_iomd_device - constructor +//------------------------------------------------- + +void arm_iomd_device::base_map(address_map &map) +{ + // I/O + map(0x000, 0x003).rw(FUNC(arm_iomd_device::iocr_r), FUNC(arm_iomd_device::iocr_w)); + map(0x004, 0x007).rw(FUNC(arm_iomd_device::kbddat_r), FUNC(arm_iomd_device::kbddat_w)); + map(0x008, 0x00b).rw(FUNC(arm_iomd_device::kbdcr_r), FUNC(arm_iomd_device::kbdcr_w)); + + // interrupt A/B/fiq + master clock controls + map(0x010, 0x013).r(FUNC(arm_iomd_device::irqst_r<IRQA>)); + map(0x014, 0x017).rw(FUNC(arm_iomd_device::irqrq_r<IRQA>), FUNC(arm_iomd_device::irqrq_w<IRQA>)); + map(0x018, 0x01b).rw(FUNC(arm_iomd_device::irqmsk_r<IRQA>), FUNC(arm_iomd_device::irqmsk_w<IRQA>)); + + map(0x020, 0x023).r(FUNC(arm_iomd_device::irqst_r<IRQB>)); + map(0x024, 0x027).rw(FUNC(arm_iomd_device::irqrq_r<IRQB>), FUNC(arm_iomd_device::irqrq_w<IRQB>)); + map(0x028, 0x02b).rw(FUNC(arm_iomd_device::irqmsk_r<IRQB>), FUNC(arm_iomd_device::irqmsk_w<IRQB>)); + +// map(0x030, 0x033).r(FUNC(arm_iomd_device::fiqst_r)); +// map(0x034, 0x037).rw(FUNC(arm_iomd_device::fiqrq_r), FUNC(arm_iomd_device::fiqrq_w)); +// map(0x038, 0x03b).rw(FUNC(arm_iomd_device::fiqmsk_r), FUNC(arm_iomd_device::fiqmsk_w)); + + // timers + map(0x040, 0x043).rw(FUNC(arm_iomd_device::tNlow_r<0>), FUNC(arm_iomd_device::tNlow_w<0>)); + map(0x044, 0x047).rw(FUNC(arm_iomd_device::tNhigh_r<0>), FUNC(arm_iomd_device::tNhigh_w<0>)); + map(0x048, 0x04b).w(FUNC(arm_iomd_device::tNgo_w<0>)); + map(0x04c, 0x04f).w(FUNC(arm_iomd_device::tNlatch_w<0>)); + + map(0x050, 0x053).rw(FUNC(arm_iomd_device::tNlow_r<1>), FUNC(arm_iomd_device::tNlow_w<1>)); + map(0x054, 0x057).rw(FUNC(arm_iomd_device::tNhigh_r<1>), FUNC(arm_iomd_device::tNhigh_w<1>)); + map(0x058, 0x05b).w(FUNC(arm_iomd_device::tNgo_w<1>)); + map(0x05c, 0x05f).w(FUNC(arm_iomd_device::tNlatch_w<1>)); + // ROM control +// map(0x080, 0x083).rw(FUNC(arm_iomd_device::romcr_r<0>), FUNC(arm_iomd_device::romcr_w<0>)); +// map(0x084, 0x087).rw(FUNC(arm_iomd_device::romcr_r<1>), FUNC(arm_iomd_device::romcr_w<1>)); +// map(0x08c, 0x08f).rw(FUNC(arm_iomd_device::refcr_r), FUNC(arm_iomd_device::refcr_w)); + + // device identifiers + map(0x094, 0x097).r(FUNC(arm_iomd_device::id_r<0>)); + map(0x098, 0x09b).r(FUNC(arm_iomd_device::id_r<1>)); + map(0x09c, 0x09f).r(FUNC(arm_iomd_device::version_r)); + // mouse +// map(0x0a0, 0x0a3) // ... + + // I/O control +// map(0x0c4, 0x0c7).rw(FUNC(arm_iomd_device::iotcr_r), FUNC(arm_iomd_device::iotcr_w)); +// map(0x0c8, 0x0cb).rw(FUNC(arm_iomd_device::ectcr_r), FUNC(arm_iomd_device::ectcr_w)); + // sound DMA + // TODO: IOMD actually have a sd1* register, rework this + map(0x180, 0x183).rw(FUNC(arm_iomd_device::sdcur_r<0>), FUNC(arm_iomd_device::sdcur_w<0>)); + map(0x184, 0x187).rw(FUNC(arm_iomd_device::sdend_r<0>), FUNC(arm_iomd_device::sdend_w<0>)); + map(0x188, 0x18b).rw(FUNC(arm_iomd_device::sdcur_r<1>), FUNC(arm_iomd_device::sdcur_w<1>)); + map(0x18c, 0x18f).rw(FUNC(arm_iomd_device::sdend_r<1>), FUNC(arm_iomd_device::sdend_w<1>)); + map(0x190, 0x193).rw(FUNC(arm_iomd_device::sdcr_r), FUNC(arm_iomd_device::sdcr_w)); + map(0x194, 0x197).r(FUNC(arm_iomd_device::sdst_r)); + + // video DMA +// map(0x1c0, 0x1c3).rw(FUNC(arm_iomd_device::curscur_r), FUNC(arm_iomd_device::curscur_w)); + map(0x1c4, 0x1c7).rw(FUNC(arm_iomd_device::cursinit_r), FUNC(arm_iomd_device::cursinit_w)); + +// map(0x1d0, 0x1d3).rw(FUNC(arm_iomd_device::vidcura_r), FUNC(arm_iomd_device::vidcura_w)); + map(0x1d4, 0x1d7).rw(FUNC(arm_iomd_device::vidend_r), FUNC(arm_iomd_device::vidend_w)); +// map(0x1d8, 0x1db).rw(FUNC(arm_iomd_device::vidstart_r), FUNC(arm_iomd_device::vidstart_w)); + map(0x1dc, 0x1df).rw(FUNC(arm_iomd_device::vidinita_r), FUNC(arm_iomd_device::vidinita_w)); + map(0x1e0, 0x1e3).rw(FUNC(arm_iomd_device::vidcr_r), FUNC(arm_iomd_device::vidcr_w)); + + // interrupt DMA + map(0x1f0, 0x1f3).r(FUNC(arm_iomd_device::irqst_r<IRQDMA>)); + map(0x1f4, 0x1f7).rw(FUNC(arm_iomd_device::irqrq_r<IRQDMA>), FUNC(arm_iomd_device::irqrq_w<IRQDMA>)); + map(0x1f8, 0x1fb).rw(FUNC(arm_iomd_device::irqmsk_r<IRQDMA>), FUNC(arm_iomd_device::irqmsk_w<IRQDMA>)); + +// TODO: iomd2 has extra regs in 0x200-0x3ff area, others NOPs / mirrors? +} + +void arm_iomd_device::map(address_map &map) +{ + arm_iomd_device::base_map(map); +// map(0x088, 0x08b).rw(FUNC(arm_iomd_device::dramcr_r), FUNC(arm_iomd_device::dramcr_w)); + // VRAM control +// map(0x08c, 0x08f).rw(FUNC(arm_iomd_device::vrefcr_r), FUNC(arm_iomd_device::vrefcr_w)); + // flyback line size +// map(0x090, 0x093).rw(FUNC(arm_iomd_device::fsize_r), FUNC(arm_iomd_device::fsize_w)); + // quadrature mouse control +// map(0x0a0, 0x0a3).rw(FUNC(arm_iomd_device::mousex_r), FUNC(arm_iomd_device::mousex_w)); +// map(0x0a4, 0x0a7).rw(FUNC(arm_iomd_device::mousey_r), FUNC(arm_iomd_device::mousey_w)); + // DACK timing control +// map(0x0c0, 0x0c3).rw(FUNC(arm_iomd_device::dmatcr_r), FUNC(arm_iomd_device::dmatcr_w)); + // DMA external control +// map(0x0cc, 0x0cf).rw(FUNC(arm_iomd_device::dmaext_r), FUNC(arm_iomd_device::dmaext_w)); + // I/O DMA, similar structure as sound DMA +// map(0x100, 0x11f) ch 0 +// map(0x120, 0x13f) ch 1 +// map(0x140, 0x15f) ch 2 +// map(0x160, 0x17f) ch 3 + // sound DMA +// map(0x1a0, 0x1bf) ch 1 + +} + +arm_iomd_device::arm_iomd_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, type, tag, owner, clock) + , m_host_cpu(*this, finder_base::DUMMY_TAG) + , m_vidc(*this, finder_base::DUMMY_TAG) + , m_kbdc(*this, finder_base::DUMMY_TAG) + , m_iocr_read_od_cb{{*this}, {*this}} + , m_iocr_write_od_cb{{*this}, {*this}} + , m_iocr_read_id_cb(*this) + , m_iocr_write_id_cb(*this) +{ +} + +arm_iomd_device::arm_iomd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : arm_iomd_device(mconfig, ARM_IOMD, tag, owner, clock) +{ + m_id = 0xd4e7; + m_version = 0; +} + +void arm7500fe_iomd_device::map(address_map &map) +{ + arm_iomd_device::base_map(map); + + map(0x00c, 0x00f).rw(FUNC(arm7500fe_iomd_device::iolines_r), FUNC(arm7500fe_iomd_device::iolines_w)); + // master clock controls +// map(0x01c, 0x01f).rw(FUNC(arm7500fe_iomd_device::susmode_r), FUNC(arm7500fe_iomd_device::susmode_w)); +// map(0x02c, 0x02f).w(FUNC(arm7500fe_iomd_device::stopmode_w)); + map(0x03c, 0x03f).rw(FUNC(arm7500fe_iomd_device::clkctl_r), FUNC(arm7500fe_iomd_device::clkctl_w)); + // interrupt C/D + map(0x060, 0x063).r(FUNC(arm7500fe_iomd_device::irqst_r<IRQC>)); + map(0x064, 0x067).rw(FUNC(arm7500fe_iomd_device::irqrq_r<IRQC>), FUNC(arm7500fe_iomd_device::irqrq_w<IRQC>)); + map(0x068, 0x06b).rw(FUNC(arm7500fe_iomd_device::irqmsk_r<IRQC>), FUNC(arm7500fe_iomd_device::irqmsk_w<IRQC>)); +// map(0x06c, 0x06f).rw(FUNC(arm7500fe_iomd_device::vidmux_r), FUNC(arm7500fe_iomd_device::vidmux_w)); + map(0x070, 0x073).r(FUNC(arm7500fe_iomd_device::irqst_r<IRQD>)); + map(0x074, 0x077).rw(FUNC(arm7500fe_iomd_device::irqrq_r<IRQD>), FUNC(arm7500fe_iomd_device::irqrq_w<IRQD>)); + map(0x078, 0x07b).rw(FUNC(arm7500fe_iomd_device::irqmsk_r<IRQD>), FUNC(arm7500fe_iomd_device::irqmsk_w<IRQD>)); + + // PS/2 mouse +// map(0x0a8, 0x0ab).rw(FUNC(arm7500fe_iomd_device::msedat_r), FUNC(arm7500fe_iomd_device::msedat_w)); + map(0x0ac, 0x0af).rw(FUNC(arm7500fe_iomd_device::msecr_r), FUNC(arm7500fe_iomd_device::msecr_w)); + // I/O control +// map(0x0cc, 0x0cf).rw(FUNC(arm7500fe_iomd_device::astcr_r), FUNC(arm7500fe_iomd_device::astcr_w)); + // RAM control +// map(0x0d0, 0x0d3).rw(FUNC(arm7500fe_iomd_device::dramctl_r), FUNC(arm7500fe_iomd_device::dramctl_w)); +// map(0x0d4, 0x0d7).rw(FUNC(arm7500fe_iomd_device::selfref_r), FUNC(arm7500fe_iomd_device::selfref_w)); + // A/D converter +// map(0x0e0, 0x0e3).rw(FUNC(arm7500fe_iomd_device::atodicr_r), FUNC(arm7500fe_iomd_device::atodicr_w)); +// map(0x0e4, 0x0e7).r(FUNC(arm7500fe_iomd_device::atodsr_r)); +// map(0x0e8, 0x0eb).rw(FUNC(arm7500fe_iomd_device::atodcc_r), FUNC(arm7500fe_iomd_device::atodcc_w)); +// map(0x0ec, 0x0ef).r(FUNC(arm7500fe_iomd_device::atodcnt_r<0>)); +// map(0x0f0, 0x0f3).r(FUNC(arm7500fe_iomd_device::atodcnt_r<1>)); +// map(0x0f4, 0x0f7).r(FUNC(arm7500fe_iomd_device::atodcnt_r<2>)); +// map(0x0f8, 0x0fb).r(FUNC(arm7500fe_iomd_device::atodcnt_r<3>)); + // video DMA +// map(0x1c8, 0x1cb).rw(FUNC(arm7500fe_iomd_device::vidcurb_r), FUNC(arm7500fe_iomd_device::vidcurb_w)); +// map(0x1e8, 0x1eb).rw(FUNC(arm7500fe_iomd_device::vidinitb_r), FUNC(arm7500fe_iomd_device::vidinitb_w)); + +} + +arm7500fe_iomd_device::arm7500fe_iomd_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : arm_iomd_device(mconfig, ARM7500FE_IOMD, tag, owner, clock) + , m_iolines_read_cb(*this) + , m_iolines_write_cb(*this) +{ + m_id = 0xaa7c; + m_version = 0; +} + +// vanilla ARM7500 m_id = 0x5b98; m_version = 0; +// IOMD2 m_id = 0xd5e8; m_version = 1; + +//------------------------------------------------- +// device_add_mconfig - device-specific machine +// configuration addiitons +//------------------------------------------------- + +void arm_iomd_device::device_add_mconfig(machine_config &config) +{ + //DEVICE(config, ...); + //TODO: keyboard and mouse interfaces at very least, also they differs by device type +} + +void arm7500fe_iomd_device::device_add_mconfig(machine_config &config) +{ + //DEVICE(config, ...); + //TODO: above plus new sub-devices +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void arm_iomd_device::device_start() +{ + for (devcb_read_line &cb : m_iocr_read_od_cb) + cb.resolve_safe(1); + + for (devcb_write_line &cb : m_iocr_write_od_cb) + cb.resolve_safe(); + + m_iocr_read_id_cb.resolve_safe(1); + m_iocr_write_id_cb.resolve_safe(); + + save_item(NAME(m_iocr_ddr)); + save_item(NAME(m_video_enable)); + save_item(NAME(m_vidinita)); + save_item(NAME(m_vidend)); + save_item(NAME(m_vidlast)); + save_item(NAME(m_videqual)); + save_item(NAME(m_cursor_enable)); + save_item(NAME(m_cursinit)); + save_pointer(NAME(m_irq_mask), IRQ_SOURCES_SIZE); + save_pointer(NAME(m_irq_status), IRQ_SOURCES_SIZE); + + m_host_space = &m_host_cpu->space(AS_PROGRAM); + + m_timer[0] = timer_alloc(T0_TIMER); + m_timer[1] = timer_alloc(T1_TIMER); + save_pointer(NAME(m_timer_in), timer_ch_size); + save_pointer(NAME(m_timer_out), timer_ch_size); + save_pointer(NAME(m_timer_counter), timer_ch_size); + save_pointer(NAME(m_timer_readinc), timer_ch_size); + + save_item(NAME(m_sndcur)); + save_item(NAME(m_sndend)); + save_item(NAME(m_sound_dma_on)); + save_item(NAME(m_sndcur_buffer)); + save_item(NAME(m_snd_overrun)); + save_item(NAME(m_snd_int)); + + save_pointer(NAME(m_sndcur_reg), sounddma_ch_size); + save_pointer(NAME(m_sndend_reg), sounddma_ch_size); + save_pointer(NAME(m_sndstop_reg), sounddma_ch_size); + save_pointer(NAME(m_sndlast_reg), sounddma_ch_size); + save_pointer(NAME(m_sndbuffer_ok), sounddma_ch_size); + + // TODO: jumps to EASI space at $0c0016xx for RiscPC if POR is on? +} + +void arm7500fe_iomd_device::device_start() +{ + arm_iomd_device::device_start(); + + m_iolines_read_cb.resolve_safe(0xff); + m_iolines_write_cb.resolve_safe(); + save_item(NAME(m_iolines_ddr)); + + save_item(NAME(m_cpuclk_divider)); + save_item(NAME(m_memclk_divider)); + save_item(NAME(m_ioclk_divider)); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void arm_iomd_device::device_reset() +{ + int i; + m_iocr_ddr = 0x0b; + m_video_enable = false; + // TODO: defaults for these + m_vidinita = 0; + m_vidend = 0; + + for (i=0; i<IRQ_SOURCES_SIZE; i++) + { + m_irq_status[i] = 0; + m_irq_mask[i] = 0; + } + + for (i=0; i<timer_ch_size; i++) + m_timer[i]->adjust(attotime::never); + + m_sound_dma_on = false; + for (i=0; i<sounddma_ch_size; i++) + m_sndbuffer_ok[i] = false; + + // ... +} + +void arm7500fe_iomd_device::device_reset() +{ + arm_iomd_device::device_reset(); + + m_cpuclk_divider = m_ioclk_divider = m_memclk_divider = false; + refresh_host_cpu_clocks(); + + m_iolines_ddr = 0xff; +} + +void arm_iomd_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch(id) + { + case T0_TIMER: + case T1_TIMER: + trigger_irq<IRQA>(id == T1_TIMER ? 0x40 : 0x20); + break; + } +} + +//************************************************************************** +// READ/WRITE HANDLERS +//************************************************************************** + +// TODO: nINT1 +READ32_MEMBER( arm_iomd_device::iocr_r ) +{ + u8 res = 0; + + res = m_iocr_read_id_cb() << 3; + res|= m_iocr_read_od_cb[1]() << 1; + res|= m_iocr_read_od_cb[0]() << 0; + + return (m_vidc->flyback_r() << 7) | 0x34 | (res & m_iocr_ddr); +} + +WRITE32_MEMBER( arm_iomd_device::iocr_w ) +{ + m_iocr_ddr = (data & 0x0b); + m_iocr_write_id_cb(BIT(m_iocr_ddr,3)); + m_iocr_write_od_cb[1](BIT(m_iocr_ddr,1)); + m_iocr_write_od_cb[0](BIT(m_iocr_ddr,0)); +} + +READ32_MEMBER( arm_iomd_device::kbddat_r ) +{ + if (m_kbdc.found()) + return m_kbdc->data_r(); + + logerror("%s attempted to read kbddat with no controller\n", this->tag()); + return 0xff; +} + +READ32_MEMBER( arm_iomd_device::kbdcr_r ) +{ + if (m_kbdc.found()) + return m_kbdc->status_r(); + + logerror("%s attempted to read kbdcr with no controller\n", this->tag()); + return 0xff; +} + +WRITE32_MEMBER( arm_iomd_device::kbddat_w ) +{ + if (m_kbdc.found()) + { + m_kbdc->data_w(data & 0xff); + return; + } + + logerror("%s attempted to write %02x on kbddat with no controller\n", this->tag(),data & 0xff); +} + +WRITE32_MEMBER( arm_iomd_device::kbdcr_w ) +{ + if (m_kbdc.found()) + { + m_kbdc->command_w(data & 0xff); + return; + } + + logerror("%s attempted to write %02x on kbdcr with no controller\n", this->tag(),data & 0xff); +} + +READ32_MEMBER( arm7500fe_iomd_device::msecr_r ) +{ + // a7000p wants a TX empty otherwise it outright refuses to boot. + return 0x80; +} + +WRITE32_MEMBER( arm7500fe_iomd_device::msecr_w ) +{ + // ... +} + +READ32_MEMBER( arm7500fe_iomd_device::iolines_r ) +{ + return m_iolines_read_cb() & m_iolines_ddr; +} + +WRITE32_MEMBER( arm7500fe_iomd_device::iolines_w ) +{ + m_iolines_ddr = data; + m_iolines_write_cb(m_iolines_ddr); +} + +// irqA is special since it has a force (7) and ignore (1) bits +inline u8 arm_iomd_device::update_irqa_type(u8 data) +{ + return (data & 0xfd) | 0x80; +} + +// interrupts + +inline void arm_iomd_device::flush_irq(unsigned Which) +{ + if (m_irq_status[Which] & m_irq_mask[Which]) + m_host_cpu->pulse_input_line(ARM7_IRQ_LINE, m_host_cpu->minimum_quantum_time()); +} + +template <unsigned Which> inline void arm_iomd_device::trigger_irq(u8 irq_type) +{ + m_irq_status[Which] |= irq_type; + flush_irq(Which); +} + +template <unsigned Which> READ32_MEMBER( arm_iomd_device::irqst_r ) +{ + return m_irq_status[Which]; +} + +template <unsigned Which> READ32_MEMBER( arm_iomd_device::irqrq_r ) +{ + return m_irq_status[Which] & m_irq_mask[Which]; +} + +template <unsigned Which> READ32_MEMBER( arm_iomd_device::irqmsk_r ) +{ + return m_irq_mask[Which]; +} + +template <unsigned Which> WRITE32_MEMBER( arm_iomd_device::irqrq_w ) +{ + u8 res = m_irq_status[Which] & ~data; + if (Which == IRQA) + res = update_irqa_type(res); + m_irq_status[Which] = res; + flush_irq(Which); +} + +template <unsigned Which> WRITE32_MEMBER( arm_iomd_device::irqmsk_w ) +{ + m_irq_mask[Which] = data & 0xff; + flush_irq(Which); +} + +// master clock control +inline void arm7500fe_iomd_device::refresh_host_cpu_clocks() +{ + m_host_cpu->set_unscaled_clock(this->clock() >> (m_cpuclk_divider == false)); +} + +READ32_MEMBER( arm7500fe_iomd_device::clkctl_r ) +{ + return (m_cpuclk_divider << 2) | (m_memclk_divider << 1) | (m_ioclk_divider); +} + +WRITE32_MEMBER( arm7500fe_iomd_device::clkctl_w ) +{ + m_cpuclk_divider = BIT(data, 2); + m_memclk_divider = BIT(data, 1); + m_ioclk_divider = BIT(data, 0); + refresh_host_cpu_clocks(); +} + +// timers +inline void arm_iomd_device::trigger_timer(unsigned Which) +{ + int timer_count = m_timer_counter[Which]; + // TODO: it's actually a 2 MHz timer + int val = timer_count / 2; + + if(val==0) + m_timer[Which]->adjust(attotime::never); + else + m_timer[Which]->adjust(attotime::from_usec(val), 0, attotime::from_usec(val)); +} + +// TODO: live updates aren't really supported here +template <unsigned Which> READ32_MEMBER( arm_iomd_device::tNlow_r ) +{ + return m_timer_out[Which] & 0xff; +} + +template <unsigned Which> READ32_MEMBER( arm_iomd_device::tNhigh_r ) +{ + return (m_timer_out[Which] >> 8) & 0xff; +} + +template <unsigned Which> WRITE32_MEMBER( arm_iomd_device::tNlow_w ) +{ + m_timer_in[Which] = (m_timer_in[Which] & 0xff00) | (data & 0xff); +} + +template <unsigned Which> WRITE32_MEMBER( arm_iomd_device::tNhigh_w ) +{ + m_timer_in[Which] = (m_timer_in[Which] & 0x00ff) | ((data & 0xff) << 8); +} + +template <unsigned Which> WRITE32_MEMBER( arm_iomd_device::tNgo_w ) +{ + m_timer_counter[Which] = m_timer_in[Which]; + trigger_timer(Which); +} + +template <unsigned Which> WRITE32_MEMBER( arm_iomd_device::tNlatch_w ) +{ + m_timer_readinc[Which] ^=1; + m_timer_out[Which] = m_timer_counter[Which]; + if(m_timer_readinc[Which]) + { + m_timer_counter[Which]--; + if(m_timer_counter[Which] < 0) + m_timer_counter[Which] += m_timer_in[Which]; + } +} + +// device identifiers +template <unsigned Nibble> READ32_MEMBER( arm_iomd_device::id_r ) +{ + return (m_id >> (Nibble*8)) & 0xff; +} + +READ32_MEMBER( arm_iomd_device::version_r ) +{ + return m_version; +} + +DECLARE_READ32_MEMBER( version_r ); + +// sound DMA + +template <unsigned Which> READ32_MEMBER( arm_iomd_device::sdcur_r ) { return m_sndcur_reg[Which]; } +template <unsigned Which> WRITE32_MEMBER( arm_iomd_device::sdcur_w ) { COMBINE_DATA(&m_sndcur_reg[Which]); } +template <unsigned Which> READ32_MEMBER( arm_iomd_device::sdend_r ) +{ + return (m_sndstop_reg[Which] << 31) | (m_sndlast_reg[Which] << 30) | (m_sndend_reg[Which] & 0x00fffff0); +} + +template <unsigned Which> WRITE32_MEMBER( arm_iomd_device::sdend_w ) +{ + COMBINE_DATA(&m_sndend_reg[Which]); + m_sndend_reg[Which] &= 0x00fffff0; + m_sndstop_reg[Which] = BIT(data, 31); + m_sndlast_reg[Which] = BIT(data, 30); +} + +READ32_MEMBER( arm_iomd_device::sdcr_r ) +{ + return (m_sound_dma_on << 5) | dmaid_size; +} + +WRITE32_MEMBER( arm_iomd_device::sdcr_w ) +{ + m_sound_dma_on = BIT(data, 5); + + m_vidc->update_sound_mode(m_sound_dma_on); + if (m_sound_dma_on) + { + m_sndcur_buffer = 0; + sounddma_swap_buffer(); + } + else + { + // ... + } + + // TODO: bit 7 resets sound DMA +} + +READ32_MEMBER( arm_iomd_device::sdst_r ) +{ + return (m_snd_overrun << 2) | (m_snd_int << 1) | m_sndcur_buffer; +} + +// video DMA +READ32_MEMBER( arm_iomd_device::cursinit_r ) +{ + return m_cursinit; +} + +WRITE32_MEMBER( arm_iomd_device::cursinit_w ) +{ + COMBINE_DATA(&m_cursinit); + m_cursinit &= 0x1ffffff0; + m_cursor_enable = true; + m_vidc->set_cursor_enable(m_cursor_enable); +} + +READ32_MEMBER( arm_iomd_device::vidcr_r ) +{ + // bit 6: DRAM mode + // bits 4-0: qword transfer + return 0x40 | (m_video_enable << 5) | dmaid_size; +} + +WRITE32_MEMBER( arm_iomd_device::vidcr_w ) +{ + m_video_enable = BIT(data, 5); + if (m_video_enable == false) + { + m_cursor_enable = false; + m_vidc->set_cursor_enable(m_cursor_enable); + } + + if (data & 0x80) + throw emu_fatalerror("%s VIDCR LCD dual panel mode enabled", this->tag()); +} + +READ32_MEMBER( arm_iomd_device::vidend_r ) +{ + return (m_vidend & 0x00fffff0); +} + +WRITE32_MEMBER( arm_iomd_device::vidend_w ) +{ + COMBINE_DATA(&m_vidend); + m_vidend &= 0x00fffff0; +} + +READ32_MEMBER( arm_iomd_device::vidinita_r ) +{ + return (m_vidlast << 30) | (m_videqual << 29) | (m_vidinita & 0x1ffffff0); +} + +WRITE32_MEMBER( arm_iomd_device::vidinita_w ) +{ + COMBINE_DATA(&m_vidinita); + m_vidinita &= 0x1ffffff0; + m_vidlast = BIT(data, 30); + m_videqual = BIT(data, 29); +} + + +//************************************************************************** +// IRQ/DRQ/Reset signals +//************************************************************************** + +WRITE_LINE_MEMBER( arm_iomd_device::vblank_irq ) +{ + if (!state) + return; + + trigger_irq<IRQA>(0x08); + if (m_video_enable == true) + { + // TODO: much more complex, last/end regs, start regs and eventually LCD hooks + u32 src = m_vidinita; + u32 size = m_vidend; + + // TODO: vidcur can be readback, support it once anything makes use of the 0x1d0 reg for obvious reasons + // (and using m_ prefix is intentional too) + for (u32 m_vidcur = 0; m_vidcur<size; m_vidcur++) + { + m_vidc->write_vram(m_vidcur, m_host_space->read_byte(src)); + src++; + src &= 0x1fffffff; + } + + if (m_cursor_enable == true) + { + src = m_cursinit; + size = m_vidc->get_cursor_size(); + + // TODO: same as above + for (u32 m_curscur = 0; m_curscur<size; m_curscur++) + { + m_vidc->write_cram(m_curscur, m_host_space->read_byte(src)); + src++; + src &= 0x1fffffff; + } + } + } +} + +inline void arm_iomd_device::sounddma_swap_buffer() +{ + m_sndcur = m_sndcur_reg[m_sndcur_buffer]; + m_sndend = m_sndcur + (m_sndend_reg[m_sndcur_buffer] + 0x10); + m_sndbuffer_ok[m_sndcur_buffer] = true; + + // TODO: actual condition for int + m_snd_overrun = false; + m_snd_int = false; +} + +WRITE_LINE_MEMBER( arm_iomd_device::sound_drq ) +{ + if (!state) + return; + + if (m_vidc->get_dac_mode() == true) + { + for (int ch=0;ch<2;ch++) + m_vidc->write_dac32(ch, (m_host_space->read_word(m_sndcur + ch*2))); + + m_sndcur += 4; + + if (m_sndcur >= m_sndend) + { + // TODO: interrupt bit + + m_vidc->update_sound_mode(m_sound_dma_on); + if (m_sound_dma_on) + { + m_sndbuffer_ok[m_sndcur_buffer] = false; + m_sndcur_buffer ^= 1; + m_snd_overrun = (m_sndbuffer_ok[m_sndcur_buffer] == false); + sounddma_swap_buffer(); + } + else + { + // ... + } + } + } + else + { + // ... + } +} + +WRITE_LINE_MEMBER( arm_iomd_device::keyboard_irq ) +{ + printf("IRQ %d\n",state); + if (!state) + return; + + trigger_irq<IRQB>(0x80); +} + +WRITE_LINE_MEMBER( arm_iomd_device::keyboard_reset ) +{ + printf("RST %d\n",state); +} + + diff --git a/src/devices/machine/arm_iomd.h b/src/devices/machine/arm_iomd.h new file mode 100644 index 00000000000..c996a88b3dd --- /dev/null +++ b/src/devices/machine/arm_iomd.h @@ -0,0 +1,211 @@ +// license:BSD-3-Clause +// copyright-holders:Angelo Salese +/*************************************************************************** + + ARM IOMD device emulation + +***************************************************************************/ + +#ifndef MAME_MACHINE_ARM_IOMD_H +#define MAME_MACHINE_ARM_IOMD_H + +#pragma once + +#include "cpu/arm7/arm7.h" +#include "cpu/arm7/arm7core.h" +#include "machine/acorn_vidc.h" +#include "machine/at_keybc.h" +#include "bus/pc_kbd/pc_kbdc.h" +#include "bus/pc_kbd/keyboards.h" + +//************************************************************************** +// INTERFACE CONFIGURATION MACROS +//************************************************************************** + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> arm_iomd_device + +class arm_iomd_device : public device_t +{ +public: + // construction/destruction + arm_iomd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + arm_iomd_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + + template <unsigned N> auto iocr_read_od() { return m_iocr_read_od_cb[N].bind(); } + template <unsigned N> auto iocr_write_od() { return m_iocr_write_od_cb[N].bind(); } + auto iocr_read_id() { return m_iocr_read_id_cb.bind(); } + auto iocr_write_id() { return m_iocr_write_id_cb.bind(); } + // IRQA + DECLARE_WRITE_LINE_MEMBER( vblank_irq ); + // IRQB + DECLARE_WRITE_LINE_MEMBER( keyboard_irq ); + // DRQs + DECLARE_WRITE_LINE_MEMBER( sound_drq ); + // Reset + DECLARE_WRITE_LINE_MEMBER( keyboard_reset ); + + // I/O operations + virtual void map(address_map &map); + template<class T> void set_host_cpu_tag(T &&tag) { m_host_cpu.set_tag(std::forward<T>(tag)); } + template<class T> void set_vidc_tag(T &&tag) { m_vidc.set_tag(std::forward<T>(tag)); } + template<class T> void set_kbdc_tag(T &&tag) { m_kbdc.set_tag(std::forward<T>(tag)); } + +protected: + // device-level overrides + //virtual void device_validity_check(validity_checker &valid) const override; + virtual void device_add_mconfig(machine_config &config) override; + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + + void base_map(address_map &map); + u16 m_id; + u8 m_version; + + enum { + IRQA = 0, + IRQB, + IRQC, + IRQD, + IRQDMA, + IRQ_SOURCES_SIZE + }; + template <unsigned Which> DECLARE_READ32_MEMBER( irqst_r ); + template <unsigned Which> DECLARE_READ32_MEMBER( irqrq_r ); + template <unsigned Which> DECLARE_WRITE32_MEMBER( irqrq_w ); + template <unsigned Which> DECLARE_READ32_MEMBER( irqmsk_r ); + template <unsigned Which> DECLARE_WRITE32_MEMBER( irqmsk_w ); + + // TODO: convert to ARM7 device instead, enums shouldn't be public + required_device<cpu_device> m_host_cpu; + required_device<arm_vidc20_device> m_vidc; + optional_device<ps2_keyboard_controller_device> m_kbdc; + address_space *m_host_space; /**< reference to the host cpu space for DMA ops */ +private: + u8 m_iocr_ddr; + + devcb_read_line m_iocr_read_od_cb[2]; + devcb_write_line m_iocr_write_od_cb[2]; + devcb_read_line m_iocr_read_id_cb; + devcb_write_line m_iocr_write_id_cb; + + DECLARE_READ32_MEMBER( iocr_r ); + DECLARE_WRITE32_MEMBER( iocr_w ); + + DECLARE_READ32_MEMBER( kbddat_r ); + DECLARE_WRITE32_MEMBER( kbddat_w ); + DECLARE_READ32_MEMBER( kbdcr_r ); + DECLARE_WRITE32_MEMBER( kbdcr_w ); + + u32 m_vidinita, m_vidend; + bool m_vidlast, m_videqual; + bool m_video_enable; + DECLARE_READ32_MEMBER( vidcr_r ); + DECLARE_WRITE32_MEMBER( vidcr_w ); + DECLARE_READ32_MEMBER( vidend_r ); + DECLARE_WRITE32_MEMBER( vidend_w ); + DECLARE_READ32_MEMBER( vidinita_r ); + DECLARE_WRITE32_MEMBER( vidinita_w ); + u32 m_cursinit; + bool m_cursor_enable; + DECLARE_READ32_MEMBER( cursinit_r ); + DECLARE_WRITE32_MEMBER( cursinit_w ); + + static constexpr int sounddma_ch_size = 2; + u32 m_sndcur, m_sndend; + u32 m_sndcur_reg[sounddma_ch_size], m_sndend_reg[sounddma_ch_size]; + bool m_sndstop_reg[sounddma_ch_size], m_sndlast_reg[sounddma_ch_size]; + bool m_sndbuffer_ok[sounddma_ch_size]; + bool m_sound_dma_on; + u8 m_sndcur_buffer; + bool m_snd_overrun, m_snd_int; + inline void sounddma_swap_buffer(); + template <unsigned Which> DECLARE_READ32_MEMBER( sdcur_r ); + template <unsigned Which> DECLARE_WRITE32_MEMBER( sdcur_w ); + template <unsigned Which> DECLARE_READ32_MEMBER( sdend_r ); + template <unsigned Which> DECLARE_WRITE32_MEMBER( sdend_w ); + DECLARE_READ32_MEMBER( sdcr_r ); + DECLARE_WRITE32_MEMBER( sdcr_w ); + DECLARE_READ32_MEMBER( sdst_r ); + + u8 m_irq_status[IRQ_SOURCES_SIZE], m_irq_mask[IRQ_SOURCES_SIZE]; + inline u8 update_irqa_type(u8 data); + inline void flush_irq(unsigned Which); + template <unsigned Which> inline void trigger_irq(u8 irq_type); + + static constexpr int timer_ch_size = 2; + enum { + T0_TIMER = 1, + T1_TIMER + }; + inline void trigger_timer(unsigned Which); + u16 m_timer_in[2]; + u16 m_timer_out[2]; + int m_timer_counter[2]; + u8 m_timer_readinc[2]; + emu_timer *m_timer[2]; + + template <unsigned Which> DECLARE_READ32_MEMBER( tNlow_r ); + template <unsigned Which> DECLARE_READ32_MEMBER( tNhigh_r ); + template <unsigned Which> DECLARE_WRITE32_MEMBER( tNlow_w ); + template <unsigned Which> DECLARE_WRITE32_MEMBER( tNhigh_w ); + template <unsigned Which> DECLARE_WRITE32_MEMBER( tNgo_w ); + template <unsigned Which> DECLARE_WRITE32_MEMBER( tNlatch_w ); + + template <unsigned Nibble> DECLARE_READ32_MEMBER( id_r ); + DECLARE_READ32_MEMBER( version_r ); + + // used in vidcr_r / sndcr_r, documentation hints this is a purged idea during chip development, to be checked out + static constexpr u8 dmaid_size = 0x10; // qword transfer +// constexpr u8 dmaid_mask = 0x1f; +}; + +class arm7500fe_iomd_device : public arm_iomd_device +{ +public: + // construction/destruction + arm7500fe_iomd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + virtual void map(address_map &map) override; + auto iolines_read() { return m_iolines_read_cb.bind(); } + auto iolines_write() { return m_iolines_write_cb.bind(); } + +protected: + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_add_mconfig(machine_config &config) override; +private: + devcb_read8 m_iolines_read_cb; + devcb_write8 m_iolines_write_cb; + + bool m_cpuclk_divider, m_memclk_divider, m_ioclk_divider; + inline void refresh_host_cpu_clocks(); + DECLARE_READ32_MEMBER( clkctl_r ); + DECLARE_WRITE32_MEMBER( clkctl_w ); + + u8 m_iolines_ddr; + DECLARE_READ32_MEMBER( iolines_r ); + DECLARE_WRITE32_MEMBER( iolines_w ); + + DECLARE_READ32_MEMBER( msecr_r ); + DECLARE_WRITE32_MEMBER( msecr_w ); +}; + +// device type definition +DECLARE_DEVICE_TYPE(ARM_IOMD, arm_iomd_device) +DECLARE_DEVICE_TYPE(ARM7500FE_IOMD, arm7500fe_iomd_device) + + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + + +#endif // MAME_MACHINE_ARM_IOMD_H diff --git a/src/mame/drivers/riscpc.cpp b/src/mame/drivers/riscpc.cpp index a5fb926105d..5208fec8e41 100644 --- a/src/mame/drivers/riscpc.cpp +++ b/src/mame/drivers/riscpc.cpp @@ -2,28 +2,25 @@ // copyright-holders:Angelo Salese /*************************************************************************** - Acorn Archimedes 7000/7000+ + Acorn RiscPC line of computers - very preliminary driver by Angelo Salese, + preliminary driver by Angelo Salese, based on work by Tomasz Slanina and Sarah Walker TODO: - - Move device implementations into specific files; - - PS/2 keyboard doesn't work properly; - - i2c device should actually be a pcf8583 RTC with i2c as slave - (and indeed CMOS settings doesn't work); - - Fix pendingUnd fatalerror from ARM7 core - - Fix per-machine configurations; - - ??? - bp (0382827C) (second trigger) - do R13 = SR13 + - IOMD currently hardwired with ARM7500FE flavour for all machines, needs information about + which uses what; + - PS/2 keyboard doesn't work properly; + - Fix pendingUnd fatalerror from ARM7 core; + - Fix pendingAbtD fatalerror for RiscOS 4.xx; ****************************************************************************/ #include "emu.h" #include "cpu/arm7/arm7.h" #include "cpu/arm7/arm7core.h" +#include "machine/acorn_vidc.h" +#include "machine/arm_iomd.h" #include "machine/i2cmem.h" #include "machine/at_keybc.h" #include "bus/pc_kbd/pc_kbdc.h" @@ -37,12 +34,13 @@ class riscpc_state : public driver_device { public: riscpc_state(const machine_config &mconfig, device_type type, const char *tag) - : driver_device(mconfig, type, tag), - m_maincpu(*this, "maincpu"), - m_screen(*this, "screen"), - m_palette(*this, "palette"), - m_i2cmem(*this, "i2cmem"), - m_kbdc(*this, "kbdc") + : driver_device(mconfig, type, tag) + , m_maincpu(*this, "maincpu") + , m_vidc(*this, "vidc") + , m_iomd(*this, "iomd") + , m_screen(*this, "screen") + , m_i2cmem(*this, "i2cmem") + , m_kbdc(*this, "kbdc") { } void rpc700(machine_config &config); @@ -56,791 +54,47 @@ private: void base_config(machine_config &config); required_device<cpu_device> m_maincpu; + required_device<arm_vidc20_device> m_vidc; + required_device<arm7500fe_iomd_device> m_iomd; required_device<screen_device> m_screen; - required_device<palette_device> m_palette; required_device<i2cmem_device> m_i2cmem; required_device<ps2_keyboard_controller_device> m_kbdc; - std::unique_ptr<uint32_t[]> m_vram; - - DECLARE_READ32_MEMBER(a7000_iomd_r); - DECLARE_WRITE32_MEMBER(a7000_iomd_w); - DECLARE_WRITE32_MEMBER(a7000_vidc20_w); - DECLARE_WRITE_LINE_MEMBER(keyboard_reset); - DECLARE_WRITE_LINE_MEMBER(keyboard_interrupt); - - uint8_t m_vidc20_pal_index; - uint16_t m_vidc20_horz_reg[0x10]; - uint16_t m_vidc20_vert_reg[0x10]; - uint8_t m_vidc20_bpp_mode; - emu_timer *m_flyback_timer; - uint16_t m_timer_in[2]; - uint16_t m_timer_out[2]; - int m_timer_counter[2]; - emu_timer *m_IOMD_timer[2]; - uint8_t m_IRQ_status_A, m_IRQ_status_B; - uint8_t m_IRQ_mask_A, m_IRQ_mask_B; - uint8_t m_IOMD_IO_ctrl; - uint8_t m_IOMD_keyb_ctrl; - uint16_t m_io_id; - uint8_t m_viddma_status; - uint32_t m_viddma_addr_init; - uint32_t m_viddma_addr_start; - uint32_t m_viddma_addr_end; - uint8_t m_t0readinc; - uint8_t m_t1readinc; uint8_t m_i2c_clk; - void fire_iomd_timer(int timer); - void viddma_transfer_start(); - void vidc20_dynamic_screen_change(); - virtual void video_start() override; + virtual void machine_reset() override; virtual void machine_start() override; - uint32_t screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); - TIMER_CALLBACK_MEMBER(IOMD_timer0_callback); - TIMER_CALLBACK_MEMBER(IOMD_timer1_callback); - TIMER_CALLBACK_MEMBER(flyback_timer_callback); - TIMER_CALLBACK_MEMBER(videodma_timer_callback); - void a7000_map(address_map &map); void riscpc_map(address_map &map); - void update_irq(); -}; - - -/* - * - * VIDC20 chip emulation - * - */ - -void riscpc_state::video_start() -{ - const uint32_t vram_size = 0x1000000/4; - m_vram = std::make_unique<uint32_t[]>(vram_size); - save_pointer(NAME(m_vram), vram_size); -} - - -static const char *const vidc20_regnames[] = -{ - "Video Palette", // 0 - "Video Palette Address", // 1 - "RESERVED", // 2 - "LCD offset", // 3 - "Border Colour", // 4 - "Cursor Palette Logical Colour 1", // 5 - "Cursor Palette Logical Colour 2", // 6 - "Cursor Palette Logical Colour 3", // 7 - "Horizontal", // 8 - "Vertical", // 9 - "Stereo Image", // A - "Sound", // B - "External", // C - "Frequency Synthesis", // D - "Control", // E - "Data Control" // F -}; - -#if 0 -static const char *const vidc20_horz_regnames[] = -{ - "Horizontal Cycle", // 0x80 HCR - "Horizontal Sync Width", // 0x81 HSWR - "Horizontal Border Start", // 0x82 HBSR - "Horizontal Display Start", // 0x83 HDSR - "Horizontal Display End", // 0x84 HDER - "Horizontal Border End", // 0x85 HBER - "Horizontal Cursor Start", // 0x86 HCSR - "Horizontal Interlace", // 0x87 HIR - "Horizontal Counter TEST", // 0x88 - "Horizontal <UNDEFINED>", // 0x89 - "Horizontal <UNDEFINED>", // 0x8a - "Horizontal <UNDEFINED>", // 0x8b - "Horizontal All TEST", // 0x8c - "Horizontal <UNDEFINED>", // 0x8d - "Horizontal <UNDEFINED>", // 0x8e - "Horizontal <UNDEFINED>" // 0x8f -}; -#endif - -#define HCR 0 -#define HSWR 1 -#define HBSR 2 -#define HDSR 3 -#define HDER 4 -#define HBER 5 -#define HCSR 6 -#define HIR 7 - -#if 0 -static const char *const vidc20_vert_regnames[] = -{ - "Vertical Cycle", // 0x90 VCR - "Vertical Sync Width", // 0x91 VSWR - "Vertical Border Start", // 0x92 VBSR - "Vertical Display Start", // 0x93 VDSR - "Vertical Display End", // 0x94 VDER - "Vertical Border End", // 0x95 VBER - "Vertical Cursor Start", // 0x96 VCSR - "Vertical Cursor End", // 0x97 VCER - "Vertical Counter TEST", // 0x98 - "Horizontal <UNDEFINED>", // 0x99 - "Vertical Counter Increment TEST", // 0x9a - "Horizontal <UNDEFINED>", // 0x9b - "Vertical All TEST", // 0x9c - "Horizontal <UNDEFINED>", // 0x9d - "Horizontal <UNDEFINED>", // 0x9e - "Horizontal <UNDEFINED>" // 0x9f -}; -#endif - -#define VCR 0 -#define VSWR 1 -#define VBSR 2 -#define VDSR 3 -#define VDER 4 -#define VBER 5 -#define VCSR 6 -#define VCER 7 - -void riscpc_state::vidc20_dynamic_screen_change() -{ - /* sanity checks - first pass */ - /* - total cycles + border start/end - */ - if(m_vidc20_horz_reg[HCR] && m_vidc20_horz_reg[HBSR] && m_vidc20_horz_reg[HBER] && - m_vidc20_vert_reg[VCR] && m_vidc20_vert_reg[VBSR] && m_vidc20_vert_reg[VBER]) - { - /* sanity checks - second pass */ - /* - total cycles > border end > border start - */ - if((m_vidc20_horz_reg[HCR] > m_vidc20_horz_reg[HBER]) && - (m_vidc20_horz_reg[HBER] > m_vidc20_horz_reg[HBSR]) && - (m_vidc20_vert_reg[VCR] > m_vidc20_vert_reg[VBER]) && - (m_vidc20_vert_reg[VBER] > m_vidc20_vert_reg[VBSR])) - { - /* finally ready to change the resolution */ - int hblank_period,vblank_period; - rectangle visarea = m_screen->visible_area(); - hblank_period = (m_vidc20_horz_reg[HCR] & 0x3ffc); - vblank_period = (m_vidc20_vert_reg[VCR] & 0x3fff); - /* note that we use the border registers as the visible area */ - visarea.set( - (m_vidc20_horz_reg[HBSR] & 0x3ffe), - (m_vidc20_horz_reg[HBER] & 0x3ffe) - 1, - (m_vidc20_vert_reg[VBSR] & 0x1fff), - (m_vidc20_vert_reg[VBER] & 0x1fff) - 1); - - m_screen->configure(hblank_period, vblank_period, visarea, m_screen->frame_period().attoseconds() ); - logerror("VIDC20: successfully changed the screen to:\n Display Size = %d x %d\n Border Size %d x %d\n Cycle Period %d x %d\n", - (m_vidc20_horz_reg[HDER]-m_vidc20_horz_reg[HDSR]),(m_vidc20_vert_reg[VDER]-m_vidc20_vert_reg[VDSR]), - (m_vidc20_horz_reg[HBER]-m_vidc20_horz_reg[HBSR]),(m_vidc20_vert_reg[VBER]-m_vidc20_vert_reg[VBSR]), - hblank_period,vblank_period); - } - } -} - -WRITE32_MEMBER( riscpc_state::a7000_vidc20_w ) -{ - int r,g,b,cursor_index,horz_reg,vert_reg,reg = data >> 28; - - switch(reg) - { - case 0: // Video Palette - r = (data & 0x0000ff) >> 0; - g = (data & 0x00ff00) >> 8; - b = (data & 0xff0000) >> 16; - - m_palette->set_pen_color(m_vidc20_pal_index & 0xff,r,g,b); - - /* auto-increment & wrap-around */ - m_vidc20_pal_index++; - m_vidc20_pal_index &= 0xff; - break; - - case 1: // Video Palette Address - - // according to RPCEmu, these mustn't be set - if (data & 0x0fffff00) - return; - - m_vidc20_pal_index = data & 0xff; - break; - - case 4: // Border Color - r = (data & 0x0000ff) >> 0; - g = (data & 0x00ff00) >> 8; - b = (data & 0xff0000) >> 16; - - m_palette->set_pen_color(0x100,r,g,b); - break; - case 5: // Cursor Palette Logical Colour n - case 6: - case 7: - cursor_index = 0x100 + reg - 4; // 0x101,0x102 and 0x103 (plus 0x100 of above, 2bpp) - - r = (data & 0x0000ff) >> 0; - g = (data & 0x00ff00) >> 8; - b = (data & 0xff0000) >> 16; - - m_palette->set_pen_color(cursor_index,r,g,b); - break; - case 8: // Horizontal - horz_reg = (data >> 24) & 0xf; - m_vidc20_horz_reg[horz_reg] = data & 0x3fff; - if(horz_reg == 0 || horz_reg == 2 || horz_reg == 5) - vidc20_dynamic_screen_change(); - - // logerror("VIDC20: %s Register write = %08x (%d)\n",vidc20_horz_regnames[horz_reg],val,val); - break; - case 9: // Vertical - vert_reg = (data >> 24) & 0xf; - m_vidc20_vert_reg[vert_reg] = data & 0x1fff; - if(vert_reg == 0 || vert_reg == 2 || vert_reg == 5) - vidc20_dynamic_screen_change(); - - if(vert_reg == 4) - { - if(m_vidc20_vert_reg[VDER] != 0) - m_flyback_timer->adjust(m_screen->time_until_pos(m_vidc20_vert_reg[VDER])); - else - m_flyback_timer->adjust(attotime::never); - } - - // logerror("VIDC20: %s Register write = %08x (%d)\n",vidc20_vert_regnames[vert_reg],val,val); - - break; - case 0x0e: // Control - m_vidc20_bpp_mode = (data & 0xe0) >> 5; - break; - default: logerror("VIDC20: %s Register write = %08x\n",vidc20_regnames[reg],data & 0xfffffff); - } -} - -uint32_t riscpc_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) -{ - int x_size,y_size,x_start,y_start; - int x,y,xi; - uint32_t count; - - bitmap.fill(m_palette->pen(0x100), cliprect); - - x_size = (m_vidc20_horz_reg[HDER]-m_vidc20_horz_reg[HDSR]); - y_size = (m_vidc20_vert_reg[VDER]-m_vidc20_vert_reg[VDSR]); - x_start = m_vidc20_horz_reg[HDSR]; - y_start = m_vidc20_vert_reg[VDSR]; - - /* check if display is enabled */ - if(x_size <= 0 || y_size <= 0) - return 0; - -// popmessage("%d",m_vidc20_bpp_mode); - - count = 0; - - switch(m_vidc20_bpp_mode) - { - case 0: /* 1 bpp */ - { - for(y=0;y<y_size;y++) - { - for(x=0;x<x_size;x+=32) - { - for(xi=0;xi<32;xi++) - bitmap.pix32(y+y_start, x+xi+x_start) = m_palette->pen((m_vram[count]>>(xi))&1); - - count++; - } - } - break; - } - case 2: /* 4 bpp */ - { - for(y=0;y<y_size;y++) - { - for(x=0;x<x_size;x+=8) - { - for(xi=0;xi<8;xi++) - bitmap.pix32(y+y_start, x+xi+x_start) = m_palette->pen((m_vram[count]>>(xi*4))&0xf); - - count++; - } - } - break; - } - #if 0 - case 1: /* 2 bpp */ - { - for(y=0;y<y_size;y++) - { - for(x=0;x<x_size;x+=4) - { - for(xi=0;xi<4;xi++) - bitmap.pix32(y+y_start, x+xi+x_start) = m_palette->pen((vram[count]>>(xi*2))&3); - - count++; - } - } - } - break; - - case 3: /* 8 bpp */ - { - for(y=0;y<y_size;y++) - { - for(x=0;x<x_size;x++) - { - bitmap.pix32(y+y_start, x+x_start) = m_palette->pen((vram[count])&0xff); - - count++; - } - } - } - break; - case 4: /* 16 bpp */ - { - for(y=0;y<y_size;y++) - { - for(x=0;x<x_size;x++) - { - int r,g,b,pen; - - pen = ((vram[count]<<8)|(vram[count+1]))&0xffff; - r = (pen & 0x000f); - g = (pen & 0x00f0) >> 4; - b = (pen & 0x0f00) >> 8; - r = (r << 4) | (r & 0xf); - g = (g << 4) | (g & 0xf); - b = (b << 4) | (b & 0xf); - - bitmap.pix32(y+y_start, x+x_start) = b | g << 8 | r << 16; - - count+=2; - } - } - } - break; - case 6: /* 32 bpp */ - { - for(y=0;y<y_size;y++) - { - for(x=0;x<x_size;x++) - { - int r,g,b,pen; - - pen = ((vram[count]<<24)|(vram[count+1]<<16)|(vram[count+2]<<8)|(vram[count+3]<<0)); - r = (pen & 0x0000ff); - g = (pen & 0x00ff00) >> 8; - b = (pen & 0xff0000) >> 16; - - bitmap.pix32(y+y_start, x+x_start) = b | g << 8 | r << 16; - - count+=4; - } - } - } - break; - #endif - default: - fatalerror("VIDC20 %08x BPP mode not supported\n",m_vidc20_bpp_mode); - break; - } - - return 0; -} - -/* -* -* IOMD / ARM7500 / ARM7500FE chip emulation -* -*/ - -/* TODO: some of these registers are actually ARM7500 specific */ -static const char *const iomd_regnames[] = -{ - "I/O Control", // 0x000 IOCR - "Keyboard Data", // 0x004 KBDDAT - "Keyboard Control", // 0x008 KBDCR - "General Purpose I/O Lines", // 0x00c IOLINES - "IRQA Status", // 0x010 IRQSTA - "IRQA Request/clear", // 0x014 IRQRQA - "IRQA Mask", // 0x018 IRQMSKA - "Enter SUSPEND Mode", // 0x01c SUSMODE - "IRQB Status", // 0x020 IRQSTB - "IRQB Request/clear", // 0x024 IRQRQB - "IRQB Mask", // 0x028 IRQMSKB - "Enter STOP Mode", // 0x02c STOPMODE - "FIQ Status", // 0x030 FIQST - "FIQ Request/clear", // 0x034 FIQRQ - "FIQ Mask", // 0x038 FIQMSK - "Clock divider control", // 0x03c CLKCTL - "Timer 0 Low Bits", // 0x040 T0LOW - "Timer 0 High Bits", // 0x044 T0HIGH - "Timer 0 Go Command", // 0x048 T0GO - "Timer 0 Latch Command", // 0x04c T0LATCH - "Timer 1 Low Bits", // 0x050 T1LOW - "Timer 1 High Bits", // 0x054 T1HIGH - "Timer 1 Go Command", // 0x058 T1GO - "Timer 1 Latch Command", // 0x05c T1LATCH - "IRQC Status", // 0x060 IRQSTC - "IRQC Request/clear", // 0x064 IRQRQC - "IRQC Mask", // 0x068 IRQMSKC - "LCD and IIS Control Bits", // 0x06c VIDIMUX - "IRQD Status", // 0x070 IRQSTD - "IRQD Request/clear", // 0x074 IRQRQD - "IRQD Mask", // 0x078 IRQMSKD - "<RESERVED>", // 0x07c - "ROM Control Bank 0", // 0x080 ROMCR0 - "ROM Control Bank 1", // 0x084 ROMCR1 - "DRAM Control (IOMD)", // 0x088 DRAMCR - "VRAM and Refresh Control", // 0x08c VREFCR - "Flyback Line Size", // 0x090 FSIZE - "Chip ID no. Low Byte", // 0x094 ID0 - "Chip ID no. High Byte", // 0x098 ID1 - "Chip Version Number", // 0x09c VERSION - "Mouse X Position", // 0x0a0 MOUSEX - "Mouse Y Position", // 0x0a4 MOUSEY - "Mouse Data", // 0x0a8 MSEDAT - "Mouse Control", // 0x0ac MSECR - "<RESERVED>", // 0x0b0 - "<RESERVED>", // 0x0b4 - "<RESERVED>", // 0x0b8 - "<RESERVED>", // 0x0bc - "DACK Timing Control", // 0x0c0 DMATCR - "I/O Timing Control", // 0x0c4 IOTCR - "Expansion Card Timing", // 0x0c8 ECTCR - "DMA External Control", // 0x0cc DMAEXT (IOMD) / ASTCR (ARM7500) - "DRAM Width Control", // 0x0d0 DRAMWID - "Force CAS/RAS Lines Low", // 0x0d4 SELFREF - "<RESERVED>", // 0x0d8 - "<RESERVED>", // 0x0dc - "A to D IRQ Control", // 0x0e0 ATODICR - "A to D IRQ Status", // 0x0e4 ATODCC - "A to D IRQ Converter Control", // 0x0e8 ATODICR - "A to D IRQ Counter 1", // 0x0ec ATODCNT1 - "A to D IRQ Counter 2", // 0x0f0 ATODCNT2 - "A to D IRQ Counter 3", // 0x0f4 ATODCNT3 - "A to D IRQ Counter 4", // 0x0f8 ATODCNT4 - "<RESERVED>", // 0x0fc - "I/O DMA 0 CurA", // 0x100 IO0CURA - "I/O DMA 0 EndA", // 0x104 IO0ENDA - "I/O DMA 0 CurB", // 0x108 IO0CURB - "I/O DMA 0 EndB", // 0x10c IO0ENDB - "I/O DMA 0 Control", // 0x110 IO0CR - "I/O DMA 0 Status", // 0x114 IO0ST - "<RESERVED>", // 0x118 - "<RESERVED>", // 0x11c - "I/O DMA 1 CurA", // 0x120 IO1CURA - "I/O DMA 1 EndA", // 0x124 IO1ENDA - "I/O DMA 1 CurB", // 0x128 IO1CURB - "I/O DMA 1 EndB", // 0x12c IO1ENDB - "I/O DMA 1 Control", // 0x130 IO1CR - "I/O DMA 1 Status", // 0x134 IO1ST - "<RESERVED>", // 0x138 - "<RESERVED>", // 0x13c - "I/O DMA 2 CurA", // 0x140 IO2CURA - "I/O DMA 2 EndA", // 0x144 IO2ENDA - "I/O DMA 2 CurB", // 0x148 IO2CURB - "I/O DMA 2 EndB", // 0x14c IO2ENDB - "I/O DMA 2 Control", // 0x150 IO2CR - "I/O DMA 2 Status", // 0x154 IO2ST - "<RESERVED>", // 0x158 - "<RESERVED>", // 0x15c - "I/O DMA 3 CurA", // 0x160 IO3CURA - "I/O DMA 3 EndA", // 0x164 IO3ENDA - "I/O DMA 3 CurB", // 0x168 IO3CURB - "I/O DMA 3 EndB", // 0x16c IO3ENDB - "I/O DMA 3 Control", // 0x170 IO3CR - "I/O DMA 3 Status", // 0x174 IO3ST - "<RESERVED>", // 0x178 - "<RESERVED>", // 0x17c - "Sound DMA 0 CurA", // 0x180 SD0CURA - "Sound DMA 0 EndA", // 0x184 SD0ENDA - "Sound DMA 0 CurB", // 0x188 SD0CURB - "Sound DMA 0 EndB", // 0x18c SD0ENDB - "Sound DMA 0 Control", // 0x190 SD0CR - "Sound DMA 0 Status", // 0x194 SD0ST - "<RESERVED>", // 0x198 - "<RESERVED>", // 0x19c - "Sound DMA 1 CurA", // 0x1a0 SD1CURA - "Sound DMA 1 EndA", // 0x1a4 SD1ENDA - "Sound DMA 1 CurB", // 0x1a8 SD1CURB - "Sound DMA 1 EndB", // 0x1ac SD1ENDB - "Sound DMA 1 Control", // 0x1b0 SD1CR - "Sound DMA 1 Status", // 0x1b4 SD1ST - "<RESERVED>", // 0x1b8 - "<RESERVED>", // 0x1bc - "Cursor DMA Current", // 0x1c0 CURSCUR - "Cursor DMA Init", // 0x1c4 CURSINIT - "Duplex LCD Current B", // 0x1c8 VIDCURB - "<RESERVED>", // 0x1cc - "Video DMA Current", // 0x1d0 VIDCUR - "Video DMA End", // 0x1d4 VIDEND - "Video DMA Start", // 0x1d8 VIDSTART - "Video DMA Init", // 0x1dc VIDINIT - "Video DMA Control", // 0x1e0 VIDCR - "<RESERVED>", // 0x1e4 - "Duplex LCD Init B", // 0x1e8 VIDINITB - "<RESERVED>", // 0x1ec - "DMA IRQ Status", // 0x1f0 DMAST - "DMA IRQ Request", // 0x1f4 DMARQ - "DMA IRQ Mask", // 0x1f8 DMAMSK - "<RESERVED>" // 0x1fc + + bool m_i2cmem_clock; + DECLARE_READ_LINE_MEMBER(iocr_od0_r); + DECLARE_READ_LINE_MEMBER(iocr_od1_r); + DECLARE_WRITE_LINE_MEMBER(iocr_od0_w); + DECLARE_WRITE_LINE_MEMBER(iocr_od1_w); }; -#define IOMD_IOCR 0x000/4 -#define IOMD_KBDDAT 0x004/4 -#define IOMD_KBDCR 0x008/4 - -#define IOMD_IRQSTA 0x010/4 -#define IOMD_IRQRQA 0x014/4 -#define IOMD_IRQMSKA 0x018/4 - -#define IOMD_IRQSTB 0x020/4 -#define IOMD_IRQRQB 0x024/4 -#define IOMD_IRQMSKB 0x028/4 - -#define IOMD_T0LOW 0x040/4 -#define IOMD_T0HIGH 0x044/4 -#define IOMD_T0GO 0x048/4 -#define IOMD_T0LATCH 0x04c/4 - -#define IOMD_T1LOW 0x050/4 -#define IOMD_T1HIGH 0x054/4 -#define IOMD_T1GO 0x058/4 -#define IOMD_T1LATCH 0x05c/4 - -#define IOMD_ID0 0x094/4 -#define IOMD_ID1 0x098/4 -#define IOMD_VERSION 0x09c/4 - -#define IOMD_VIDCUR 0x1d0/4 -#define IOMD_VIDEND 0x1d4/4 -#define IOMD_VIDSTART 0x1d8/4 -#define IOMD_VIDINIT 0x1dc/4 -#define IOMD_VIDCR 0x1e0/4 - -#define IOMD_DMARQ 0x1f4/4 - -void riscpc_state::update_irq() -{ - if (m_IRQ_status_A & m_IRQ_mask_A) - m_maincpu->pulse_input_line(ARM7_IRQ_LINE, m_maincpu->minimum_quantum_time()); - if (m_IRQ_status_B & m_IRQ_mask_B) - m_maincpu->pulse_input_line(ARM7_IRQ_LINE, m_maincpu->minimum_quantum_time()); -} - -void riscpc_state::fire_iomd_timer(int timer) +READ_LINE_MEMBER(riscpc_state::iocr_od1_r) { - int timer_count = m_timer_counter[timer]; - int val = timer_count / 2; // correct? - - if(val==0) - m_IOMD_timer[timer]->adjust(attotime::never); - else - m_IOMD_timer[timer]->adjust(attotime::from_usec(val), 0, attotime::from_usec(val)); + // TODO: presuming same as Acorn Archimedes, where i2c clock can be readback + return (m_i2cmem_clock == true) ? 1 : 0; } -TIMER_CALLBACK_MEMBER(riscpc_state::IOMD_timer0_callback) +READ_LINE_MEMBER(riscpc_state::iocr_od0_r) { - m_IRQ_status_A|=0x20; - update_irq(); -} - -TIMER_CALLBACK_MEMBER(riscpc_state::IOMD_timer1_callback) -{ - m_IRQ_status_A|=0x40; - update_irq(); -} - -TIMER_CALLBACK_MEMBER(riscpc_state::flyback_timer_callback) -{ - m_IRQ_status_A|=0x08; - update_irq(); - - // for convenience lets just implement the video DMA transfer here - // the actual transfer probably works per scanline once enabled. - if(m_viddma_status & 0x20) - viddma_transfer_start(); - - m_flyback_timer->adjust(m_screen->time_until_pos(m_vidc20_vert_reg[VDER])); + return (m_i2cmem->read_sda() ? 1 : 0); //eeprom read } -void riscpc_state::viddma_transfer_start() +WRITE_LINE_MEMBER(riscpc_state::iocr_od0_w) { - address_space &mem = m_maincpu->space(AS_PROGRAM); - uint32_t src = m_viddma_addr_init; - uint32_t dst = 0; - uint32_t size = m_viddma_addr_end; - uint32_t dma_index; - - /* TODO: this should actually be a qword transfer */ - for(dma_index = 0;dma_index < size;dma_index+=4) - { - m_vram[dst] = mem.read_dword(src); - - src+=4; - // TODO: honor boundary - dst++; - } -} - -READ32_MEMBER( riscpc_state::a7000_iomd_r ) -{ -// if(offset != IOMD_KBDCR) -// logerror("IOMD: %s Register (%04x) read\n",iomd_regnames[offset & (0x1ff >> 2)],offset*4); - - - switch(offset) - { - case IOMD_IOCR: - { - uint8_t flyback; - int vert_pos; - - vert_pos = m_screen->vpos(); - flyback = (vert_pos <= m_vidc20_vert_reg[VDSR] || vert_pos >= m_vidc20_vert_reg[VDER]) ? 0x80 : 0x00; - - return flyback | (m_IOMD_IO_ctrl & 0x7c) | 0x34 | (m_i2c_clk << 1) | (m_i2cmem->read_sda() ? 1 : 0); - } - case IOMD_KBDDAT: return m_kbdc->data_r(); - case IOMD_KBDCR: return m_kbdc->status_r(); - - /* - 1--- ---- always high (force) - -x-- ---- Timer 1 - --x- ---- Timer 0 - ---x ---- Power On Reset - ---- x--- Flyback - ---- -x-- nINT1 - ---- --0- always low - ---- ---x INT2 - */ - case IOMD_IRQSTA: return (m_IRQ_status_A & 0x7d) | 0x80; - case IOMD_IRQRQA: return (m_IRQ_status_A & m_IRQ_mask_A); - case IOMD_IRQMSKA: return m_IRQ_mask_A; - - case IOMD_IRQSTB: return m_IRQ_status_B; - case IOMD_IRQRQB: return (m_IRQ_status_B & m_IRQ_mask_B); - case IOMD_IRQMSKB: return m_IRQ_mask_B; - - case IOMD_T0LOW: return m_timer_out[0] & 0xff; - case IOMD_T0HIGH: return (m_timer_out[0] >> 8) & 0xff; - - case IOMD_T1LOW: return m_timer_out[1] & 0xff; - case IOMD_T1HIGH: return (m_timer_out[1] >> 8) & 0xff; - - case IOMD_ID0: return m_io_id & 0xff; // IOMD ID low - case IOMD_ID1: return (m_io_id >> 8) & 0xff; // IOMD ID high - case IOMD_VERSION: return 0; - - case IOMD_VIDEND: return m_viddma_addr_end & 0x00fffff8; //bits 31:24 undefined - case IOMD_VIDSTART: return m_viddma_addr_start & 0x1ffffff8; //bits 31, 30, 29 undefined - case IOMD_VIDINIT: return m_viddma_addr_init & 0x1ffffff8; - case IOMD_VIDCR: return (m_viddma_status & 0xa0) | 0x50; //bit 6 = DRAM mode, bit 4 = QWORD transfer - - default: logerror("IOMD: %s Register (%04x) read\n",iomd_regnames[offset & (0x1ff >> 2)],offset*4); break; - } - - return 0; + m_i2cmem->write_sda(state == true ? 1 : 0); } -WRITE32_MEMBER( riscpc_state::a7000_iomd_w ) +WRITE_LINE_MEMBER(riscpc_state::iocr_od1_w) { -// logerror("IOMD: %s Register (%04x) write = %08x\n",iomd_regnames[offset & (0x1ff >> 2)],offset*4,data); - - switch(offset) - { - case IOMD_IOCR: - m_IOMD_IO_ctrl = data & 0x7c; - m_i2cmem->write_sda(data & 0x01); - m_i2c_clk = (data & 2) >> 1; - m_i2cmem->write_scl(m_i2c_clk); - break; - - case IOMD_KBDDAT: m_kbdc->data_w(data); break; - case IOMD_KBDCR: m_kbdc->command_w(data); - //m_IRQ_status_B |= 0x40; - //machine().debug_break(); - //update_irq(); - break; - - case IOMD_IRQRQA: - m_IRQ_status_A &= ~data; - m_IRQ_status_A |= 0x80; - update_irq(); - break; - - case IOMD_IRQMSKA: - m_IRQ_mask_A = data; - update_irq(); - break; - - case IOMD_IRQRQB: - m_IRQ_status_B &= ~data; - update_irq(); - break; - - case IOMD_IRQMSKB: - m_IRQ_mask_B = data; - update_irq(); - break; - - case IOMD_T0LOW: m_timer_in[0] = (m_timer_in[0] & 0xff00) | (data & 0xff); break; - case IOMD_T0HIGH: m_timer_in[0] = (m_timer_in[0] & 0x00ff) | ((data & 0xff) << 8); break; - case IOMD_T0GO: - m_timer_counter[0] = m_timer_in[0]; - fire_iomd_timer(0); - break; - case IOMD_T0LATCH: - { - m_t0readinc^=1; - m_timer_out[0] = m_timer_counter[0]; - if(m_t0readinc) - { - m_timer_counter[0]--; - if(m_timer_counter[0] < 0) - m_timer_counter[0]+= m_timer_in[0]; - } - } - break; - - case IOMD_T1LOW: m_timer_in[1] = (m_timer_in[1] & 0xff00) | (data & 0xff); break; - case IOMD_T1HIGH: m_timer_in[1] = (m_timer_in[1] & 0x00ff) | ((data & 0xff) << 8); break; - case IOMD_T1GO: - m_timer_counter[1] = m_timer_in[1]; - fire_iomd_timer(1); - break; - case IOMD_T1LATCH: - { - m_t1readinc^=1; - m_timer_out[1] = m_timer_counter[1]; - if(m_t1readinc) - { - m_timer_counter[1]--; - if(m_timer_counter[1] < 0) - m_timer_counter[1]+= m_timer_in[1]; - } - } - break; - - case IOMD_VIDEND: m_viddma_addr_end = data & 0x00fffff8; //bits 31:24 unused - break; - case IOMD_VIDSTART: m_viddma_addr_start = data & 0x1ffffff8; //bits 31, 30, 29 unused - break; - case IOMD_VIDINIT: m_viddma_addr_init = data & 0x1ffffff8; - break; - case IOMD_VIDCR: - m_viddma_status = data & 0xa0; if(data & 0x20) { viddma_transfer_start(); } - break; - - - default: logerror("IOMD: %s Register (%04x) write = %08x\n",iomd_regnames[offset & (0x1ff >> 2)],offset*4,data); - } + m_i2cmem_clock = state; + m_i2cmem->write_scl(state == true ? 1 : 0); } void riscpc_state::a7000_map(address_map &map) @@ -850,16 +104,17 @@ void riscpc_state::a7000_map(address_map &map) // // map(0x02000000, 0x027fffff).mirror(0x00800000).ram(); // VRAM, not installed on A7000 models // I/O 03000000 - 033fffff -// map(0x03010000, 0x03011fff) //Super IO -// map(0x03012000, 0x03029fff) //FDC -// map(0x0302b000, 0x0302bfff) //Network podule -// map(0x03040000, 0x0304ffff) //podule space 0,1,2,3 -// map(0x03070000, 0x0307ffff) //podule space 4,5,6,7 - map(0x03200000, 0x032001ff).rw(FUNC(riscpc_state::a7000_iomd_r), FUNC(riscpc_state::a7000_iomd_w)); //IOMD Registers //mirrored at 0x03000000-0x1ff? +// AM_RANGE(0x03010000, 0x03011fff) //Super IO +// AM_RANGE(0x03012000, 0x03029fff) //FDC +// AM_RANGE(0x0302b000, 0x0302bfff) //Network podule +// AM_RANGE(0x03040000, 0x0304ffff) //podule space 0,1,2,3 +// AM_RANGE(0x03070000, 0x0307ffff) //podule space 4,5,6,7 + map(0x03200000, 0x032001ff).m(m_iomd, FUNC(arm7500fe_iomd_device::map)); map(0x03310000, 0x03310003).portr("MOUSE"); - map(0x03400000, 0x037fffff).w(FUNC(riscpc_state::a7000_vidc20_w)); -// map(0x08000000, 0x08ffffff).mirror(0x07000000); //EASI space + map(0x03400000, 0x037fffff).w(m_vidc, FUNC(arm_vidc20_device::write)); +// AM_RANGE(0x08000000, 0x08ffffff) AM_MIRROR(0x07000000) //EASI space + map(0x10000000, 0x13ffffff).ram(); //SIMM 0 bank 0 map(0x14000000, 0x17ffffff).ram(); //SIMM 0 bank 1 // map(0x18000000, 0x18ffffff).mirror(0x03000000).ram(); //SIMM 1 bank 0 @@ -898,44 +153,14 @@ INPUT_PORTS_END void riscpc_state::machine_start() { - m_IOMD_timer[0] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(riscpc_state::IOMD_timer0_callback),this)); - m_IOMD_timer[1] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(riscpc_state::IOMD_timer1_callback),this)); - m_flyback_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(riscpc_state::flyback_timer_callback),this)); - - m_io_id = 0xd4e7; + // ... } void riscpc_state::machine_reset() { - m_IOMD_IO_ctrl = 0x08 | 0x34; //bit 0,1 and 3 set high on reset plus 2,4,5 always high - // TODO: jumps to EASI space at $0c0016xx with this on!? -// m_IRQ_status_A = 0x10; // set POR bit ON - m_IRQ_mask_A = 0x00; - - m_IOMD_keyb_ctrl = 0x00; - m_IOMD_timer[0]->adjust(attotime::never); - m_IOMD_timer[1]->adjust(attotime::never); - m_flyback_timer->adjust(attotime::never); } -WRITE_LINE_MEMBER(riscpc_state::keyboard_reset) -{ - printf("RST %d\n",state); -} - -WRITE_LINE_MEMBER(riscpc_state::keyboard_interrupt) -{ - printf("IRQ %d\n",state); - if (!state) - return; - -// machine().debug_break(); - m_IRQ_status_B|=0x80; - update_irq(); -} - - void riscpc_state::base_config(machine_config &config) { I2C_24C02(config, m_i2cmem); @@ -958,67 +183,95 @@ void riscpc_state::base_config(machine_config &config) // aux.set_pc_kbdc_slot(&aux_con); PS2_KEYBOARD_CONTROLLER(config, m_kbdc, 12_MHz_XTAL); - m_kbdc->hot_res().set(FUNC(riscpc_state::keyboard_reset)); + m_kbdc->hot_res().set(m_iomd, FUNC(arm_iomd_device::keyboard_reset)); m_kbdc->kbd_clk().set(kbd_con, FUNC(pc_kbdc_device::clock_write_from_mb)); m_kbdc->kbd_data().set(kbd_con, FUNC(pc_kbdc_device::data_write_from_mb)); - m_kbdc->kbd_irq().set(FUNC(riscpc_state::keyboard_interrupt)); + m_kbdc->kbd_irq().set(m_iomd, FUNC(arm_iomd_device::keyboard_irq)); // m_kbdc->aux_clk().set(aux_con, FUNC(pc_kbdc_device::clock_write_from_mb)); // m_kbdc->aux_data().set(aux_con, FUNC(pc_kbdc_device::data_write_from_mb)); // m_kbdc->aux_irq().set(FUNC(riscpc_state::keyboard_interrupt)); /* video hardware */ SCREEN(config, m_screen, SCREEN_TYPE_RASTER); - m_screen->set_refresh_hz(60); - m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */ - m_screen->set_size(1900, 1080); //max available size - m_screen->set_visarea(0, 1900-1, 0, 1080-1); - m_screen->set_screen_update(FUNC(riscpc_state::screen_update)); - PALETTE(config, m_palette).set_entries(0x200); + + ARM_VIDC20(config, m_vidc, 24_MHz_XTAL); + m_vidc->set_screen("screen"); + m_vidc->vblank().set(m_iomd, FUNC(arm_iomd_device::vblank_irq)); + m_vidc->sound_drq().set(m_iomd, FUNC(arm_iomd_device::sound_drq)); + + m_iomd->set_host_cpu_tag(m_maincpu); + m_iomd->set_vidc_tag(m_vidc); + m_iomd->set_kbdc_tag(m_kbdc); + m_iomd->iocr_read_od<0>().set(FUNC(riscpc_state::iocr_od0_r)); + m_iomd->iocr_read_od<1>().set(FUNC(riscpc_state::iocr_od1_r)); + m_iomd->iocr_write_od<0>().set(FUNC(riscpc_state::iocr_od0_w)); + m_iomd->iocr_write_od<1>().set(FUNC(riscpc_state::iocr_od1_w)); } void riscpc_state::rpc600(machine_config &config) { - /* Basic machine hardware */ - ARM7(config, m_maincpu, 60_MHz_XTAL/2); // ARM610 + constexpr XTAL cpuxtal(60_MHz_XTAL/2); + + ARM7(config, m_maincpu, cpuxtal); // really ARM610 m_maincpu->set_addrmap(AS_PROGRAM, &riscpc_state::riscpc_map); + + ARM7500FE_IOMD(config, m_iomd, cpuxtal); base_config(config); } void riscpc_state::rpc700(machine_config &config) { - /* Basic machine hardware */ - ARM710A(config, m_maincpu, 80_MHz_XTAL/2); // ARM710 + constexpr XTAL cpuxtal(80_MHz_XTAL/2); + ARM710A(config, m_maincpu, cpuxtal); m_maincpu->set_addrmap(AS_PROGRAM, &riscpc_state::riscpc_map); + + ARM7500FE_IOMD(config, m_iomd, cpuxtal); base_config(config); } void riscpc_state::a7000(machine_config &config) { - /* Basic machine hardware */ - ARM7(config, m_maincpu, XTAL(32'000'000)); // ARM7500 + constexpr XTAL cpuxtal(32'000'000); + + ARM7500(config, m_maincpu, cpuxtal); m_maincpu->set_addrmap(AS_PROGRAM, &riscpc_state::a7000_map); + + ARM7500FE_IOMD(config, m_iomd, cpuxtal); base_config(config); } void riscpc_state::a7000p(machine_config &config) { - a7000(config); - m_maincpu->set_clock(XTAL(48'000'000)); // ARM7500FE + constexpr XTAL cpuxtal(48'000'000); + + ARM7500(config, m_maincpu, cpuxtal); // really ARM7500FE + m_maincpu->set_addrmap(AS_PROGRAM, &riscpc_state::a7000_map); + + ARM7500FE_IOMD(config, m_iomd, cpuxtal); + base_config(config); } void riscpc_state::sarpc(machine_config &config) { - /* Basic machine hardware */ - SA1110(config, m_maincpu, 202000000); // StrongARM + // TODO: ranges from 160 to 233 MHz + constexpr XTAL cpuxtal(200'000'000); + + SA1110(config, m_maincpu, cpuxtal); // StrongARM m_maincpu->set_addrmap(AS_PROGRAM, &riscpc_state::riscpc_map); + + ARM7500FE_IOMD(config, m_iomd, cpuxtal); base_config(config); } void riscpc_state::sarpc_j233(machine_config &config) { - /* Basic machine hardware */ - SA1110(config, m_maincpu, 233000000); // StrongARM + // TODO: 233 MHz, unsupported by xtal module + constexpr XTAL cpuxtal(200'000'000); + + SA1110(config, m_maincpu, cpuxtal); // StrongARM m_maincpu->set_addrmap(AS_PROGRAM, &riscpc_state::riscpc_map); + + ARM7500FE_IOMD(config, m_iomd, cpuxtal); base_config(config); } diff --git a/src/mame/drivers/ssfindo.cpp b/src/mame/drivers/ssfindo.cpp index 4bc633cb729..26a14c43de2 100644 --- a/src/mame/drivers/ssfindo.cpp +++ b/src/mame/drivers/ssfindo.cpp @@ -12,7 +12,7 @@ Tomasz Slanina analog[at]op.pl TODO: - - move PS7500 stuff to machine/ps7500.c + - Correct IOMD type (CL-PS7500 -> ARM7500FE?) - 24c01 eeprom (IOLINES) - timing - unknown reads/writes @@ -128,136 +128,48 @@ Notes: #include "cpu/arm7/arm7.h" #include "cpu/arm7/arm7core.h" #include "machine/i2cmem.h" +#include "machine/acorn_vidc.h" +#include "machine/arm_iomd.h" #include "emupal.h" #include "screen.h" #include "speaker.h" -enum -{ - IOCR=0, - KBDDAT, - KBDCR, - IOLINES, - IRQSTA, - IRQRQA, - IRQMSKA, - SUSMODE, - IRQSTB, - IRQRQB, - IRQMSKB, - STOPMODE, - FIQST, - FIQRQ, - FIQMSK, - CLKCTL, - T0low, - T0high, - T0GO, - T0LAT, - T1low, - T1high, - T1GO, - T1LAT, - IRQSTC, - IRQRQC, - IRQMSKC, - VIDMUX, - IRQSTD, - IRQRQD, - IRQMSKD, - - ROMCR0=32, - ROMCR1, - - REFCR=35, - - ID0=37, - ID1, - VERSION, - - MSEDAT=42, - MSECR, - - IOTCR=49, - ECTCR, - ASTCR, - DRAMCR, - SELREF, - - ATODICR=56, - ATODSR, - ATODCC, - ATODCNT1, - ATODCNT2, - ATODCNT3, - ATODCNT4, - - SD0CURA=96, - SD0ENDA, - SD0CURB, - SD0ENDB, - SD0CR, - SD0ST, - - CURSCUR=112, - CURSINIT, - VIDCURB, - - VIDCURA=116, - VIDEND, - VIDSTART, - VIDINITA, - VIDCR, - - VIDINITB=122, - - DMAST=124, - DMARQ, - DMASK, - MAXIO=128 -}; - - class ssfindo_state : public driver_device { public: ssfindo_state(const machine_config &mconfig, device_type type, const char *tag) - : driver_device(mconfig, type, tag), - m_maincpu(*this, "maincpu"), - m_palette(*this, "palette"), - m_i2cmem(*this, "i2cmem"), - m_vram(*this, "vram"), - m_flashrom(*this, "flash"), - m_io_ps7500(*this, "PS7500") { } + : driver_device(mconfig, type, tag) + , m_maincpu(*this, "maincpu") + , m_vidc(*this, "vidc") + , m_iomd(*this, "iomd") + , m_i2cmem(*this, "i2cmem") + , m_flashrom(*this, "flash") + { } void ssfindo(machine_config &config); void ppcar(machine_config &config); - void tetfight(machine_config &config); void init_ssfindo(); void init_ppcar(); - void init_tetfight(); -private: +protected: required_device<cpu_device> m_maincpu; - required_device<palette_device> m_palette; + required_device<arm_vidc20_device> m_vidc; + required_device<arm7500fe_iomd_device> m_iomd; optional_device<i2cmem_device> m_i2cmem; - - required_shared_ptr<uint32_t> m_vram; - required_region_ptr<uint16_t> m_flashrom; + + void init_common(); + virtual void machine_start() override; + virtual void machine_reset() override; - required_ioport m_io_ps7500; + typedef void (ssfindo_state::*speedup_func)(); + speedup_func m_speedup; - // driver init configuration uint32_t m_flashType; - int m_iocr_hack; - // common - uint32_t m_PS7500_IO[MAXIO]; - uint32_t m_PS7500_FIFO[256]; - emu_timer *m_PS7500timer0; - emu_timer *m_PS7500timer1; + bool m_i2cmem_clock; +private: // ssfindo and ppcar uint32_t m_flashAdr; @@ -265,11 +177,6 @@ private: uint32_t m_adrLatch; uint32_t m_flashN; - // common - DECLARE_WRITE32_MEMBER(FIFO_w); - DECLARE_READ32_MEMBER(PS7500_IO_r); - DECLARE_WRITE32_MEMBER(PS7500_IO_w); - // ssfindo and ppcar DECLARE_READ32_MEMBER(io_r); DECLARE_WRITE32_MEMBER(io_w); @@ -282,125 +189,94 @@ private: // ppcar DECLARE_READ32_MEMBER(randomized_r); - // tetfight - DECLARE_READ32_MEMBER(tetfight_unk_r); - DECLARE_WRITE32_MEMBER(tetfight_unk_w); - - void init_common(); - virtual void machine_reset() override; - - uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); - - INTERRUPT_GEN_MEMBER(interrupt); - TIMER_CALLBACK_MEMBER(PS7500_Timer0_callback); - TIMER_CALLBACK_MEMBER(PS7500_Timer1_callback); - - typedef void (ssfindo_state::*speedup_func)(); - speedup_func m_speedup; - - void PS7500_startTimer0(); - void PS7500_startTimer1(); - void PS7500_reset(); void ssfindo_speedups(); void ppcar_speedups(); void ppcar_map(address_map &map); void ssfindo_map(address_map &map); - void tetfight_map(address_map &map); + + DECLARE_READ8_MEMBER(iolines_r); + DECLARE_WRITE8_MEMBER(iolines_w); + bool m_flash_bank_select; }; - -uint32_t ssfindo_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) +class tetfight_state : public ssfindo_state { - int s,x,y; +public: + tetfight_state(const machine_config &mconfig, device_type type, const char *tag) + : ssfindo_state(mconfig, type, tag) + {} - if( m_PS7500_IO[VIDCR]&0x20) //video DMA enabled - { - s=( (m_PS7500_IO[VIDINITA]&0x1fffffff)-0x10000000)/4; - - if(s>=0 && s<(0x10000000/4)) - { - for(y=0;y<256;y++) - for(x=0;x<320;x+=4) - { - bitmap.pix16(y, x+0) = m_vram[s]&0xff; - bitmap.pix16(y, x+1) = (m_vram[s]>>8)&0xff; - bitmap.pix16(y, x+2) = (m_vram[s]>>16)&0xff; - bitmap.pix16(y, x+3) = (m_vram[s]>>24)&0xff; - s++; - } - } - } + void init_tetfight(); + void tetfight(machine_config &config); - return 0; -} +protected: -WRITE32_MEMBER(ssfindo_state::FIFO_w) -{ - m_PS7500_FIFO[data>>28]=data; +private: + bool m_i2cdata_hack; - if(!(data>>28)) - { - m_palette->set_pen_color(m_PS7500_FIFO[1]&0xff, data&0xff,(data>>8)&0xff,(data>>16)&0xff); - m_PS7500_FIFO[1]++; //autoinc - } -} -TIMER_CALLBACK_MEMBER(ssfindo_state::PS7500_Timer0_callback) + void tetfight_map(address_map &map); + + DECLARE_READ_LINE_MEMBER(iocr_od0_r); + DECLARE_READ_LINE_MEMBER(iocr_od1_r); + DECLARE_WRITE_LINE_MEMBER(iocr_od0_w); + DECLARE_WRITE_LINE_MEMBER(iocr_od1_w); + DECLARE_READ32_MEMBER(tetfight_unk_r); + DECLARE_WRITE32_MEMBER(tetfight_unk_w); +}; + +//TODO: eeprom 24c01 & 24c02 +// TODO: untangle, kill hacks +READ8_MEMBER(ssfindo_state::iolines_r) { - m_PS7500_IO[IRQSTA]|=0x20; - if(m_PS7500_IO[IRQMSKA]&0x20) - { - m_maincpu->pulse_input_line(ARM7_IRQ_LINE, m_maincpu->minimum_quantum_time()); - } + if(m_flashType == 1) + return 0; + else + return machine().rand(); } -void ssfindo_state::PS7500_startTimer0() +WRITE8_MEMBER(ssfindo_state::iolines_w) { - int val=((m_PS7500_IO[T0low]&0xff)|((m_PS7500_IO[T0high]&0xff)<<8))>>1; + if(data&0xc0) + m_adrLatch=0; - if(val==0) - m_PS7500timer0->adjust(attotime::never); - else - m_PS7500timer0->adjust(attotime::from_usec(val ), 0, attotime::from_usec(val )); + if(m_maincpu->pc() == 0xbac0 && m_flashType == 1) + m_flashN=data&1; + + m_flash_bank_select = BIT(data, 0); } -TIMER_CALLBACK_MEMBER(ssfindo_state::PS7500_Timer1_callback) +// inverted compared to riscpc.cpp +// TODO: simplify hookup over i2cmem having AND 1 on state for a writeline (?) +READ_LINE_MEMBER(tetfight_state::iocr_od1_r) { - m_PS7500_IO[IRQSTA]|=0x40; - if(m_PS7500_IO[IRQMSKA]&0x40) - { - m_maincpu->pulse_input_line(ARM7_IRQ_LINE, m_maincpu->minimum_quantum_time()); - } + // TODO: completely get rid of this speedup fn or move anywhere else + //if (m_speedup) (this->*m_speedup)(); + return (m_i2cmem->read_sda() ? 1 : 0); //eeprom read } -void ssfindo_state::PS7500_startTimer1() +READ_LINE_MEMBER(tetfight_state::iocr_od0_r) { - int val=((m_PS7500_IO[T1low]&0xff)|((m_PS7500_IO[T1high]&0xff)<<8))>>1; - if(val==0) - m_PS7500timer1->adjust(attotime::never); - else - m_PS7500timer1->adjust(attotime::from_usec(val ), 0, attotime::from_usec(val )); + return (m_i2cmem_clock == true ? 1 : 0); //eeprom read } -INTERRUPT_GEN_MEMBER(ssfindo_state::interrupt) +WRITE_LINE_MEMBER(tetfight_state::iocr_od1_w) { - m_PS7500_IO[IRQSTA]|=0x08; - if(m_PS7500_IO[IRQMSKA]&0x08) - { - device.execute().pulse_input_line(ARM7_IRQ_LINE, device.execute().minimum_quantum_time()); - } + // TODO: i2c cares about the order of this!? + // tetfight reaches PC=0x106c if initialization has success + // rpc700 doesn't like above and go into supervisor mode even with a valid NVRAM + // both wants clock first then data, not sure where the issue actually is + m_i2cdata_hack = state == true; + //m_i2cmem->write_sda(state == true ? 1 : 0); } -void ssfindo_state::PS7500_reset() +WRITE_LINE_MEMBER(tetfight_state::iocr_od0_w) { - m_PS7500_IO[IOCR] = 0x3f; - m_PS7500_IO[VIDCR] = 0; - - m_PS7500timer0->adjust( attotime::never); - m_PS7500timer1->adjust( attotime::never); + m_i2cmem_clock = state; + m_i2cmem->write_scl(state == true ? 1 : 0); + m_i2cmem->write_sda(m_i2cdata_hack == true ? 1 : 0); } - void ssfindo_state::ssfindo_speedups() { if (m_maincpu->pc()==0x2d6c8) // ssfindo @@ -417,138 +293,15 @@ void ssfindo_state::ppcar_speedups() m_maincpu->spin_until_time(attotime::from_usec(20)); } - -READ32_MEMBER(ssfindo_state::PS7500_IO_r) -{ - switch(offset) - { - case MSECR: - return machine().rand(); - - case IOLINES: //TODO: eeprom 24c01 -#if 0 - osd_printf_debug("IOLINESR %i @%x\n", offset, m_maincpu->pc()); -#endif - - if(m_flashType == 1) - return 0; - else - return machine().rand(); - - case IRQSTA: - return (m_PS7500_IO[offset] & (~2)) | 0x80; - - case IRQRQA: - return (m_PS7500_IO[IRQSTA] & m_PS7500_IO[IRQMSKA]) | 0x80; - - case IOCR: //TODO: nINT1, OD[n] p.81 - if (m_speedup) (this->*m_speedup)(); - - if( m_iocr_hack) - { - return (m_io_ps7500->read() & 0x80) | 0x34 | (m_i2cmem->read_sda() ? 0x03 : 0x00); //eeprom read - } - - return (m_io_ps7500->read() & 0x80) | 0x37; - - case VIDCR: - return (m_PS7500_IO[offset] | 0x50) & 0xfffffff0; - - case T1low: - case T0low: - case T1high: - case T0high: - case IRQMSKA: - case VIDEND: - case VIDSTART: - case VIDINITA: //TODO: bits 29 ("equal") and 30 (last bit) p.105 - - return m_PS7500_IO[offset]; - - - } - return machine().rand();//m_PS7500_IO[offset]; -} - -WRITE32_MEMBER(ssfindo_state::PS7500_IO_w) -{ - uint32_t temp=m_PS7500_IO[offset]; - - COMBINE_DATA(&temp); - - switch(offset) - { - case IOLINES: //TODO: eeprom 24c01 - m_PS7500_IO[offset]=data; - if(data&0xc0) - m_adrLatch=0; - - if(m_maincpu->pc() == 0xbac0 && m_flashType == 1) - { - m_flashN=data&1; - } - -#if 0 - logerror("IOLINESW %i = %x @%x\n",offset,data,m_maincpu->pc()); -#endif - break; - - case IRQRQA: - m_PS7500_IO[IRQSTA]&=~temp; - break; - - case IRQMSKA: - m_PS7500_IO[IRQMSKA]=(temp&(~2))|0x80; - break; - - case T1GO: - PS7500_startTimer1(); - break; - - case T0GO: - PS7500_startTimer0(); - break; - - case VIDEND: - case VIDSTART: - COMBINE_DATA(&m_PS7500_IO[offset]); - m_PS7500_IO[offset]&=0xfffffff0; // qword align - break; - - case IOCR: - //popmessage("IOLINESW %i = %x @%x\n",offset,data,m_maincpu->pc()); - COMBINE_DATA(&m_PS7500_IO[offset]); - // TODO: correct hook-up - m_i2cmem->write_scl((data & 0x01) ? 1 : 0); - m_i2cmem->write_sda((data & 0x02) ? 1 : 0); - break; - - case REFCR: - case DRAMCR: - case SD0CR: - case ROMCR0: - case VIDMUX: - case CLKCTL: - case T1low: - case T0low: - case T1high: - case T0high: - case VIDCR: - case VIDINITA: //TODO: bit 30 (last bit) p.105 - COMBINE_DATA(&m_PS7500_IO[offset]); - break; - } -} - READ32_MEMBER(ssfindo_state::io_r) { int adr=m_flashAdr*0x200+(m_flashOffset); - switch(m_flashType) { case 0: - if(m_PS7500_IO[IOLINES]&1) //bit 0 of IOLINES = flash select ( 5/6 or 3/2 ) + //bit 0 of IOLINES = flash select ( 5/6 or 3/2 ) + if (m_flash_bank_select) adr+=0x400000; break; @@ -609,7 +362,7 @@ READ32_MEMBER(ssfindo_state::randomized_r) void ssfindo_state::ssfindo_map(address_map &map) { map(0x00000000, 0x000fffff).rom(); - map(0x03200000, 0x032001ff).rw(FUNC(ssfindo_state::PS7500_IO_r), FUNC(ssfindo_state::PS7500_IO_w)); + map(0x03200000, 0x032001ff).m(m_iomd, FUNC(arm7500fe_iomd_device::map)); map(0x03012e60, 0x03012e67).noprw(); map(0x03012fe0, 0x03012fe3).w(FUNC(ssfindo_state::debug_w)); map(0x03012ff0, 0x03012ff3).noprw(); @@ -621,14 +374,14 @@ void ssfindo_state::ssfindo_map(address_map &map) map(0x03243000, 0x03243003).portr("DSW").nopw(); map(0x0324f000, 0x0324f003).r(FUNC(ssfindo_state::SIMPLEIO_r)); map(0x03245000, 0x03245003).nopw(); /* sound ? */ - map(0x03400000, 0x03400003).w(FUNC(ssfindo_state::FIFO_w)); - map(0x10000000, 0x11ffffff).ram().share("vram"); + map(0x03400000, 0x037fffff).w(m_vidc, FUNC(arm_vidc20_device::write)); + map(0x10000000, 0x11ffffff).ram(); } void ssfindo_state::ppcar_map(address_map &map) { map(0x00000000, 0x000fffff).rom(); - map(0x03200000, 0x032001ff).rw(FUNC(ssfindo_state::PS7500_IO_r), FUNC(ssfindo_state::PS7500_IO_w)); + map(0x03200000, 0x032001ff).m(m_iomd, FUNC(arm7500fe_iomd_device::map)); map(0x03012b00, 0x03012bff).r(FUNC(ssfindo_state::randomized_r)).nopw(); map(0x03012e60, 0x03012e67).nopw(); map(0x03012ff8, 0x03012ffb).portr("IN0").nopw(); @@ -636,43 +389,52 @@ void ssfindo_state::ppcar_map(address_map &map) map(0x03340000, 0x03340007).nopw(); map(0x03341000, 0x0334101f).nopw(); map(0x033c0000, 0x033c0003).r(FUNC(ssfindo_state::io_r)).w(FUNC(ssfindo_state::io_w)); - map(0x03400000, 0x03400003).w(FUNC(ssfindo_state::FIFO_w)); + map(0x03400000, 0x037fffff).w(m_vidc, FUNC(arm_vidc20_device::write)); map(0x08000000, 0x08ffffff).ram(); - map(0x10000000, 0x10ffffff).ram().share("vram"); + map(0x10000000, 0x10ffffff).ram(); } -READ32_MEMBER(ssfindo_state::tetfight_unk_r) +READ32_MEMBER(tetfight_state::tetfight_unk_r) { //sound status ? return machine().rand(); } -WRITE32_MEMBER(ssfindo_state::tetfight_unk_w) +WRITE32_MEMBER(tetfight_state::tetfight_unk_w) { //sound latch ? } -void ssfindo_state::tetfight_map(address_map &map) +void tetfight_state::tetfight_map(address_map &map) { map(0x00000000, 0x001fffff).rom(); - map(0x03200000, 0x032001ff).rw(FUNC(ssfindo_state::PS7500_IO_r), FUNC(ssfindo_state::PS7500_IO_w)); - map(0x03400000, 0x03400003).w(FUNC(ssfindo_state::FIFO_w)); + map(0x03200000, 0x032001ff).m(m_iomd, FUNC(arm7500fe_iomd_device::map)); map(0x03240000, 0x03240003).portr("IN0"); map(0x03240004, 0x03240007).portr("IN1"); map(0x03240008, 0x0324000b).portr("DSW2"); - map(0x03240020, 0x03240023).rw(FUNC(ssfindo_state::tetfight_unk_r), FUNC(ssfindo_state::tetfight_unk_w)); - map(0x10000000, 0x14ffffff).ram().share("vram"); + map(0x03240020, 0x03240023).rw(FUNC(tetfight_state::tetfight_unk_r), FUNC(tetfight_state::tetfight_unk_w)); + map(0x03400000, 0x037fffff).w(m_vidc, FUNC(arm_vidc20_device::write)); + map(0x10000000, 0x14ffffff).ram(); +} + +void ssfindo_state::machine_start() +{ + // TODO: convert to exact flash device + save_item(NAME(m_flashAdr)); + save_item(NAME(m_flashOffset)); + save_item(NAME(m_adrLatch)); + save_item(NAME(m_flashN)); + save_item(NAME(m_i2cmem_clock)); + save_item(NAME(m_flash_bank_select)); } void ssfindo_state::machine_reset() { - PS7500_reset(); + // ... } static INPUT_PORTS_START( ssfindo ) - PORT_START("PS7500") - PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_VBLANK("screen") - + // TODO: incomplete structs PORT_START("IN0") PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_UNUSED ) // IPT_START2 ?? PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) @@ -698,31 +460,28 @@ static INPUT_PORTS_START( ssfindo ) PORT_DIPNAME( 0x02, 0x02, DEF_STR( Free_Play ) ) PORT_DIPSETTING( 0x02, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) - PORT_DIPNAME( 0x80, 0x80, DEF_STR( Service_Mode ) ) - PORT_DIPSETTING( 0x80, DEF_STR( Off ) ) - PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x04, 0x04, "DSW 2" ) PORT_DIPSETTING( 0x04, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x08, 0x08, "DSW 3" ) PORT_DIPSETTING( 0x08, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) - PORT_DIPNAME( 0x10, 0x010, "DSW 4" ) + PORT_DIPNAME( 0x10, 0x10, "DSW 4" ) PORT_DIPSETTING( 0x10, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x20, 0x20, "DSW 5" ) PORT_DIPSETTING( 0x20, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) - PORT_DIPNAME( 0x40, 0x040, "DSW 6" ) + PORT_DIPNAME( 0x40, 0x40, "DSW 6" ) PORT_DIPSETTING( 0x40, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) + PORT_DIPNAME( 0x80, 0x80, DEF_STR( Service_Mode ) ) + PORT_DIPSETTING( 0x80, DEF_STR( Off ) ) + PORT_DIPSETTING( 0x00, DEF_STR( On ) ) INPUT_PORTS_END static INPUT_PORTS_START( ppcar ) - PORT_START("PS7500") - PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_VBLANK("screen") - PORT_START("IN0") PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_COIN1 ) @@ -737,9 +496,6 @@ static INPUT_PORTS_START( ppcar ) INPUT_PORTS_END static INPUT_PORTS_START( tetfight ) - PORT_START("PS7500") - PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_VBLANK("screen") - PORT_START("IN0") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN ) @@ -798,42 +554,46 @@ INPUT_PORTS_END void ssfindo_state::ssfindo(machine_config &config) { - /* basic machine hardware */ - ARM7(config, m_maincpu, 54000000); // guess... + ARM7(config, m_maincpu, 54000000); m_maincpu->set_addrmap(AS_PROGRAM, &ssfindo_state::ssfindo_map); - m_maincpu->set_vblank_int("screen", FUNC(ssfindo_state::interrupt)); I2C_24C01(config, m_i2cmem); - screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); - screen.set_refresh_hz(60); - screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */ - screen.set_size(320, 256); - screen.set_visarea(0, 319, 0, 239); - screen.set_screen_update(FUNC(ssfindo_state::screen_update)); - screen.set_palette(m_palette); - - PALETTE(config, m_palette).set_entries(256); + SCREEN(config, "screen", SCREEN_TYPE_RASTER); + + ARM_VIDC20(config, m_vidc, 24_MHz_XTAL); + m_vidc->set_screen("screen"); + m_vidc->vblank().set(m_iomd, FUNC(arm_iomd_device::vblank_irq)); + m_vidc->sound_drq().set(m_iomd, FUNC(arm_iomd_device::sound_drq)); + + ARM7500FE_IOMD(config, m_iomd, 54000000); + m_iomd->set_host_cpu_tag(m_maincpu); + m_iomd->set_vidc_tag(m_vidc); + m_iomd->iolines_read().set(FUNC(ssfindo_state::iolines_r)); + m_iomd->iolines_write().set(FUNC(ssfindo_state::iolines_w)); } void ssfindo_state::ppcar(machine_config &config) { ssfindo(config); - /* basic machine hardware */ m_maincpu->set_addrmap(AS_PROGRAM, &ssfindo_state::ppcar_map); config.device_remove("i2cmem"); } -void ssfindo_state::tetfight(machine_config &config) +void tetfight_state::tetfight(machine_config &config) { ppcar(config); - /* basic machine hardware */ - m_maincpu->set_addrmap(AS_PROGRAM, &ssfindo_state::tetfight_map); + m_maincpu->set_addrmap(AS_PROGRAM, &tetfight_state::tetfight_map); I2C_24C02(config, m_i2cmem); + + m_iomd->iocr_read_od<0>().set(FUNC(tetfight_state::iocr_od0_r)); + m_iomd->iocr_read_od<1>().set(FUNC(tetfight_state::iocr_od1_r)); + m_iomd->iocr_write_od<0>().set(FUNC(tetfight_state::iocr_od0_w)); + m_iomd->iocr_write_od<1>().set(FUNC(tetfight_state::iocr_od1_w)); } ROM_START( ssfindo ) @@ -909,11 +669,11 @@ ROM_END void ssfindo_state::init_common() { m_speedup = nullptr; - m_PS7500timer0 = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ssfindo_state::PS7500_Timer0_callback),this)); - m_PS7500timer1 = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ssfindo_state::PS7500_Timer1_callback),this)); + //m_PS7500timer0 = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ssfindo_state::PS7500_Timer0_callback),this)); + //m_PS7500timer1 = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ssfindo_state::PS7500_Timer1_callback),this)); - save_item(NAME(m_PS7500_IO)); - save_item(NAME(m_PS7500_FIFO)); + //save_item(NAME(m_PS7500_IO)); + //save_item(NAME(m_PS7500_FIFO)); } void ssfindo_state::init_ssfindo() @@ -921,12 +681,6 @@ void ssfindo_state::init_ssfindo() init_common(); m_flashType = 0; m_speedup = &ssfindo_state::ssfindo_speedups; - m_iocr_hack = 0; - - save_item(NAME(m_flashAdr)); - save_item(NAME(m_flashOffset)); - save_item(NAME(m_adrLatch)); - save_item(NAME(m_flashN)); } void ssfindo_state::init_ppcar() @@ -936,13 +690,12 @@ void ssfindo_state::init_ppcar() m_speedup = &ssfindo_state::ppcar_speedups; } -void ssfindo_state::init_tetfight() +void tetfight_state::init_tetfight() { init_common(); m_flashType = 0; - m_iocr_hack = 1; } GAME( 1999, ssfindo, 0, ssfindo, ssfindo, ssfindo_state, init_ssfindo, ROT0, "Icarus", "See See Find Out", MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1999, ppcar, 0, ppcar, ppcar, ssfindo_state, init_ppcar, ROT0, "Icarus", "Pang Pang Car", MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE ) -GAME( 2001, tetfight,0, tetfight, tetfight, ssfindo_state, init_tetfight, ROT0, "Sego", "Tetris Fighters", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) +GAME( 2001, tetfight,0, tetfight, tetfight, tetfight_state, init_tetfight, ROT0, "Sego", "Tetris Fighters", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) |