MSX: add Video80 homebrew card (#13568)

* MSX: fixed vy0010 ROM image
This commit is contained in:
fulivi 2025-04-10 14:10:58 +02:00 committed by GitHub
parent b8f93608a7
commit defaf6283c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 237 additions and 5 deletions

View File

@ -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.

View File

@ -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",

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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