summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author fulivi <fulivi@users.noreply.github.com>2025-04-10 14:10:58 +0200
committer GitHub <noreply@github.com>2025-04-10 08:10:58 -0400
commitdefaf6283c4b5de1e493cf8cadfeea93209e89ba (patch)
treef0dc635ac88e555533a5eef250bd435be87f8022
parentb8f93608a7a79db7bd7649e4c639f73bc4d764b7 (diff)
MSX: add Video80 homebrew card (#13568)
* MSX: fixed vy0010 ROM image
-rw-r--r--hash/msx1_cart.xml15
-rw-r--r--scripts/src/bus.lua2
-rw-r--r--src/devices/bus/msx/cart/cartridge.cpp2
-rw-r--r--src/devices/bus/msx/cart/slotoptions.cpp2
-rw-r--r--src/devices/bus/msx/cart/slotoptions.h2
-rw-r--r--src/devices/bus/msx/cart/video80.cpp200
-rw-r--r--src/devices/bus/msx/cart/video80.h19
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