diff options
author | 2025-04-10 14:10:58 +0200 | |
---|---|---|
committer | 2025-04-10 08:10:58 -0400 | |
commit | defaf6283c4b5de1e493cf8cadfeea93209e89ba (patch) | |
tree | f0dc635ac88e555533a5eef250bd435be87f8022 | |
parent | b8f93608a7a79db7bd7649e4c639f73bc4d764b7 (diff) |
MSX: add Video80 homebrew card (#13568)
* MSX: fixed vy0010 ROM image
-rw-r--r-- | hash/msx1_cart.xml | 15 | ||||
-rw-r--r-- | scripts/src/bus.lua | 2 | ||||
-rw-r--r-- | src/devices/bus/msx/cart/cartridge.cpp | 2 | ||||
-rw-r--r-- | src/devices/bus/msx/cart/slotoptions.cpp | 2 | ||||
-rw-r--r-- | src/devices/bus/msx/cart/slotoptions.h | 2 | ||||
-rw-r--r-- | src/devices/bus/msx/cart/video80.cpp | 200 | ||||
-rw-r--r-- | src/devices/bus/msx/cart/video80.h | 19 |
7 files changed, 237 insertions, 5 deletions
diff --git a/hash/msx1_cart.xml b/hash/msx1_cart.xml index bbc3f370f58..0b0fc69cde9 100644 --- a/hash/msx1_cart.xml +++ b/hash/msx1_cart.xml @@ -19097,9 +19097,7 @@ legacy FM implementations cannot find it. <part name="cart" interface="msx_cart"> <feature name="slot" value="disk_vy0010" /> <dataarea name="rom" size="0x4000"> - <!-- Dump contains contents of the memory mapped FDC registers --> - <!-- ROM label taken from schematic, possibly incorrect --> - <rom name="27128q25-8.ic2" size="0x4000" crc="164f5a6d" sha1="8924e3e11eb1c8c1edcb7efa63c26d2bdc142473" status="baddump" /> + <rom name="vy0010_jvc.rom" size="0x4000" crc="4ad08ce4" sha1="28b683d5f6fd4a59046d5a8843233596fced104a" /> </dataarea> </part> </software> @@ -27681,6 +27679,17 @@ legacy FM implementations cannot find it. </part> </software> + <software name="video80"> + <description>Video-80 80-column video card</description> + <year>1988</year> + <publisher>F.Ulivi</publisher> + <part name="cart" interface="msx_cart"> + <feature name="slot" value="video80" /> + <dataarea name="rom" size="0x1000"> + <rom name="video80_101.bin" size="0x1000" crc="9b7e4743" sha1="bba13511c8d4f1efb753e653da1691c0fad08eb3" /> + </dataarea> + </part> + </software> <!-- Sets below are hacked games "converted" from their original format into cartridge. diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua index 9c4c6e72f97..1ef192b9890 100644 --- a/scripts/src/bus.lua +++ b/scripts/src/bus.lua @@ -2108,6 +2108,8 @@ if (BUSES["MSX_SLOT"]~=null) then MAME_DIR .. "src/devices/bus/msx/cart/superloderunner.h", MAME_DIR .. "src/devices/bus/msx/cart/super_swangi.cpp", MAME_DIR .. "src/devices/bus/msx/cart/super_swangi.h", + MAME_DIR .. "src/devices/bus/msx/cart/video80.h", + MAME_DIR .. "src/devices/bus/msx/cart/video80.cpp", MAME_DIR .. "src/devices/bus/msx/cart/yamaha_ucn01.cpp", MAME_DIR .. "src/devices/bus/msx/cart/yamaha_ucn01.h", MAME_DIR .. "src/devices/bus/msx/minicart/minicart.cpp", diff --git a/src/devices/bus/msx/cart/cartridge.cpp b/src/devices/bus/msx/cart/cartridge.cpp index 030e0a96475..f452bd45ac2 100644 --- a/src/devices/bus/msx/cart/cartridge.cpp +++ b/src/devices/bus/msx/cart/cartridge.cpp @@ -42,6 +42,7 @@ #include "super_swangi.h" #include "superloderunner.h" #include "yamaha_ucn01.h" +#include "video80.h" #include "bus/msx/slot/cartridge.h" @@ -100,6 +101,7 @@ void msx_cart(device_slot_interface &device, bool is_in_subslot) device.option_add_internal(slotoptions::SUPERLODERUNNER, MSX_CART_SUPERLODERUNNER); device.option_add_internal(slotoptions::SYNTHESIZER, MSX_CART_SYNTHESIZER); device.option_add_internal(slotoptions::EC701, MSX_CART_EC701); + device.option_add_internal(slotoptions::VIDEO80, MSX_CART_VIDEO80); device.option_add(slotoptions::BEEPACK, MSX_CART_BEEPACK); device.option_add(slotoptions::BM_012, MSX_CART_BM_012); device.option_add(slotoptions::FRANKY, MSX_CART_FRANKY); diff --git a/src/devices/bus/msx/cart/slotoptions.cpp b/src/devices/bus/msx/cart/slotoptions.cpp index 7dab963dc4c..61e44463cb4 100644 --- a/src/devices/bus/msx/cart/slotoptions.cpp +++ b/src/devices/bus/msx/cart/slotoptions.cpp @@ -89,6 +89,6 @@ char const *const SUPER_SWANGI = "super_swangi"; char const *const SUPERLODERUNNER = "superloderunner"; char const *const SYNTHESIZER = "synthesizer"; char const *const UCN01 = "ucn01"; - +char const *const VIDEO80 = "video80"; } // namespace bus::msx::cart::slotoptions diff --git a/src/devices/bus/msx/cart/slotoptions.h b/src/devices/bus/msx/cart/slotoptions.h index c47a1006b86..d5bf0586de3 100644 --- a/src/devices/bus/msx/cart/slotoptions.h +++ b/src/devices/bus/msx/cart/slotoptions.h @@ -92,7 +92,7 @@ extern char const *const SUPER_SWANGI; extern char const *const SUPERLODERUNNER; extern char const *const SYNTHESIZER; extern char const *const UCN01; - +extern char const *const VIDEO80; } // namespace bus::msx::cart::slotoptions #endif // MAME_BUS_MSX_CART_SLOTOPTIONS_H diff --git a/src/devices/bus/msx/cart/video80.cpp b/src/devices/bus/msx/cart/video80.cpp new file mode 100644 index 00000000000..cac478bf88b --- /dev/null +++ b/src/devices/bus/msx/cart/video80.cpp @@ -0,0 +1,200 @@ +// license:BSD-3-Clause +// copyright-holders:F. Ulivi + +/********************************************************************* + + VIDEO80 homebrew 80-column video card + + This is a 80-column video card that I designed and built in 1988. + Here's a summary of its features: + - 80 columns by 24 rows of text video + - 8x8 character cell + - 16 MHz dot clock, 50 Hz frame rate + - Based on MC6845 CRT controller + - 4k of SRAM: half for the framebuffer & half for the character + generator. + - 4k of EPROM: it contains the firmware and the compressed font. + FW supports seamless operation in both BASIC and MSXDOS + environments. No driver or additional sw is needed. + +*********************************************************************/ + +#include "emu.h" +#include "video80.h" +#include "cpu/z80/z80.h" +#include "video/mc6845.h" + +#include "emupal.h" +#include "screen.h" + +// Debugging +#define VERBOSE 0 +#include "logmacro.h" + +namespace { + +// Dot clock +constexpr auto DOT_CLOCK = 16_MHz_XTAL; + +class msx_cart_video80_device : public device_t, public msx_cart_interface +{ +public: + msx_cart_video80_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, MSX_CART_VIDEO80, tag, owner, clock) + , msx_cart_interface(mconfig, *this) + , m_palette(*this, "palette") + , m_crtc(*this, "crtc") + , m_screen(*this, "screen1") + , m_cpu_waiting(false) + {} + + virtual std::error_condition initialize_cartridge(std::string &message) override; + +protected: + virtual void device_start() override ATTR_COLD; + virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + + void de_w(int state); + uint8_t vram_r(offs_t addr); + void vram_w(offs_t addr, uint8_t data); + uint8_t crtc_r(offs_t addr); + void crtc_w(offs_t addr, uint8_t data); + +private: + static inline constexpr unsigned VRAM_SIZE = 4096; + + required_device<palette_device> m_palette; + required_device<mc6845_device> m_crtc; + required_device<screen_device> m_screen; + + uint8_t m_vram[ VRAM_SIZE ]; + bool m_cpu_waiting; + + MC6845_UPDATE_ROW(crtc_update_row); +}; + +std::error_condition msx_cart_video80_device::initialize_cartridge(std::string &message) +{ + if (!cart_rom_region()) { + message = "msx_cart_video80_device: Required region 'rom' was not found."; + return image_error::INTERNAL; + } + + if (cart_rom_region()->bytes() != 0x1000) { + message = "msx_cart_video80_device: Region 'rom' has invalid size."; + return image_error::INVALIDLENGTH; + } + + page(1)->install_rom(0x4000, 0x4fff, cart_rom_region()->base()); + page(1)->install_read_handler(0x5000, 0x5fff, emu::rw_delegate(*this, FUNC(msx_cart_video80_device::vram_r))); + page(1)->install_write_handler(0x5000, 0x5fff, emu::rw_delegate(*this, FUNC(msx_cart_video80_device::vram_w))); + page(1)->install_read_handler(0x6000, 0x6fff, emu::rw_delegate(*this, FUNC(msx_cart_video80_device::crtc_r))); + page(1)->install_write_handler(0x6000, 0x6fff, emu::rw_delegate(*this, FUNC(msx_cart_video80_device::crtc_w))); + + return std::error_condition(); +} + +void msx_cart_video80_device::device_start() +{ + save_item(NAME(m_vram)); + save_item(NAME(m_cpu_waiting)); +} + +void msx_cart_video80_device::device_add_mconfig(machine_config &config) +{ + PALETTE(config, m_palette, palette_device::MONOCHROME); + + SCREEN(config, m_screen, SCREEN_TYPE_RASTER, rgb_t::green()); + m_screen->set_raw(DOT_CLOCK, 1024, 0, 640, 312, 0, 192); + m_screen->set_screen_update(m_crtc, FUNC(mc6845_device::screen_update)); + + MC6845(config, m_crtc, DOT_CLOCK / 8); + m_crtc->set_char_width(8); + m_crtc->set_show_border_area(false); + m_crtc->set_screen(m_screen); + m_crtc->set_update_row_callback(FUNC(msx_cart_video80_device::crtc_update_row)); + m_crtc->out_de_callback().set(FUNC(msx_cart_video80_device::de_w)); +} + +void msx_cart_video80_device::de_w(int state) +{ + if (m_cpu_waiting && !state) { + maincpu().set_input_line(Z80_INPUT_LINE_WAIT, CLEAR_LINE); + m_cpu_waiting = false; + } +} + +uint8_t msx_cart_video80_device::vram_r(offs_t addr) +{ + uint8_t res = 0; + + if (!machine().side_effects_disabled() && m_crtc->de_r()) { + maincpu().set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE); + maincpu().defer_access(); + m_cpu_waiting = true; + } else { + res = m_vram[ addr ]; + } + return res; +} + +void msx_cart_video80_device::vram_w(offs_t addr, uint8_t data) +{ + if (!machine().side_effects_disabled() && m_crtc->de_r()) { + maincpu().set_input_line(Z80_INPUT_LINE_WAIT, ASSERT_LINE); + maincpu().defer_access(); + m_cpu_waiting = true; + } else { + m_vram[ addr ] = data; + } +} + +uint8_t msx_cart_video80_device::crtc_r(offs_t addr) +{ + uint8_t res = 0; + + if (BIT(addr, 1)) { + if (BIT(addr, 0)) { + res = m_crtc->register_r(); + } else { + LOG("Reading from non-existing reg!\n"); + } + } else { + LOG("Reading from a write-only address!\n"); + } + + return res; +} + +void msx_cart_video80_device::crtc_w(offs_t addr, uint8_t data) +{ + if (BIT(addr, 1)) { + LOG("Writing to a read-only address!\n"); + } else { + if (BIT(addr, 0)) { + m_crtc->register_w(data); + } else { + m_crtc->address_w(data); + } + } +} + +MC6845_UPDATE_ROW(msx_cart_video80_device::crtc_update_row) +{ + const pen_t *pen = m_palette->pens(); + for (int i = 0; i < x_count; i++) { + uint8_t char_code = m_vram[ (ma + i) & 0x7ff ]; + uint8_t pixels = m_vram[ (unsigned(char_code) << 3) | 0x800 | (ra & 7) ]; + bool cursor = cursor_x == i; + if (cursor) { + pixels = ~pixels; + } + for (unsigned col = 0; col < 8; col++) { + bitmap.pix(y, i * 8 + col) = pen[ BIT(pixels, 7 - col) ]; + } + } +} + +} // anonymous namespace + +DEFINE_DEVICE_TYPE_PRIVATE(MSX_CART_VIDEO80, msx_cart_interface, msx_cart_video80_device, "msx_cart_video80", "MSX Cartridge - VIDEO80") diff --git a/src/devices/bus/msx/cart/video80.h b/src/devices/bus/msx/cart/video80.h new file mode 100644 index 00000000000..ee7a8d522ef --- /dev/null +++ b/src/devices/bus/msx/cart/video80.h @@ -0,0 +1,19 @@ +// license:BSD-3-Clause +// copyright-holders:F. Ulivi + +/********************************************************************* + + VIDEO80 homebrew 80-column video card + +*********************************************************************/ + +#ifndef MAME_BUS_MSX_CART_VIDEO80_H +#define MAME_BUS_MSX_CART_VIDEO80_H + +#pragma once + +#include "bus/msx/slot/cartridge.h" + +DECLARE_DEVICE_TYPE(MSX_CART_VIDEO80, msx_cart_interface) + +#endif // MAME_BUS_MSX_CART_VIDEO80_H |