-
Notifications
You must be signed in to change notification settings - Fork 2.2k
esq5505.cpp: Add VFX-family ROM & EEPROM Cartridge support, and improve floppy support. #14444
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6897837
0fb58f4
9d13988
c1ec150
7712060
1455cef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -178,10 +178,12 @@ | |
| #include "sound/esqpump.h" | ||
| #include "emupal.h" | ||
| #include "speaker.h" | ||
| #include "vfxcart.h" | ||
|
|
||
| #include <cstdarg> | ||
| #include <cstdio> | ||
|
|
||
|
|
||
| // #define VERBOSE 1 | ||
| #include "logmacro.h" | ||
|
|
||
|
|
@@ -223,6 +225,7 @@ class esq5505_state : public driver_device | |
| , m_pump(*this, "pump") | ||
| , m_fdc(*this, "wd1772") | ||
| , m_floppy_connector(*this, "wd1772:0") | ||
| , m_cart(*this, "cart") | ||
| , m_panel(*this, "panel") | ||
| , m_dmac(*this, "mc68450") | ||
| , m_mdout(*this, "mdout") | ||
|
|
@@ -258,6 +261,7 @@ class esq5505_state : public driver_device | |
| required_device<esq_5505_5510_pump_device> m_pump; | ||
| optional_device<wd1772_device> m_fdc; | ||
| optional_device<floppy_connector> m_floppy_connector; | ||
| optional_device<ensoniq_vfx_cartridge> m_cart; | ||
| required_device<esqpanel_device> m_panel; | ||
| optional_device<hd63450_device> m_dmac; | ||
| required_device<midi_port_device> m_mdout; | ||
|
|
@@ -283,7 +287,18 @@ class esq5505_state : public driver_device | |
|
|
||
| int m_system_type = 0; | ||
| uint8_t m_duart_io = 0; | ||
|
|
||
| bool m_otis_irq = false; | ||
| bool m_floppy_dskchg = false; | ||
| bool m_docirq = false; | ||
| bool m_floppy_is_loaded = false; | ||
| bool m_floppy_is_active = false; | ||
| emu_timer *m_motor_on_timer; | ||
| emu_timer *m_dskchg_reset_timer; | ||
|
|
||
| TIMER_CALLBACK_MEMBER(floppy_motor_on); | ||
| TIMER_CALLBACK_MEMBER(floppy_dskchg_reset); | ||
|
|
||
| static void floppy_drives(device_slot_interface &device); | ||
| static void floppy_formats(format_registration &fr); | ||
|
|
||
| void eps_map(address_map &map) ATTR_COLD; | ||
|
|
@@ -294,15 +309,142 @@ class esq5505_state : public driver_device | |
| void cpu_space_map(address_map &map) ATTR_COLD; | ||
| void eps_cpu_space_map(address_map &map) ATTR_COLD; | ||
|
|
||
| void update_floppy_inputs(); | ||
| void floppy_loaded(bool loaded); | ||
| void floppy_load(floppy_image_device *floppy); | ||
| void floppy_unload(floppy_image_device *floppy); | ||
| void cartridge_loaded(bool loaded); | ||
| void cartridge_load(ensoniq_vfx_cartridge *cart); | ||
| void cartridge_unload(ensoniq_vfx_cartridge *cart); | ||
|
|
||
| void update_docirq_to_maincpu(); | ||
| void otis_irq(int irq); | ||
|
|
||
| uint16_t m_analog_values[8]; | ||
| }; | ||
|
|
||
| void esq5505_state::cartridge_loaded(bool loaded) | ||
| { | ||
| LOG("Cartridge %s\n", loaded ? "Inserted" : "Ejected"); | ||
| int state = loaded ? CLEAR_LINE : ASSERT_LINE; | ||
|
|
||
| // On VFX and later, DUART input bit 1 is 0 for cartridge present. | ||
| LOG("ip1 -> %d\n", state); | ||
| m_duart->ip1_w(state); | ||
| } | ||
|
|
||
| void esq5505_state::cartridge_load(ensoniq_vfx_cartridge *cart) | ||
| { | ||
| cartridge_loaded(true); | ||
| } | ||
|
|
||
| void esq5505_state::cartridge_unload(ensoniq_vfx_cartridge *cart) | ||
| { | ||
| cartridge_loaded(false); | ||
| } | ||
|
|
||
| void esq5505_state::floppy_drives(device_slot_interface &device) | ||
| { | ||
| device.option_add_internal("35dd", FLOPPY_35_DD); | ||
| } | ||
|
|
||
| void esq5505_state::floppy_formats(format_registration &fr) | ||
| { | ||
| fr.add_mfm_containers(); | ||
| fr.add(FLOPPY_ESQIMG_FORMAT); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's probably not a bad idea to add the HFE format too - that's become kind of the standard way to distribute synth floppies with nonstandard physical formats, since a lot of people still using real hardware have Gotek or similar drive replacements that use that format.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably a good idea, though the FlashFloppy firmware for Gotek drives explicitly supports the Ensoniq 800 kB format for Ensoniq hosts, so for these keyboards in particular, HFE may be less important. Either way I'd prefer to read up more on that before I make such a change, separately from this PR.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For reading Ensoniq floppies, the GreaseWeazle software also explicitly supports Ensoniq's floppy formats, so they can be directly read to .img files for use with a FlashFloppy Gotek drive, or here in MAME. |
||
| } | ||
|
|
||
| TIMER_CALLBACK_MEMBER(esq5505_state::floppy_motor_on) | ||
| { | ||
| bool motor_on = param; | ||
| if (m_floppy_connector) | ||
| { | ||
| floppy_image_device *floppy = m_floppy_connector->get_device(); | ||
| if (floppy) | ||
| { | ||
| floppy->mon_w(!motor_on); // active low | ||
| m_panel->set_floppy_active(motor_on); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| TIMER_CALLBACK_MEMBER(esq5505_state::floppy_dskchg_reset) | ||
| { | ||
| m_floppy_dskchg = !m_floppy_is_loaded; | ||
| LOG("Resetting floppy_dskchg -> %s\n", m_floppy_dskchg ? "true" : "false"); | ||
| update_docirq_to_maincpu(); | ||
| } | ||
|
|
||
| void esq5505_state::update_floppy_inputs() | ||
| { | ||
| // update the "Disk Ready" input | ||
| m_duart->ip0_w(m_floppy_is_active && m_floppy_is_loaded); | ||
|
|
||
| // Also update the DOC IRQ in case there's a pending disk change to handle. | ||
| update_docirq_to_maincpu(); | ||
| } | ||
|
|
||
| void esq5505_state::floppy_loaded(bool loaded) | ||
| { | ||
| if (m_floppy_connector) | ||
| { | ||
| m_floppy_is_loaded = loaded; | ||
| if (!loaded) | ||
| { | ||
| // Only set m_floppy_dskchg; it will be reset a short time after | ||
| // the disk has been enabled while m_floppy_dskchg is true. | ||
| m_floppy_dskchg = true; | ||
| } | ||
|
|
||
| LOG("Floppy %s\n", loaded ? "Inserted" : "Ejected"); | ||
| update_floppy_inputs(); | ||
| } | ||
| else | ||
| { | ||
| LOG("<No Floppy connector for loaded=%d>\n", loaded); | ||
| } | ||
| } | ||
|
|
||
| void esq5505_state::floppy_load(floppy_image_device *floppy) | ||
| { | ||
| floppy_loaded(true); | ||
| } | ||
|
|
||
| void esq5505_state::floppy_unload(floppy_image_device *floppy) | ||
| { | ||
| floppy_loaded(false); | ||
| } | ||
|
|
||
| void esq5505_state::update_docirq_to_maincpu() | ||
| { | ||
| bool floppy_dskchg_irq = m_floppy_is_active && m_floppy_dskchg; | ||
| if (floppy_dskchg_irq) | ||
| LOG("docirq (m68k_irq1) due to disk change = %d\n", floppy_dskchg_irq); | ||
| if (floppy_dskchg_irq && m_floppy_is_loaded) | ||
| { | ||
| // The drives that Ensoniq use only _pulse_ DSKCHG for a brief time, when a disk is in the drive. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this 100% verified? Most drives will raise DSKCHG on eject and then lower it the next time a seek happens with a disk in the drive, and MAME emulates that by default with the dskchg_r() method on the floppy image device.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think so; please see this comment on the previous attempt at these changes:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Huh. That's kind of wild.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, the floppy signal routing in general and the DSKCHG pulsing behaviour in particular were interesting to trace and track down. Being able to measure on real hardware and seeing the behaviour really helped confirm that was what was actually happening, even after reading about it in the FlashFloppy firmware discussion and documentation. |
||
| // schedule a reset. | ||
| LOG("Scheduling DSKCHG reset\n"); | ||
| m_dskchg_reset_timer->adjust(attotime::from_nsec(500)); | ||
| } | ||
| bool updated_irq = m_otis_irq || floppy_dskchg_irq; | ||
| if (updated_irq != m_docirq) | ||
| { | ||
| LOG("docirq (m68k_irq1) -> %d\n", updated_irq); | ||
| m_maincpu->set_input_line(M68K_IRQ_1, updated_irq); | ||
| m_docirq = updated_irq; | ||
| } | ||
| } | ||
|
|
||
| void esq5505_state::otis_irq(int irq) | ||
| { | ||
| if (irq != m_otis_irq) | ||
| { | ||
| m_otis_irq = irq; | ||
| update_docirq_to_maincpu(); | ||
| } | ||
| } | ||
|
|
||
| void esq5505_state::cpu_space_map(address_map &map) | ||
| { | ||
| map(0xfffff0, 0xffffff).m(m_maincpu, FUNC(m68000_base_device::autovectors_map)); | ||
|
|
@@ -317,11 +459,59 @@ void esq5505_state::eps_cpu_space_map(address_map &map) | |
|
|
||
| void esq5505_state::machine_start() | ||
| { | ||
| LOG("machine_start()\n"); | ||
| if (m_floppy_connector) { | ||
| floppy_image_device *floppy = m_floppy_connector->get_device(); | ||
| if (floppy) { | ||
| floppy->setup_load_cb(floppy_image_device::load_cb(&esq5505_state::floppy_load, this)); | ||
| floppy->setup_unload_cb(floppy_image_device::unload_cb(&esq5505_state::floppy_unload, this)); | ||
|
|
||
| m_motor_on_timer = timer_alloc(FUNC(esq5505_state::floppy_motor_on), this); | ||
| m_dskchg_reset_timer = timer_alloc(FUNC(esq5505_state::floppy_dskchg_reset), this); | ||
|
|
||
| // Set DSKCHG according to whether there is a floppy in the drive. | ||
| if (floppy->exists()) { | ||
| LOG("\nFloppy Drive has Floppy '%s'\n", floppy->filename()); | ||
| m_floppy_dskchg = false; | ||
| } else { | ||
| LOG("\nFloppy Drive has No Floppy\n"); | ||
| m_floppy_dskchg = true; | ||
| } | ||
| } else { | ||
| LOG("\nFloppy Drive has No Image Device!\n"); | ||
| } | ||
| } else { | ||
| LOG("\nNo Floppy Drive\n"); | ||
| } | ||
| if (m_cart) { | ||
| m_cart->setup_load_cb(ensoniq_vfx_cartridge::load_cb(&esq5505_state::cartridge_load, this)); | ||
| m_cart->setup_unload_cb(ensoniq_vfx_cartridge::unload_cb(&esq5505_state::cartridge_unload, this)); | ||
|
|
||
| if (m_cart->exists()) { | ||
| LOG("\nCartridge Slot has Cartridge '%s'\n", m_cart->filename()); | ||
| } else { | ||
| LOG("\nCartridge Slot has No Cartridge\n"); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void esq5505_state::machine_reset() | ||
| { | ||
| floppy_image_device *floppy = m_floppy_connector ? m_floppy_connector->get_device() : nullptr; | ||
| // Check our image devices for load status. | ||
| if (m_floppy_connector) { | ||
| floppy_image_device *floppy = m_floppy_connector->get_device(); | ||
| if (floppy && floppy->exists()) { | ||
| floppy_load(floppy); | ||
| } else { | ||
| floppy_unload(floppy); | ||
| } | ||
| } | ||
|
|
||
| if (m_cart && m_cart->exists()) { | ||
| cartridge_load(m_cart); | ||
| } else { | ||
| cartridge_unload(m_cart); | ||
| } | ||
|
|
||
| // Default analog values: all values are 10 bits, left-justified within 16 bits. | ||
| m_analog_values[0] = 0x7fc0; // pitch mod: start in the center | ||
|
|
@@ -332,27 +522,6 @@ void esq5505_state::machine_reset() | |
| m_analog_values[5] = 0xffc0; // Volume control: full on. | ||
| m_analog_values[6] = 0x7fc0; // Battery voltage: something reasonable. | ||
| m_analog_values[7] = 0x5540; // vRef to check battery. | ||
|
|
||
| // on VFX, bit 0 is 1 for 'cartridge present'. | ||
| // on VFX-SD and later, bit 0 is2 1 for floppy present, bit 1 is 1 for cartridge present | ||
| if (strcmp(machine().system().name, "vfx") == 0) | ||
| { | ||
| // todo: handle VFX cart-in when we support cartridges | ||
| m_duart->ip0_w(ASSERT_LINE); | ||
| } | ||
| else | ||
| { | ||
| m_duart->ip1_w(CLEAR_LINE); | ||
|
|
||
| if (floppy) | ||
| { | ||
| m_duart->ip0_w(CLEAR_LINE); | ||
| } | ||
| else | ||
| { | ||
| m_duart->ip0_w(ASSERT_LINE); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| uint16_t esq5505_state::lower_r(offs_t offset) | ||
|
|
@@ -396,6 +565,7 @@ void esq5505_state::vfx_map(address_map &map) | |
| map(0x200000, 0x20001f).rw("otis", FUNC(es5505_device::read), FUNC(es5505_device::write)); | ||
| map(0x280000, 0x28001f).rw(m_duart, FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0x00ff); | ||
| map(0x260000, 0x2601ff).rw(m_esp, FUNC(es5510_device::host_r), FUNC(es5510_device::host_w)).umask16(0x00ff); | ||
| map(0x2e0000, 0x2fffff).rw(m_cart, FUNC(ensoniq_vfx_cartridge::read), FUNC(ensoniq_vfx_cartridge::write)).umask16(0x00ff); | ||
| map(0xc00000, 0xc1ffff).rom().region("osrom", 0); | ||
| map(0xff0000, 0xffffff).ram().share("osram"); | ||
| } | ||
|
|
@@ -407,6 +577,7 @@ void esq5505_state::vfxsd_map(address_map &map) | |
| map(0x280000, 0x28001f).rw(m_duart, FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0x00ff); | ||
| map(0x260000, 0x2601ff).rw(m_esp, FUNC(es5510_device::host_r), FUNC(es5510_device::host_w)).umask16(0x00ff); | ||
| map(0x2c0000, 0x2c0007).rw(m_fdc, FUNC(wd1772_device::read), FUNC(wd1772_device::write)).umask16(0x00ff); | ||
| map(0x2e0000, 0x2fffff).rw(m_cart, FUNC(ensoniq_vfx_cartridge::read), FUNC(ensoniq_vfx_cartridge::write)).umask16(0x00ff); | ||
| map(0x330000, 0x37ffff).ram().share("seqram"); | ||
| map(0xc00000, 0xc3ffff).rom().region("osrom", 0); | ||
| map(0xff0000, 0xffffff).ram().share("osram"); | ||
|
|
@@ -469,7 +640,7 @@ void esq5505_state::duart_output(uint8_t data) | |
| VFX-SD & SD-1 (32): | ||
| bits 0/1/2 = analog sel | ||
| bit 3 = SSEL (disk side) | ||
| bit 4 = DSEL (drive select?) | ||
| bit 4 = DSEL (Drive Select and floppy Motor On) | ||
| bit 6 = ESPHALT | ||
| bit 7 = SACK (?) | ||
| */ | ||
|
|
@@ -499,7 +670,23 @@ void esq5505_state::duart_output(uint8_t data) | |
| } | ||
| else | ||
| { | ||
| floppy->ss_w(((data & 8)>>3)^1); | ||
| floppy->ss_w(!BIT(data, 3)); // bit 3, inverted -> floppy | ||
| m_floppy_is_active = BIT(data, 4); // bit 4 is used to activate the floppy: | ||
| if (m_floppy_is_active) | ||
| { | ||
| // immediately assert DISK SELECT (active low) | ||
| floppy->ds_w(CLEAR_LINE); | ||
| // but schedule a delayed MOTOR ON, since the keyboard constantly pulses this after a file has been read. | ||
| m_motor_on_timer->adjust(attotime::from_usec(100), 1); | ||
| } | ||
| else | ||
| { | ||
| // immediately deassert DISK SELECT (active low) | ||
| floppy->ds_w(ASSERT_LINE); | ||
| // but schedule a slightly delayed MOTOR OFF, since the keyboard sometimes seems to pulse this. | ||
| m_motor_on_timer->adjust(attotime::from_usec(50), 0); | ||
| } | ||
| update_floppy_inputs(); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -594,7 +781,7 @@ void esq5505_state::common(machine_config &config) | |
| m_otis->set_region0("waverom"); /* Bank 0 */ | ||
| m_otis->set_region1("waverom2"); /* Bank 1 */ | ||
| m_otis->set_channels(4); /* channels */ | ||
| m_otis->irq_cb().set_inputline(m_maincpu, M68K_IRQ_1); | ||
| m_otis->irq_cb().set(FUNC(esq5505_state::otis_irq)); | ||
| m_otis->read_port_cb().set(FUNC(esq5505_state::analog_r)); /* ADC */ | ||
| m_otis->add_route(0, "pump", 1.0, 0); | ||
| m_otis->add_route(1, "pump", 1.0, 1); | ||
|
|
@@ -610,6 +797,8 @@ void esq5505_state::vfx(machine_config &config, int panel_type) | |
| { | ||
| common(config); | ||
|
|
||
| ENSONIQ_VFX_CARTRIDGE(config, m_cart); | ||
|
|
||
| ESQPANEL2X40_VFX(config, m_panel, panel_type); | ||
| m_panel->write_tx().set(m_duart, FUNC(mc68681_device::rx_b_w)); | ||
| m_panel->write_analog().set(FUNC(esq5505_state::analog_w)); | ||
|
|
@@ -630,10 +819,7 @@ void esq5505_state::eps(machine_config &config) | |
| m_panel->write_analog().set(FUNC(esq5505_state::analog_w)); | ||
|
|
||
| WD1772(config, m_fdc, 8_MHz_XTAL); | ||
| FLOPPY_CONNECTOR(config, m_floppy_connector); | ||
| m_floppy_connector->option_add("35dd", FLOPPY_35_DD); | ||
| m_floppy_connector->set_default_option("35dd"); | ||
| m_floppy_connector->set_formats(esq5505_state::floppy_formats); | ||
| FLOPPY_CONNECTOR(config, m_floppy_connector, esq5505_state::floppy_drives, "35dd", esq5505_state::floppy_formats, true);//.enable_sound(true); | ||
|
|
||
| HD63450(config, m_dmac, 10_MHz_XTAL); // MC68450 compatible | ||
| m_dmac->set_cpu_tag(m_maincpu); | ||
|
|
@@ -656,10 +842,7 @@ void esq5505_state::vfxsd(machine_config &config, int panel_type) | |
| m_pump->add_route(3, "aux", 1.0, 1); | ||
|
|
||
| WD1772(config, m_fdc, 8000000); | ||
| FLOPPY_CONNECTOR(config, m_floppy_connector); | ||
| m_floppy_connector->option_add("35dd", FLOPPY_35_DD); | ||
| m_floppy_connector->set_default_option("35dd"); | ||
| m_floppy_connector->set_formats(esq5505_state::floppy_formats); | ||
| FLOPPY_CONNECTOR(config, m_floppy_connector, esq5505_state::floppy_drives, "35dd", esq5505_state::floppy_formats, true).enable_sound(true); | ||
| } | ||
|
|
||
| void esq5505_state::sd1(machine_config &config, int panel_type) | ||
|
|
@@ -724,7 +907,7 @@ void esq5505_state::common32(machine_config &config) | |
| m_otis->set_region0("waverom"); /* Bank 0 */ | ||
| m_otis->set_region1("waverom2"); /* Bank 1 */ | ||
| m_otis->set_channels(4); /* channels */ | ||
| m_otis->irq_cb().set_inputline(m_maincpu, M68K_IRQ_1); | ||
| m_otis->irq_cb().set(FUNC(esq5505_state::otis_irq)); | ||
| m_otis->read_port_cb().set(FUNC(esq5505_state::analog_r)); /* ADC */ | ||
| m_otis->add_route(0, "pump", 1.0, 0); | ||
| m_otis->add_route(1, "pump", 1.0, 1); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A thought for future work: having more than one optional_device often means it would be cleaner to have a base class with nothing optional and then various inherited child classes that extend that in various ways. Back when I wrote esq5505 originally I wasn't in that mindset and it does show.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be worth looking at this for a refactor more generally. The EPS Classic, for example, doesn't really belong here at all: it has no es5510 ESP, and the sound generator is the es5504 "DOC II", not the es5505 "OTIS".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, for sure.