From b2aa336249ad052653ae94d7c6936e7ae5943619 Mon Sep 17 00:00:00 2001 From: Sandro Ronco Date: Sun, 8 May 2022 14:00:53 +0200 Subject: Z88: added serial port and NVRAM support. [Sandro Ronco] --- scripts/target/mame/mess.lua | 2 + src/devices/bus/z88/flash.cpp | 19 ++- src/devices/bus/z88/flash.h | 4 + src/devices/bus/z88/ram.cpp | 5 +- src/devices/bus/z88/ram.h | 6 + src/devices/bus/z88/rom.cpp | 28 ++- src/devices/bus/z88/rom.h | 13 +- src/devices/bus/z88/z88.cpp | 28 +++ src/devices/bus/z88/z88.h | 2 + src/mame/drivers/z88.cpp | 183 ++++++++------------ src/mame/includes/z88.h | 48 ++---- src/mame/machine/upd65031.cpp | 366 +++++++++++++++++++++++++++++++--------- src/mame/machine/upd65031.h | 27 ++- src/mame/machine/z88_impexp.cpp | 273 ++++++++++++++++++++++++++++++ src/mame/machine/z88_impexp.h | 70 ++++++++ src/mame/video/z88.cpp | 52 ++---- 16 files changed, 859 insertions(+), 267 deletions(-) create mode 100644 src/mame/machine/z88_impexp.cpp create mode 100644 src/mame/machine/z88_impexp.h diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index e53e9a96958..30879e781c9 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -1705,6 +1705,8 @@ files { MAME_DIR .. "src/mame/includes/z88.h", MAME_DIR .. "src/mame/machine/upd65031.cpp", MAME_DIR .. "src/mame/machine/upd65031.h", + MAME_DIR .. "src/mame/machine/z88_impexp.cpp", + MAME_DIR .. "src/mame/machine/z88_impexp.h", MAME_DIR .. "src/mame/video/z88.cpp", } diff --git a/src/devices/bus/z88/flash.cpp b/src/devices/bus/z88/flash.cpp index c769530b1de..bda80f4cff6 100644 --- a/src/devices/bus/z88/flash.cpp +++ b/src/devices/bus/z88/flash.cpp @@ -37,9 +37,26 @@ z88_1024k_flash_device::z88_1024k_flash_device(const machine_config &mconfig, co : device_t(mconfig, Z88_1024K_FLASH, tag, owner, clock) , device_z88cart_interface(mconfig, *this) , m_flash(*this, FLASH_TAG) + , m_region(*this, FLASH_TAG) { } +//------------------------------------------------- +// rom_region - device-specific ROM region +//------------------------------------------------- + +ROM_START( z88_1024k_flash ) + ROM_REGION( 0x100000, FLASH_TAG, ROMREGION_ERASEFF ) + // this region is required to initialize the flash device with the data loaded from the cartridge interface +ROM_END + + +const tiny_rom_entry *z88_1024k_flash_device::device_rom_region() const +{ + return ROM_NAME( z88_1024k_flash ); +} + + //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- @@ -64,7 +81,7 @@ void z88_1024k_flash_device::device_add_mconfig(machine_config &config) uint8_t* z88_1024k_flash_device::get_cart_base() { - return m_flash->base(); + return m_region->base(); } /*------------------------------------------------- diff --git a/src/devices/bus/z88/flash.h b/src/devices/bus/z88/flash.h index f92a9448d1b..12d3bf7b825 100644 --- a/src/devices/bus/z88/flash.h +++ b/src/devices/bus/z88/flash.h @@ -26,6 +26,9 @@ protected: virtual void device_add_mconfig(machine_config &config) override; virtual void device_start() override; + // optional information overrides + virtual const tiny_rom_entry *device_rom_region() const override; + // z88cart_interface overrides virtual uint8_t read(offs_t offset) override; virtual void write(offs_t offset, uint8_t data) override; @@ -34,6 +37,7 @@ protected: private: required_device m_flash; + required_memory_region m_region; }; // device type definition diff --git a/src/devices/bus/z88/ram.cpp b/src/devices/bus/z88/ram.cpp index 9c259260c58..6ba311908f9 100644 --- a/src/devices/bus/z88/ram.cpp +++ b/src/devices/bus/z88/ram.cpp @@ -38,7 +38,10 @@ z88_32k_ram_device::z88_32k_ram_device(const machine_config &mconfig, const char } z88_32k_ram_device::z88_32k_ram_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, type, tag, owner, clock), device_z88cart_interface(mconfig, *this), m_ram(nullptr) + : device_t(mconfig, type, tag, owner, clock) + , device_nvram_interface(mconfig, *this) + , device_z88cart_interface(mconfig, *this) + , m_ram(nullptr) { } diff --git a/src/devices/bus/z88/ram.h b/src/devices/bus/z88/ram.h index 3a304bf80ea..96aafd82e24 100644 --- a/src/devices/bus/z88/ram.h +++ b/src/devices/bus/z88/ram.h @@ -14,6 +14,7 @@ // ======================> z88_32k_ram_device class z88_32k_ram_device : public device_t, + public device_nvram_interface, public device_z88cart_interface { public: @@ -26,6 +27,11 @@ protected: // device-level overrides virtual void device_start() override; + // device_nvram_interface overrides + virtual void nvram_default() override { } + virtual bool nvram_read(util::read_stream &file) override { size_t actual; return !file.read (get_cart_base(), get_cart_size(), actual) && actual == get_cart_size(); } + virtual bool nvram_write(util::write_stream &file) override { size_t actual; return !file.write(get_cart_base(), get_cart_size(), actual) && actual == get_cart_size(); } + // z88cart_interface overrides virtual uint8_t read(offs_t offset) override; virtual void write(offs_t offset, uint8_t data) override; diff --git a/src/devices/bus/z88/rom.cpp b/src/devices/bus/z88/rom.cpp index c265d99dfc9..3d28a0a38b8 100644 --- a/src/devices/bus/z88/rom.cpp +++ b/src/devices/bus/z88/rom.cpp @@ -37,7 +37,12 @@ z88_32k_rom_device::z88_32k_rom_device(const machine_config &mconfig, const char } z88_32k_rom_device::z88_32k_rom_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, type, tag, owner, clock), device_z88cart_interface(mconfig, *this), m_rom(nullptr) + : device_t(mconfig, type, tag, owner, clock) + , device_nvram_interface(mconfig, *this) + , device_z88cart_interface(mconfig, *this) + , m_rom(nullptr) + , m_vpp_state(0) + , m_modified(false) { } @@ -66,6 +71,10 @@ z88_256k_rom_device::z88_256k_rom_device(const machine_config &mconfig, const ch void z88_32k_rom_device::device_start() { m_rom = machine().memory().region_alloc(tag(), get_cart_size(), 1, ENDIANNESS_LITTLE)->base(); + std::fill_n(m_rom, get_cart_size(), 0xff); + + save_item(NAME(m_vpp_state)); + save_item(NAME(m_modified)); } /*------------------------------------------------- @@ -85,3 +94,20 @@ uint8_t z88_32k_rom_device::read(offs_t offset) { return m_rom[offset & (get_cart_size() - 1)]; } + +/*------------------------------------------------- + write +-------------------------------------------------*/ + +void z88_32k_rom_device::write(offs_t offset, uint8_t data) +{ + if (m_vpp_state) + { + const uint32_t offset_mask = get_cart_size() - 1; + if (m_rom[offset & offset_mask] & ~data) + m_modified = true; + + m_rom[offset & offset_mask] &= data; + } +} + diff --git a/src/devices/bus/z88/rom.h b/src/devices/bus/z88/rom.h index 41598dcb869..bcd4be06430 100644 --- a/src/devices/bus/z88/rom.h +++ b/src/devices/bus/z88/rom.h @@ -14,6 +14,7 @@ // ======================> z88_32k_rom_device class z88_32k_rom_device : public device_t, + public device_nvram_interface, public device_z88cart_interface { public: @@ -26,14 +27,24 @@ protected: // device-level overrides virtual void device_start() override; + // device_nvram_interface overrides + virtual void nvram_default() override { } + virtual bool nvram_read(util::read_stream &file) override { size_t actual; return !file.read (get_cart_base(), get_cart_size(), actual) && actual == get_cart_size(); } + virtual bool nvram_write(util::write_stream &file) override { size_t actual; return !file.write(get_cart_base(), get_cart_size(), actual) && actual == get_cart_size(); } + virtual bool nvram_can_write() override { return m_modified; } // Save only if the EPROM has been programmed + // z88cart_interface overrides virtual uint8_t read(offs_t offset) override; + virtual void write(offs_t offset, uint8_t data) override; + virtual void vpp_w(int state) override { m_vpp_state = state; } virtual uint8_t* get_cart_base() override; virtual uint32_t get_cart_size() override { return 0x8000; } protected: // internal state uint8_t * m_rom; + int m_vpp_state; + bool m_modified; }; // ======================> z88_128k_rom_device @@ -59,7 +70,7 @@ public: protected: // z88cart_interface overrides - virtual uint32_t get_cart_size() override { return 0x200000; } + virtual uint32_t get_cart_size() override { return 0x40000; } }; // device type definition diff --git a/src/devices/bus/z88/z88.cpp b/src/devices/bus/z88/z88.cpp index ba4cc9632fa..65c71c8d686 100644 --- a/src/devices/bus/z88/z88.cpp +++ b/src/devices/bus/z88/z88.cpp @@ -162,6 +162,24 @@ void z88cart_slot_device::call_unload() std::string z88cart_slot_device::get_default_card_software(get_default_card_software_hook &hook) const { + // select the correct slot device for the ROM size + if (hook.image_file()) + { + uint64_t size; + std::error_condition err = hook.image_file()->length(size); + + if (!err) + { + if (size <= 0x8000) return std::string("32krom"); + if (size <= 0x20000) return std::string("128krom"); + if (size <= 0x40000) return std::string("256krom"); + if (size <= 0x100000) return std::string("1024kflash"); + fatalerror("%s: unsupported ROM size 0x%06x", tag(), size); + } + else + fatalerror("%s: %s:%d %s\n", tag(), err.category().name(), err.value(), err.message()); + } + return software_get_default_slot("128krom"); } @@ -189,6 +207,16 @@ void z88cart_slot_device::write(offs_t offset, uint8_t data) m_cart->write(offset, data); } +/*------------------------------------------------- + set EPROM programming voltage to slot 3 +-------------------------------------------------*/ + +void z88cart_slot_device::vpp_w(int state) +{ + if (m_cart) + m_cart->vpp_w(state); +} + /*------------------------------------------------- get_cart_base diff --git a/src/devices/bus/z88/z88.h b/src/devices/bus/z88/z88.h index 04c9b35fcf4..f4f655f1e95 100644 --- a/src/devices/bus/z88/z88.h +++ b/src/devices/bus/z88/z88.h @@ -74,6 +74,7 @@ public: // reading and writing virtual uint8_t read(offs_t offset) { return 0xff; } virtual void write(offs_t offset, uint8_t data) { } + virtual void vpp_w(int state) { } virtual uint8_t* get_cart_base() { return nullptr; } virtual uint32_t get_cart_size() { return 0; } @@ -117,6 +118,7 @@ public: // reading and writing uint8_t read(offs_t offset); void write(offs_t offset, uint8_t data); + void vpp_w(int state); uint8_t* get_cart_base(); protected: diff --git a/src/mame/drivers/z88.cpp b/src/mame/drivers/z88.cpp index daf98ef8db8..789ad19283b 100644 --- a/src/mame/drivers/z88.cpp +++ b/src/mame/drivers/z88.cpp @@ -2,17 +2,15 @@ // copyright-holders:Kevin Thacker,Sandro Ronco /****************************************************************************** - z88.c + z88.cpp z88 Notepad computer system driver TODO: - - speaker controlled by txd - cartridges should be hot swappable - expansion interface - - serial port Kevin Thacker [MESS driver] @@ -46,110 +44,37 @@ explains why the extra checks are done */ -// cartridges read -uint8_t z88_state::bank0_cart_r(offs_t offset) { return m_carts[m_bank[0].slot]->read((m_bank[0].page<<14) + offset); } -uint8_t z88_state::bank1_cart_r(offs_t offset) { return m_carts[m_bank[1].slot]->read((m_bank[1].page<<14) + offset); } -uint8_t z88_state::bank2_cart_r(offs_t offset) { return m_carts[m_bank[2].slot]->read((m_bank[2].page<<14) + offset); } -uint8_t z88_state::bank3_cart_r(offs_t offset) { return m_carts[m_bank[3].slot]->read((m_bank[3].page<<14) + offset); } - -// cartridges write -void z88_state::bank0_cart_w(offs_t offset, uint8_t data) { m_carts[m_bank[0].slot]->write((m_bank[0].page<<14) + offset, data); } -void z88_state::bank1_cart_w(offs_t offset, uint8_t data) { m_carts[m_bank[1].slot]->write((m_bank[1].page<<14) + offset, data); } -void z88_state::bank2_cart_w(offs_t offset, uint8_t data) { m_carts[m_bank[2].slot]->write((m_bank[2].page<<14) + offset, data); } -void z88_state::bank3_cart_w(offs_t offset, uint8_t data) { m_carts[m_bank[3].slot]->write((m_bank[3].page<<14) + offset, data); } - - UPD65031_MEMORY_UPDATE(z88_state::bankswitch_update) { - // bank 0 is always even - if (bank == 0) page &= 0xfe; - - if (page < 0x20) // internal ROM - { - // install read bank - if (m_bank_type[bank] != Z88_BANK_ROM) - { - m_maincpu->space(AS_PROGRAM).install_read_bank(bank<<14, (bank<<14) + 0x3fff, m_banks[bank + 1]); - m_maincpu->space(AS_PROGRAM).unmap_write(bank<<14, (bank<<14) + 0x3fff); - m_bank_type[bank] = Z88_BANK_ROM; - } - - m_banks[bank + 1]->set_entry(page); - } - else if (page < 0x40) // internal RAM - { - if((page & 0x1f) < (m_ram->size()>>14)) - { - // install readwrite bank - if (m_bank_type[bank] != Z88_BANK_RAM) - { - m_maincpu->space(AS_PROGRAM).install_readwrite_bank(bank<<14, (bank<<14) + 0x3fff, m_banks[bank + 1]); - m_bank_type[bank] = Z88_BANK_RAM; - } - - // set the bank - m_banks[bank + 1]->set_entry(page); - } - else - { - if (m_bank_type[bank] != Z88_BANK_UNMAP) - { - m_maincpu->space(AS_PROGRAM).unmap_readwrite(bank<<14, (bank<<14) + 0x3fff); - m_bank_type[bank] = Z88_BANK_UNMAP; - } - } - } - else // cartridges - { - m_bank[bank].slot = (page >> 6) & 3; - m_bank[bank].page = page & 0x3f; - - if (m_bank_type[bank] != Z88_BANK_CART) - { - switch (bank) - { - case 0: - m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x0000, 0x3fff, read8sm_delegate(*this, FUNC(z88_state::bank0_cart_r)), write8sm_delegate(*this, FUNC(z88_state::bank0_cart_w))); - break; - case 1: - m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x4000, 0x7fff, read8sm_delegate(*this, FUNC(z88_state::bank1_cart_r)), write8sm_delegate(*this, FUNC(z88_state::bank1_cart_w))); - break; - case 2: - m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0x8000, 0xbfff, read8sm_delegate(*this, FUNC(z88_state::bank2_cart_r)), write8sm_delegate(*this, FUNC(z88_state::bank2_cart_w))); - break; - case 3: - m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0xc000, 0xffff, read8sm_delegate(*this, FUNC(z88_state::bank3_cart_r)), write8sm_delegate(*this, FUNC(z88_state::bank3_cart_w))); - break; - } - - m_bank_type[bank] = Z88_BANK_CART; - } - } - - - // override setting for lower 8k of bank 0 if (bank == 0) { - m_maincpu->space(AS_PROGRAM).install_read_bank(0, 0x1fff, m_banks[0]); - - // enable RAM - if (rams) - m_maincpu->space(AS_PROGRAM).install_write_bank(0, 0x1fff, m_banks[0]); - else - m_maincpu->space(AS_PROGRAM).unmap_write(0, 0x1fff); - - m_banks[0]->set_entry(rams & 1); + // bank 0 is only 8k (0x2000 - 0x3fff) and bit 0 is used to select the upper/lower + // part of a 16k page, for this reason only even pages can be mapped in this bank. + m_banks[bank]->set_bank(((page & 0xfe) << 1) | (page & 1)); + m_boot_view.select(rams ? 1 : 0); } + else + m_banks[bank]->set_bank(page); } +void z88_state::z88_map(address_map &map) +{ + map(0x000000, 0x07ffff).rom().region("bios", 0); + map(0x080000, 0x0fffff).rw(m_ram, FUNC(ram_device::read), FUNC(ram_device::write)); + map(0x100000, 0x1fffff).rw(m_carts[1], FUNC(z88cart_slot_device::read), FUNC(z88cart_slot_device::write)); + map(0x200000, 0x2fffff).rw(m_carts[2], FUNC(z88cart_slot_device::read), FUNC(z88cart_slot_device::write)); + map(0x300000, 0x3fffff).rw(m_carts[3], FUNC(z88cart_slot_device::read), FUNC(z88cart_slot_device::write)); +} void z88_state::z88_mem(address_map &map) { - map(0x0000, 0x1fff).bankrw(m_banks[0]); - map(0x2000, 0x3fff).bankrw(m_banks[1]); - map(0x4000, 0x7fff).bankrw(m_banks[2]); - map(0x8000, 0xbfff).bankrw(m_banks[3]); - map(0xc000, 0xffff).bankrw(m_banks[4]); + map(0x0000, 0x1fff).view(m_boot_view); + m_boot_view[0](0x0000, 0x1fff).rom().region("bios", 0); + m_boot_view[1](0x0000, 0x1fff).rw(m_ram, FUNC(ram_device::read), FUNC(ram_device::write)); + map(0x2000, 0x3fff).rw(m_banks[0], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8)); + map(0x4000, 0x7fff).rw(m_banks[1], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8)); + map(0x8000, 0xbfff).rw(m_banks[2], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8)); + map(0xc000, 0xffff).rw(m_banks[3], FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8)); } void z88_state::z88_io(address_map &map) @@ -183,6 +108,11 @@ Small note about natural keyboard: currently, */ static INPUT_PORTS_START( z88 ) + PORT_START("BATTERY") + PORT_CONFNAME( 0x01, 0x00, "Battery Status" ) PORT_WRITE_LINE_DEVICE_MEMBER("blink", upd65031_device, btl_w) + PORT_CONFSETTING( 0x00, DEF_STR( Normal ) ) + PORT_CONFSETTING( 0x01, "Low" ) + PORT_START("LINE0") PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Del") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHAR(8) PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) @@ -551,26 +481,16 @@ INPUT_PORTS_END void z88_state::machine_start() { - m_bios = (uint8_t*)m_bios_region->base(); - m_ram_base = (uint8_t*)m_ram->pointer(); - - // configure the memory banks - - m_banks[0]->configure_entry(0, m_bios); - m_banks[0]->configure_entry(1, m_ram_base); - m_banks[1]->configure_entries(0, 32, m_bios, 0x4000); - m_banks[2]->configure_entries(0, 32, m_bios, 0x4000); - m_banks[3]->configure_entries(0, 32, m_bios, 0x4000); - m_banks[4]->configure_entries(0, 32, m_bios, 0x4000); - m_banks[1]->configure_entries(32, m_ram->size()>>14, m_ram_base, 0x4000); - m_banks[2]->configure_entries(32, m_ram->size()>>14, m_ram_base, 0x4000); - m_banks[3]->configure_entries(32, m_ram->size()>>14, m_ram_base, 0x4000); - m_banks[4]->configure_entries(32, m_ram->size()>>14, m_ram_base, 0x4000); + m_nvram->set_base(m_ram->pointer(), m_ram->size()); } void z88_state::machine_reset() { - m_bank_type[0] = m_bank_type[1] = m_bank_type[2] = m_bank_type[3] = 0; + m_boot_view.select(0); + m_banks[0]->set_bank(0); + m_banks[1]->set_bank(0); + m_banks[2]->set_bank(0); + m_banks[3]->set_bank(0); } uint8_t z88_state::kb_r(offs_t offset) @@ -598,6 +518,23 @@ static void z88_cart(device_slot_interface &device) device.option_add("1024kflash", Z88_1024K_FLASH); // 1024KB Flash cart } +static DEVICE_INPUT_DEFAULTS_START( z88_rs232_defaults ) + DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_9600 ) + DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_9600 ) + DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 ) + DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE ) + DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 ) + DEVICE_INPUT_DEFAULTS( "FLOW_CONTROL", 0x07, 0x01 ) // Flow Control: RTS +DEVICE_INPUT_DEFAULTS_END + + +static void z88_rs232_devices(device_slot_interface &device) +{ + default_rs232_devices(device); + device.option_add("z88_impexp", Z88_IMPEXP); +} + + void z88_state::z88(machine_config &config) { /* basic machine hardware */ @@ -605,12 +542,17 @@ void z88_state::z88(machine_config &config) m_maincpu->set_addrmap(AS_PROGRAM, &z88_state::z88_mem); m_maincpu->set_addrmap(AS_IO, &z88_state::z88_io); + ADDRESS_MAP_BANK(config, m_banks[0]).set_map(&z88_state::z88_map).set_options(ENDIANNESS_LITTLE, 8, 22, 0x2000); + ADDRESS_MAP_BANK(config, m_banks[1]).set_map(&z88_state::z88_map).set_options(ENDIANNESS_LITTLE, 8, 22, 0x4000); + ADDRESS_MAP_BANK(config, m_banks[2]).set_map(&z88_state::z88_map).set_options(ENDIANNESS_LITTLE, 8, 22, 0x4000); + ADDRESS_MAP_BANK(config, m_banks[3]).set_map(&z88_state::z88_map).set_options(ENDIANNESS_LITTLE, 8, 22, 0x4000); + /* video hardware */ SCREEN(config, m_screen, SCREEN_TYPE_LCD); m_screen->set_refresh_hz(50); m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(0)); m_screen->set_size(Z88_SCREEN_WIDTH, Z88_SCREEN_HEIGHT); - m_screen->set_visarea(0, (Z88_SCREEN_WIDTH - 1), 0, (Z88_SCREEN_HEIGHT - 1)); + m_screen->set_visarea_full(); m_screen->set_palette(m_palette); m_screen->set_screen_update("blink", FUNC(upd65031_device::screen_update)); @@ -623,13 +565,24 @@ void z88_state::z88(machine_config &config) m_blink->spkr_wr_callback().set("speaker", FUNC(speaker_sound_device::level_w)); m_blink->set_screen_update_callback(FUNC(z88_state::lcd_update)); m_blink->set_memory_update_callback(FUNC(z88_state::bankswitch_update)); + m_blink->txd_wr_callback().set("rs232", FUNC(rs232_port_device::write_txd)); + m_blink->rts_wr_callback().set("rs232", FUNC(rs232_port_device::write_rts)); + m_blink->dtr_wr_callback().set("rs232", FUNC(rs232_port_device::write_dtr)); + m_blink->vpp_wr_callback().set(m_carts[3], FUNC(z88cart_slot_device::vpp_w)); // Only on Slot 3 + + rs232_port_device &rs232(RS232_PORT(config, "rs232", z88_rs232_devices, "z88_impexp")); + rs232.rxd_handler().set(m_blink, FUNC(upd65031_device::rxd_w)); + rs232.cts_handler().set(m_blink, FUNC(upd65031_device::cts_w)); + rs232.dcd_handler().set(m_blink, FUNC(upd65031_device::dcd_w)); + rs232.set_option_device_input_defaults("null_modem", DEVICE_INPUT_DEFAULTS_NAME(z88_rs232_defaults)); /* sound hardware */ SPEAKER(config, "mono").front_center(); SPEAKER_SOUND(config, "speaker").add_route(ALL_OUTPUTS, "mono", 0.50); /* internal ram */ - RAM(config, RAM_TAG).set_default_size("128K").set_extra_options("32K,64K,256K,512K"); + RAM(config, m_ram).set_default_size("128K").set_extra_options("32K,64K,256K,512K"); + NVRAM(config, m_nvram, nvram_device::DEFAULT_NONE); /* cartridges */ Z88CART_SLOT(config, m_carts[1], z88_cart, nullptr); diff --git a/src/mame/includes/z88.h b/src/mame/includes/z88.h index ccfac5cec9b..3778431d44e 100644 --- a/src/mame/includes/z88.h +++ b/src/mame/includes/z88.h @@ -12,10 +12,14 @@ #pragma once #include "cpu/z80/z80.h" +#include "machine/bankdev.h" +#include "machine/nvram.h" #include "machine/ram.h" #include "machine/upd65031.h" +#include "machine/z88_impexp.h" #include "sound/spkrdev.h" +#include "bus/rs232/rs232.h" #include "bus/z88/flash.h" #include "bus/z88/ram.h" #include "bus/z88/rom.h" @@ -44,7 +48,7 @@ public: z88_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag) , m_maincpu(*this, "maincpu") - , m_bios_region(*this, "bios") + , m_nvram(*this, "nvram") , m_ram(*this, RAM_TAG) , m_screen(*this, "screen") , m_palette(*this, "palette") @@ -52,66 +56,40 @@ public: , m_lines(*this, "LINE%u", 0U) , m_banks(*this, "bank%u", 1U) , m_carts(*this, "slot%u", 0U) + , m_boot_view(*this, "boot_view") { } void z88(machine_config &config); private: - enum - { - Z88_BANK_ROM = 1, - Z88_BANK_RAM, - Z88_BANK_CART, - Z88_BANK_UNMAP - }; - virtual void machine_start() override; virtual void machine_reset() override; uint8_t kb_r(offs_t offset); UPD65031_MEMORY_UPDATE(bankswitch_update); UPD65031_SCREEN_UPDATE(lcd_update); - // cartridges read/write - uint8_t bank0_cart_r(offs_t offset); - uint8_t bank1_cart_r(offs_t offset); - uint8_t bank2_cart_r(offs_t offset); - uint8_t bank3_cart_r(offs_t offset); - void bank0_cart_w(offs_t offset, uint8_t data); - void bank1_cart_w(offs_t offset, uint8_t data); - void bank2_cart_w(offs_t offset, uint8_t data); - void bank3_cart_w(offs_t offset, uint8_t data); - - // defined in video/z88.c + // defined in video/z88.cpp inline void plot_pixel(bitmap_ind16 &bitmap, int x, int y, uint16_t color); - inline uint8_t* convert_address(uint32_t offset); - void vh_render_8x8(bitmap_ind16 &bitmap, int x, int y, uint16_t pen0, uint16_t pen1, uint8_t *gfx); - void vh_render_6x8(bitmap_ind16 &bitmap, int x, int y, uint16_t pen0, uint16_t pen1, uint8_t *gfx); + void vh_render_8x8(address_space &space, bitmap_ind16 &bitmap, int x, int y, uint16_t pen0, uint16_t pen1, uint32_t offset); + void vh_render_6x8(address_space &space, bitmap_ind16 &bitmap, int x, int y, uint16_t pen0, uint16_t pen1, uint32_t offset); void vh_render_line(bitmap_ind16 &bitmap, int x, int y, uint16_t pen); void z88_palette(palette_device &palette) const; void z88_io(address_map &map); void z88_mem(address_map &map); + void z88_map(address_map &map); required_device m_maincpu; - required_memory_region m_bios_region; + required_device m_nvram; required_device m_ram; required_device m_screen; required_device m_palette; required_device m_blink; required_ioport_array<8> m_lines; - required_memory_bank_array<5> m_banks; + required_device_array m_banks; optional_device_array m_carts; - - struct - { - uint8_t slot = 0; - uint8_t page = 0; - } m_bank[4]; - - int m_bank_type[4] = {}; - uint8_t * m_bios = 0; - uint8_t * m_ram_base = 0; + memory_view m_boot_view; }; #endif // MAME_INCLUDES_Z88_H diff --git a/src/mame/machine/upd65031.cpp b/src/mame/machine/upd65031.cpp index f8dcbbf4797..cc4b33e90d2 100644 --- a/src/mame/machine/upd65031.cpp +++ b/src/mame/machine/upd65031.cpp @@ -15,9 +15,7 @@ TODO: - coma and snooze mode - - speaker controlled by txd - - EPROM programming - - UART + - UART Loopback mode *********************************************************************/ @@ -25,6 +23,9 @@ #include "emu.h" #include "upd65031.h" +#define VERBOSE 0 +#include "logmacro.h" + // device type definition DEFINE_DEVICE_TYPE(UPD65031, upd65031_device, "upd65031", "NEC uPD65031") @@ -34,9 +35,9 @@ DEFINE_DEVICE_TYPE(UPD65031, upd65031_device, "upd65031", "NEC uPD65031") // MACROS / CONSTANTS //************************************************************************** -#define LOG 0 +namespace { -#define SPEAKER_ALARM_FREQ attotime::from_hz(3200) +static constexpr uint32_t SPEAKER_ALARM_FREQ = 3200; // internal registers enum @@ -92,48 +93,98 @@ enum }; // interrupt status -#define STA_FLAPOPEN 0x80 -#define STA_A19 0x40 -#define STA_FLAP 0x20 -#define STA_UART 0x10 -#define STA_BTL 0x08 -#define STA_KEY 0x04 -#define STA_TIME 0x01 +static constexpr uint8_t STA_FLAPOPEN = 0x80; // Flap status +static constexpr uint8_t STA_A19 = 0x40; // High level on A19 occurred during Coma +static constexpr uint8_t STA_FLAP = 0x20; // Flap interrupt +static constexpr uint8_t STA_UART = 0x10; // UART interrupt +static constexpr uint8_t STA_BTL = 0x08; // Battery low interrupt +static constexpr uint8_t STA_KEY = 0x04; // Keyboard interrupt +static constexpr uint8_t STA_TIME = 0x01; // RTC interrupt // interrupt control -#define INT_KWAIT 0x80 -#define INT_A19 0x40 -#define INT_FLAP 0x20 -#define INT_UART 0x10 -#define INT_BTL 0x08 -#define INT_KEY 0x04 -#define INT_TIME 0x02 -#define INT_GINT 0x01 +static constexpr uint8_t INT_KWAIT = 0x80; // Reading the keyboard will Snooze +static constexpr uint8_t INT_A19 = 0x40; // A19 high will exit Coma mode +static constexpr uint8_t INT_FLAP = 0x20; // Enable Flap open interrupt +static constexpr uint8_t INT_UART = 0x10; // Enable UART interrupt +static constexpr uint8_t INT_BTL = 0x08; // Enable Battery low interrupt +static constexpr uint8_t INT_KEY = 0x04; // Enable Keyboard interrupt +static constexpr uint8_t INT_TIME = 0x02; // Enable RTC interrupt +static constexpr uint8_t INT_GINT = 0x01; // Global interrupts mask + +// acknowledge interrupts +static constexpr uint8_t ACK_A19 = 0x40; // Acknowledge A19 interrupt +static constexpr uint8_t ACK_FLAP = 0x20; // Acknowledge Flap interrupt +static constexpr uint8_t ACK_BTL = 0x08; // Acknowledge battery low interrupt +static constexpr uint8_t ACK_KEY = 0x04; // Acknowledge keyboard interrupt // command register -#define COM_SRUN 0x80 -#define COM_SBIT 0x40 -#define COM_OVERP 0x20 -#define COM_RESTIM 0x10 -#define COM_PROGRAM 0x08 -#define COM_RAMS 0x04 -#define COM_VPPON 0x02 -#define COM_LCDON 0x01 +static constexpr uint8_t COM_SRUN = 0x80; // Speaker source (0: manual, 1: auto) +static constexpr uint8_t COM_SBIT = 0x40; // Speaker source for SRUN=1 (0: 3200Hz, 1: TxD) +static constexpr uint8_t COM_OVERP = 0x20; // Overprogram EPROMs +static constexpr uint8_t COM_RESTIM = 0x10; // RTC reset +static constexpr uint8_t COM_PROGRAM = 0x08; // EPROM programming +static constexpr uint8_t COM_RAMS = 0x04; // Enable boot ROM bank +static constexpr uint8_t COM_VPPON = 0x02; // Programming voltage ON +static constexpr uint8_t COM_LCDON = 0x01; // LCD ON // EPROM programming register -#define EPR_PD1 0x80 -#define EPR_PD0 0x40 -#define EPR_PGMD 0x20 -#define EPR_EOED 0x10 -#define EPR_SE3D 0x08 -#define EPR_PGMP 0x04 -#define EPR_EOEP 0x02 -#define EPR_SE3P 0x01 +static constexpr uint8_t EPR_PD = 0xc0; // Two bits representing the length of delay period +static constexpr uint8_t EPR_PGMD = 0x20; // State of program pulse during delay period +static constexpr uint8_t EPR_EOED = 0x10; // State of EOE during delay period +static constexpr uint8_t EPR_SE3D = 0x08; // State of slot 3 select during delay period +static constexpr uint8_t EPR_PGMP = 0x04; // State of program pulse during porch period +static constexpr uint8_t EPR_EOEP = 0x02; // State of EOE during porch period +static constexpr uint8_t EPR_SE3P = 0x01; // State of slot 3 select during porch period // RTC interrupt status -#define TSTA_MIN 0x04 -#define TSTA_SEC 0x02 -#define TSTA_TICK 0x01 +static constexpr uint8_t TSTA_MIN = 0x04; // Minute interrupt has occurred +static constexpr uint8_t TSTA_SEC = 0x02; // Second interrupt has occurred +static constexpr uint8_t TSTA_TICK = 0x01; // Tick interrupt has occurred + +// UART extended receive data +static constexpr uint8_t RXE_FE = 0x20; // Frame error +static constexpr uint8_t RXE_RXDB = 0x10; // RXD line state +static constexpr uint8_t RXE_TCLK = 0x08; // Transmit clock +static constexpr uint8_t RXE_RCLK = 0x04; // Receive clock +static constexpr uint8_t RXE_PAR = 0x02; // Parity bit +static constexpr uint8_t RXE_START = 0x01; // Start bit (should be zero) + +// UART receive control +static constexpr uint8_t RXC_SHTW = 0x80; // Short word mode +static constexpr uint8_t RXC_LOOP = 0x40; // Loopback mode +static constexpr uint8_t RXC_UART = 0x20; // Reset +static constexpr uint8_t RXC_ARTS = 0x10; // Auto RTS mode +static constexpr uint8_t RXC_IRTS = 0x08; // Invert RTS +static constexpr uint8_t RXC_BAUD = 0x07; // Baud rate + +// UART transmit control +static constexpr uint8_t TXC_UTEST = 0x80; // Fast baud rate +static constexpr uint8_t TXC_IDCD = 0x40; // DCD interrupt when low (0 for when high) +static constexpr uint8_t TXC_ICTS = 0x20; // CTD interrupt when low (0 for when high) +static constexpr uint8_t TXC_ATX = 0x10; // Auto transmit mode +static constexpr uint8_t TXC_ITX = 0x08; // Invert Tx +static constexpr uint8_t TXC_BAUD = 0x07; // Baud rate + +// UART interrupt status +static constexpr uint8_t UIT_RSRD = 0x80; // Receive shift register full +static constexpr uint8_t UIT_DCDI = 0x40; // DCD interrupt +static constexpr uint8_t UIT_CTSI = 0x20; // CTS interrupt +static constexpr uint8_t UIT_TDRE = 0x10; // Transmit register empty +static constexpr uint8_t UIT_RDRF = 0x04; // Receive register full +static constexpr uint8_t UIT_DCD = 0x02; // Inverse of the DCD line level +static constexpr uint8_t UIT_CTS = 0x01; // Inverse of the CTS line level + +// UART interrupt mask +static constexpr uint8_t UMK_DCD = 0x40; // DCD interrupts are enabled +static constexpr uint8_t UMK_CTS = 0x20; // CTS interrupts are enabled +static constexpr uint8_t UMK_TDRE = 0x10; // Transmit data register empty interrupt enabled +static constexpr uint8_t UMK_RDRF = 0x04; // Receive data register full interrupt enabled + +// UART interrupt acknowledge register +static constexpr uint8_t UAK_DCD = 0x40; // Acknowledge DCD interrupt +static constexpr uint8_t UAK_CTS = 0x20; // Acknowledge CTS interrupt + +} // anonymous namespace //************************************************************************** // INLINE HELPERS @@ -143,13 +194,13 @@ inline void upd65031_device::interrupt_refresh() { if ((m_int & INT_GINT) && ((m_int & m_sta & 0x7c) || ((m_int & INT_TIME) && (m_sta & STA_TIME)))) { - if (LOG) logerror("uPD65031 '%s': set int\n", tag()); + LOG("%s: set int\n", machine().describe_context()); m_write_int(ASSERT_LINE); } else { - if (LOG) logerror("uPD65031 '%s': clear int\n", tag()); + LOG("%s: clear int\n", machine().describe_context()); m_write_int(CLEAR_LINE); } @@ -165,6 +216,24 @@ inline void upd65031_device::update_rtc_interrupt() m_sta &= ~STA_TIME; } +inline void upd65031_device::update_uart_interrupt() +{ + if ((m_int & INT_UART) && (m_uit & m_umk)) + m_sta |= STA_UART; + else + m_sta &= ~STA_UART; + + interrupt_refresh(); +} + +inline void upd65031_device::update_tx(int state) +{ + m_txd_line = state; + m_write_txd(m_txd_line); + + if ((m_com & COM_SRUN) && (m_com & COM_SBIT)) + m_write_spkr(m_txd_line); +} inline void upd65031_device::set_mode(int mode) { @@ -198,12 +267,19 @@ inline void upd65031_device::set_mode(int mode) upd65031_device::upd65031_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, UPD65031, tag, owner, clock), + device_serial_interface(mconfig, *this), m_read_kb(*this), m_write_int(*this), m_write_nmi(*this), m_write_spkr(*this), + m_write_txd(*this), + m_write_rts(*this), + m_write_dtr(*this), + m_write_vpp(*this), m_screen_update_cb(*this), - m_out_mem_cb(*this) + m_out_mem_cb(*this), + m_sta(0), + m_int(0) { } @@ -219,6 +295,10 @@ void upd65031_device::device_start() m_write_int.resolve_safe(); m_write_nmi.resolve_safe(); m_write_spkr.resolve_safe(); + m_write_txd.resolve_safe(); + m_write_rts.resolve_safe(); + m_write_dtr.resolve_safe(); + m_write_vpp.resolve_safe(); // bind delegates m_screen_update_cb.resolve(); @@ -244,6 +324,12 @@ void upd65031_device::device_start() save_item(NAME(m_tmk)); save_item(NAME(m_tack)); save_item(NAME(m_com)); + save_item(NAME(m_uit)); + save_item(NAME(m_umk)); + save_item(NAME(m_txc)); + save_item(NAME(m_rxe)); + save_item(NAME(m_rxc)); + save_item(NAME(m_txd_line)); save_item(NAME(m_flash)); } @@ -266,6 +352,12 @@ void upd65031_device::device_reset() m_com = 0; m_flash = 0; m_mode = 0; + m_uit = UIT_TDRE; // Transmit register empty + m_umk = 0x00; + m_rxe = 0x00; + m_rxc = RXC_SHTW | 0x05; // 9600 baud, 1 Stop Bit + m_txc = TXC_IDCD | TXC_ICTS | 0x05; // 9600 baud + m_txd_line = 0; set_mode(STATE_AWAKE); if (!m_out_mem_cb.isnull()) @@ -276,6 +368,14 @@ void upd65031_device::device_reset() m_out_mem_cb(2, 0, 0); m_out_mem_cb(3, 0, 0); } + + set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1); + set_rate(9600); + transmit_register_reset(); + receive_register_reset(); + m_write_rts(1); + m_write_dtr(1); + m_write_vpp(0); } @@ -292,7 +392,7 @@ void upd65031_device::device_timer(emu_timer &timer, device_timer_id id, int par // if a key is pressed sets the interrupt if ((m_int & INT_GINT) && (m_int & INT_KEY) && m_read_kb(0) != 0xff) { - if (LOG) logerror("uPD65031 '%s': Keyboard interrupt!\n", tag()); + LOG("%s: Keyboard interrupt!\n", machine().describe_context()); // awakes CPU from snooze on key down if (m_mode == STATE_SNOOZE) @@ -324,20 +424,22 @@ void upd65031_device::device_timer(emu_timer &timer, device_timer_id id, int par } } - if (m_tim[0] == 200) + if (m_tim[0] == 128) // on the rising edge of TIM0 bit 7 { - m_tim[0] = 0; - // set seconds int has occurred if (m_tmk & TSTA_SEC) { m_tsta |= TSTA_SEC; irq_change = true; } + } + if (m_tim[0] == 200) + { + m_tim[0] = 0; m_tim[1]++; - if (m_tim[1] == 60) + if (m_tim[1] == 32) // on the rising edge of TIM1 bit 5 { // set minutes int has occurred if (m_tmk & TSTA_MIN) @@ -345,6 +447,10 @@ void upd65031_device::device_timer(emu_timer &timer, device_timer_id id, int par m_tsta |= TSTA_MIN; irq_change = true; } + } + + if (m_tim[1] == 60) + { m_tim[1] = 0; m_tim[2]++; @@ -419,47 +525,54 @@ uint8_t upd65031_device::read(offs_t offset) { set_mode(STATE_SNOOZE); - if (LOG) logerror("uPD65031 '%s': entering snooze!\n", tag()); + LOG("%s: entering snooze!\n", machine().describe_context()); } uint8_t data = m_read_kb(offset>>8); - if (LOG) logerror("uPD65031 '%s': key r %02x: %02x\n", tag(), offset>>8, data); + LOG("%s: key r %02x %02x\n", machine().describe_context(), offset>>8, data); return data; } // read real time clock status case REG_TSTA: - if (LOG) logerror("uPD65031 '%s': tsta r: %02x\n", tag(), m_tsta); + LOG("%s: tsta r %02x\n", machine().describe_context(), m_tsta); return m_tsta & 0x07; // read real time clock counters case REG_TIM0: - if (LOG) logerror("uPD65031 '%s': TIM0 r: %02x\n", tag(), m_tim[0]); + LOG("%s: TIM0 r %02x\n", machine().describe_context(), m_tim[0]); return m_tim[0]; case REG_TIM1: - if (LOG) logerror("uPD65031 '%s': TIM1 r: %02x\n", tag(), m_tim[1]); + LOG("%s: TIM1 r %02x\n", machine().describe_context(), m_tim[1]); return m_tim[1]; case REG_TIM2: - if (LOG) logerror("uPD65031 '%s': TIM2 r: %02x\n", tag(), m_tim[2]); + LOG("%s: TIM2 r %02x\n", machine().describe_context(), m_tim[2]); return m_tim[2]; case REG_TIM3: - if (LOG) logerror("uPD65031 '%s': TIM3 r: %02x\n", tag(), m_tim[3]); + LOG("%s: TIM3 r %02x\n", machine().describe_context(), m_tim[3]); return m_tim[3]; case REG_TIM4: - if (LOG) logerror("uPD65031 '%s': TIM4 r: %02x\n", tag(), m_tim[4]); + LOG("%s: TIM4 r %02x\n", machine().describe_context(), m_tim[4]); return m_tim[4]; // UART - case REG_RXD: - case REG_RXE: - case REG_UIT: - // TODO - return 0; + case REG_RXD: // UART receive data register + m_uit &= ~UIT_RDRF; + update_uart_interrupt(); + if (m_rxc & RXC_ARTS) // Auto RTS mode + m_write_rts(1); + return get_received_char(); + + case REG_RXE: // UART extended receive data + return m_rxe; + + case REG_UIT: // UART interrupt status + return m_uit; default: - logerror("uPD65031 '%s': blink r: %04x\n", tag(), offset); + logerror("%s: blink r %04x\n", machine().describe_context(), offset); return 0; } } @@ -471,6 +584,7 @@ uint8_t upd65031_device::read(offs_t offset) void upd65031_device::write(offs_t offset, uint8_t data) { + static const int uart_div[] = { 1 << 17, 1 << 15, 1 << 14, 1 << 13, 1 << 12, 1 << 10, 1 << 9, 1 << 8 }; uint8_t port = offset & 0xff; switch (port) @@ -485,7 +599,7 @@ void upd65031_device::write(offs_t offset, uint8_t data) break; case REG_COM: // command register - if (LOG) logerror("uPD65031 '%s': com w: %02x\n", tag(), data); + LOG("%s: com w %02x\n", machine().describe_context(), data); // reset clock? if (data & COM_RESTIM) @@ -494,7 +608,7 @@ void upd65031_device::write(offs_t offset, uint8_t data) if ((data & COM_SRUN) && !(data & COM_SBIT)) { // constant tone used for keyclick and alarm - m_speaker_timer->adjust(SPEAKER_ALARM_FREQ, 0, SPEAKER_ALARM_FREQ); + m_speaker_timer->adjust(attotime::from_hz(SPEAKER_ALARM_FREQ), 0, attotime::from_hz(SPEAKER_ALARM_FREQ)); } else { @@ -507,7 +621,7 @@ void upd65031_device::write(offs_t offset, uint8_t data) else { // speaker controlled by txd line - // TODO + m_write_spkr(m_txd_line); } m_speaker_timer->reset(); @@ -517,11 +631,13 @@ void upd65031_device::write(offs_t offset, uint8_t data) if (BIT(m_com^data, 2) && !m_out_mem_cb.isnull()) m_out_mem_cb(0, m_sr[0], BIT(data, 2)); + m_write_vpp(BIT(data, 1)); + m_com = data; break; case REG_INT: // interrupt control - if (LOG) logerror("uPD65031 '%s': int w: %02x\n", tag(), data); + LOG("%s: int w %02x\n", machine().describe_context(), data); m_int = data; @@ -531,12 +647,11 @@ void upd65031_device::write(offs_t offset, uint8_t data) break; case REG_EPR: // EPROM programming register - if (LOG) logerror("uPD65031 '%s': epr w: %02x\n", tag(), data); - // TODO + LOG("%s: epr w %02x\n", machine().describe_context(), data); break; case REG_TACK: // rtc interrupt acknowledge - if (LOG) logerror("uPD65031 '%s': tack w: %02x\n", tag(), data); + LOG("%s: tack w %02x\n", machine().describe_context(), data); // clear ints that have occurred m_tsta &= ~(data & 0x07); @@ -548,13 +663,13 @@ void upd65031_device::write(offs_t offset, uint8_t data) break; case REG_TMK: // write rtc interrupt mask - if (LOG) logerror("uPD65031 '%s': tmk w: %02x\n", tag(), data); + LOG("%s: tmk w %02x\n", machine().describe_context(), data); m_tmk = data & 0x07; break; case REG_ACK: // acknowledge ints - if (LOG) logerror("uPD65031 '%s': ack w: %02x\n", tag(), data); + LOG("%s: ack w %02x\n", machine().describe_context(), data); m_ack = data; m_sta &= ~(data & 0x7f); @@ -575,21 +690,116 @@ void upd65031_device::write(offs_t offset, uint8_t data) break; // UART - case REG_RXC: - case REG_TXD: - case REG_TXC: - case REG_UMK: - case REG_UAK: - if (LOG) logerror("uPD65031 '%s': UART w: %02x %02x\n", tag(), port & 7 , data); - // TODO + case REG_RXC: // UART receive control + LOG("%s: UART receive control %02x\n", machine().describe_context(), data); + + if ((m_rxc & RXC_BAUD) != (data & RXC_BAUD)) + set_rcv_rate(clock() / uart_div[data & RXC_BAUD]); + + if ((m_rxc ^ data) & RXC_SHTW) + set_data_frame(1, 8, PARITY_NONE, (data & RXC_SHTW) ? STOP_BITS_1 : STOP_BITS_2); + + if (data & RXC_LOOP) + logerror("%s: Unsupported UART Loopback mode\n", machine().describe_context()); + + if (!(data & RXC_ARTS)) + m_write_rts((data & RXC_IRTS) ? 0 : 1); + + m_rxc = data; + break; + + case REG_TXD: // UART transmit data + transmit_register_setup(data); + m_uit &= ~UIT_TDRE; + update_uart_interrupt(); + break; + + case REG_TXC: // UART transmit control + LOG("%s: UART transmit control %02x\n", machine().describe_context(), data); + + if ((m_txc & TXC_BAUD) != (data & TXC_BAUD)) + set_tra_rate(clock() / uart_div[data & TXC_BAUD]); + + if (!(data & TXC_ATX) && ((m_txc ^ data) & TXC_ITX)) + update_tx((data & TXC_ITX) ? 0 : 1); + + m_txc = data; + break; + + case REG_UMK: // UART interrupt mask + LOG("%s: UART interrupt mask %02x\n", machine().describe_context(), data); + + m_umk = data; + update_uart_interrupt(); + break; + + case REG_UAK: // UART interrupt acknowledge + LOG("%s: UART interrupt acknowledge %02x\n", machine().describe_context(), data); + + m_uit &= ~(data & m_umk & (UAK_CTS | UAK_DCD)); + update_uart_interrupt(); break; default: - logerror("uPD65031 '%s': blink w: %04x %02x\n", tag(), offset, data); + logerror("%s: blink w %04x = %02x\n", machine().describe_context(), offset, data); break; } } +void upd65031_device::tra_callback() +{ + update_tx(transmit_register_get_data_bit() ^ BIT(m_txc, 3)); +} + +void upd65031_device::tra_complete() +{ + m_uit |= UIT_TDRE; + update_uart_interrupt(); +} + +void upd65031_device::rcv_complete() +{ + receive_register_extract(); + + m_uit |= UIT_RDRF; + + if (m_rxc & RXC_ARTS) // Auto RTS mode + m_write_rts(0); + + // Frame error + if (is_receive_framing_error()) + m_rxe |= RXE_FE; + else + m_rxe &= ~RXE_FE; + + update_uart_interrupt(); +} + +WRITE_LINE_MEMBER( upd65031_device::cts_w ) +{ + if (state == BIT(m_uit, 0)) + { + m_uit = (m_uit & ~UIT_CTS) | (state ? 0 : UIT_CTS); + if (state != BIT(m_txc, 5)) + { + m_uit |= UIT_CTSI; + update_uart_interrupt(); + } + } +} + +WRITE_LINE_MEMBER( upd65031_device::dcd_w ) +{ + if (state == BIT(m_uit, 1)) + { + m_uit = (m_uit & ~UIT_DCD) | (state ? 0 : UIT_DCD); + if (state != BIT(m_txc, 6)) + { + m_uit |= UIT_DCDI; + update_uart_interrupt(); + } + } +} //------------------------------------------------- // flp line diff --git a/src/mame/machine/upd65031.h b/src/mame/machine/upd65031.h index 730da988e09..70436c53366 100644 --- a/src/mame/machine/upd65031.h +++ b/src/mame/machine/upd65031.h @@ -11,6 +11,7 @@ #pragma once +#include "diserial.h" //************************************************************************** // TYPE DEFINITIONS @@ -22,7 +23,8 @@ // ======================> upd65031_device -class upd65031_device : public device_t +class upd65031_device : public device_t, + public device_serial_interface { public: typedef device_delegate screen_update_delegate; @@ -35,6 +37,10 @@ public: auto int_wr_callback() { return m_write_int.bind(); } auto nmi_wr_callback() { return m_write_nmi.bind(); } auto spkr_wr_callback() { return m_write_spkr.bind(); } + auto txd_wr_callback() { return m_write_txd.bind(); } + auto rts_wr_callback() { return m_write_rts.bind(); } + auto dtr_wr_callback() { return m_write_dtr.bind(); } + auto vpp_wr_callback() { return m_write_vpp.bind(); } template void set_screen_update_callback(T &&... args) { m_screen_update_cb.set(std::forward(args)...); } template void set_memory_update_callback(T &&... args) { m_out_mem_cb.set(std::forward(args)...); } @@ -43,6 +49,9 @@ public: void write(offs_t offset, uint8_t data); DECLARE_WRITE_LINE_MEMBER( flp_w ); DECLARE_WRITE_LINE_MEMBER( btl_w ); + DECLARE_WRITE_LINE_MEMBER( rxd_w ) { rx_w(state); } + DECLARE_WRITE_LINE_MEMBER( cts_w ); + DECLARE_WRITE_LINE_MEMBER( dcd_w ); uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); protected: @@ -51,9 +60,15 @@ protected: virtual void device_reset() override; virtual void device_timer(emu_timer &timer, device_timer_id id, int param) override; + virtual void tra_callback() override; + virtual void tra_complete() override; + virtual void rcv_complete() override; + private: inline void interrupt_refresh(); inline void update_rtc_interrupt(); + inline void update_uart_interrupt(); + inline void update_tx(int state); inline void set_mode(int mode); static const device_timer_id TIMER_RTC = 0; static const device_timer_id TIMER_FLASH = 1; @@ -63,6 +78,10 @@ private: devcb_write_line m_write_int; devcb_write_line m_write_nmi; devcb_write_line m_write_spkr; + devcb_write_line m_write_txd; + devcb_write_line m_write_rts; + devcb_write_line m_write_dtr; + devcb_write_line m_write_vpp; screen_update_delegate m_screen_update_cb; // callback for update the LCD memory_update_delegate m_out_mem_cb; // callback for update bankswitch @@ -78,6 +97,12 @@ private: uint8_t m_tmk; // timer interrupt mask uint8_t m_tack; // timer interrupts acknowledge uint8_t m_com; // command register + uint8_t m_uit; // UART interrupt status + uint8_t m_umk; // UART interrupt mask + uint8_t m_txc; // UART transmit control register + uint8_t m_rxc; // UART receive control register + uint8_t m_rxe; // UART extended receive data register + int m_txd_line; // TXD line int m_flash; // cursor flash int m_speaker_state; // spkr line diff --git a/src/mame/machine/z88_impexp.cpp b/src/mame/machine/z88_impexp.cpp new file mode 100644 index 00000000000..431fae75197 --- /dev/null +++ b/src/mame/machine/z88_impexp.cpp @@ -0,0 +1,273 @@ +// license:BSD-3-Clause +// copyright-holders:Sandro Ronco +/********************************************************************** + + Z88 Imp-Export protocol + + Send to Z88: + - Launch Imp-Export popdown on Z88. + - On Z88, select batch receive by pressing b and then the Enter key. + - Go in the MAME file manager and load the file to import in the serial device. + + Receive from Z88: + - Launch Imp-Export popdown on Z88. + - Go in the MAME file manager and create a new file in the serial device. + - On Z88, select send by pressing s and then the Enter key. + - Enter the filename to transfer, and then the Enter key. + - When transfer is complete go in the MAME file manager and unload the image. + + Protocol: + - ESC + N: start of file name + - ESC + F: start of file data + - ESC + E: end of file + - ESC + Z: end of batch + - ESC + BXX: escaped byte, where XX is the uppercase hexadecimal + +*********************************************************************/ + +#include "emu.h" +#include "z88_impexp.h" + +static constexpr uint32_t Z88_RS232_BAUD = 9600; + +// device type definition +DEFINE_DEVICE_TYPE(Z88_IMPEXP, z88_impexp_device, "z88_impexp", "Z88 Imp-Export protocol"); + +z88_impexp_device::z88_impexp_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, Z88_IMPEXP, tag, owner, clock) + , device_serial_interface(mconfig, *this) + , device_rs232_port_interface(mconfig, *this) + , device_image_interface(mconfig, *this) + , m_timer_poll(nullptr) + , m_mode(MODE_IDLE) + , m_rts(1) + , m_dtr(1) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void z88_impexp_device::device_start() +{ + m_timer_poll = timer_alloc(); +} + +//------------------------------------------------- +// device_reset - reset up the device +//------------------------------------------------- + +void z88_impexp_device::device_reset() +{ + set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1); + set_rate(Z88_RS232_BAUD); + + output_rxd(1); + output_dcd(0); + output_dsr(0); + output_cts(0); + + m_rts = 1; + m_dtr = 1; + m_queue = std::queue(); +} + +//------------------------------------------------- +// device_timer - handler timer events +//------------------------------------------------- + +void z88_impexp_device::device_timer(emu_timer &timer, device_timer_id id, int param) +{ + queue(); +} + +WRITE_LINE_MEMBER( z88_impexp_device::input_rts ) +{ + if (!state && m_rts) + { + m_rts = state; + queue(); + } + else + m_rts = state; +} + +void z88_impexp_device::queue() +{ + if (is_transmit_register_empty()) + { + if (!m_queue.empty() && m_rts == 0) + { + transmit_register_setup(m_queue.front()); + m_queue.pop(); + m_timer_poll->adjust(attotime::never); + return; + } + + m_timer_poll->adjust(attotime::from_hz(Z88_RS232_BAUD)); + } +} + +void z88_impexp_device::tra_callback() +{ + output_rxd(transmit_register_get_data_bit()); +} + +void z88_impexp_device::tra_complete() +{ + if (m_mode == MODE_SEND) + queue(); +} + +void z88_impexp_device::rcv_complete() +{ + receive_register_extract(); + if (m_mode == MODE_RECV) + m_queue.push(get_received_char()); +} + + +void z88_impexp_device::check_filename(std::string &filename) +{ + for (auto &c : filename) + if (c != '-' && !std::isalnum(c)) + c = '-'; +} + + +image_init_result z88_impexp_device::call_load() +{ + m_mode = MODE_SEND; + m_queue = std::queue(); + + std::string name; + if (basename()) + { + if (basename_noext()) + name = basename_noext(); + else + name = basename(); + + if (name.length() > 12) + name.resize(12); + + check_filename(name); + + if (filetype().length()) + { + std::string ext = filetype(); + if (ext.length() > 3) + ext.resize(3); + + check_filename(ext); + name += "." + ext; + } + } + else + name = "UNKNOWN"; + + // file name + m_queue.push(0x1b); + m_queue.push('N'); + for (char const &c: name) + m_queue.push(c); + + // file data + m_queue.push(0x1b); + m_queue.push('F'); + while (!image_feof()) + { + uint8_t b; + if (fread(&b, 1) != 1) + return image_init_result::FAIL; + + // Escape non printable characters + if ((b < 0x20 || b >= 0x7f) && b != 0x0a && b != 0x0d && b != 0x09) + { + m_queue.push(0x1b); + m_queue.push('B'); + for (int i = 4; i >= 0; i -= 4) + { + uint8_t n = (b >> i) & 0x0f; + if (n < 10) + m_queue.push('0' + n); + else + m_queue.push('A' + n - 10); + } + } + else + m_queue.push(b); + } + + // end of file + m_queue.push(0x1b); + m_queue.push('E'); + queue(); + + return image_init_result::PASS; +} + + +image_init_result z88_impexp_device::call_create(int format_type, util::option_resolution *format_options) +{ + m_queue = std::queue(); + m_mode = MODE_RECV; + return image_init_result::PASS; +} + + +void z88_impexp_device::call_unload() +{ + if (m_mode == MODE_RECV && !m_queue.empty()) + { + std::string name; + char mode = 0; + while (!m_queue.empty()) + { + uint8_t b = m_queue.front(); + m_queue.pop(); + if (b != 0x1b) + { + if (mode == 'F') + fwrite(&b, 1); + else if (mode == 'N') + name.push_back(b); + } + else if (!m_queue.empty()) + { + b = m_queue.front(); + m_queue.pop(); + if (b == 'N') mode = 'N'; // File name + else if (b == 'F') mode = 'F'; // File data + else if (b == 'E') break; // End of file + else if (b == 'Z') break; // End of Batch + else if (b == 'B') // Escaped byte + { + uint8_t val = 0; + for (int i = 4; !m_queue.empty() && i >= 0; i -= 4) + { + b = m_queue.front(); + m_queue.pop(); + if (b >= '0' && b <= '9') + val |= (b - '0') << i; + else if (b >= 'A' && b <= 'F') + val |= (b - 'A' + 10) << i; + else + logerror("Invalid escaped byte 0x%02x", b); + } + + if (mode == 'F') + fwrite(&val, 1); + } + else + logerror("Unknown escape 0x%02x\n", b); + } + } + + logerror("Received file '%s'\n", name); + } + + m_queue = std::queue(); + m_mode = MODE_IDLE; +} diff --git a/src/mame/machine/z88_impexp.h b/src/mame/machine/z88_impexp.h new file mode 100644 index 00000000000..67a818467bf --- /dev/null +++ b/src/mame/machine/z88_impexp.h @@ -0,0 +1,70 @@ +// license:BSD-3-Clause +// copyright-holders:Sandro Ronco + +#ifndef MAME_MACHINE_Z88_IMPEXP_H +#define MAME_MACHINE_Z88_IMPEXP_H + +#pragma once + +#include "bus/rs232/rs232.h" +#include "diserial.h" + +#include + + +class z88_impexp_device : public device_t, + public device_serial_interface, + public device_rs232_port_interface, + public device_image_interface +{ +public: + z88_impexp_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + virtual WRITE_LINE_MEMBER( input_txd ) override { device_serial_interface::rx_w(state); } + virtual WRITE_LINE_MEMBER( input_rts ) override; + virtual WRITE_LINE_MEMBER( input_dtr ) override { m_dtr = state; } + +protected: + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param) override; + + // device_serial_interface overrides + virtual void tra_callback() override; + virtual void tra_complete() override; + virtual void rcv_complete() override; + + // image-level overrides + virtual image_init_result call_load() override; + virtual void call_unload() override; + virtual image_init_result call_create(int format_type, util::option_resolution *format_options) override; + + virtual bool is_readable() const noexcept override { return true; } + virtual bool is_writeable() const noexcept override { return true; } + virtual bool is_creatable() const noexcept override { return true; } + virtual bool is_reset_on_load() const noexcept override { return false; } + virtual const char *file_extensions() const noexcept override { return ""; } + virtual const char *image_type_name() const noexcept override { return "serial"; } + virtual const char *image_brief_type_name() const noexcept override { return "serl"; } + +private: + void check_filename(std::string &filename); + void queue(); + + enum op_mode_t : uint8_t + { + MODE_IDLE, + MODE_SEND, + MODE_RECV, + }; + + emu_timer * m_timer_poll; + op_mode_t m_mode; + int m_rts; + int m_dtr; + std::queue m_queue; +}; + +DECLARE_DEVICE_TYPE(Z88_IMPEXP, z88_impexp_device) + +#endif // MAME_MACHINE_Z88_IMPEXP_H diff --git a/src/mame/video/z88.cpp b/src/mame/video/z88.cpp index 3619cd33847..3435b5950db 100644 --- a/src/mame/video/z88.cpp +++ b/src/mame/video/z88.cpp @@ -2,7 +2,7 @@ // copyright-holders:Kevin Thacker,Sandro Ronco /*************************************************************************** - z88.c + z88.cpp Functions to emulate the video hardware of the Cambridge Z88 @@ -18,23 +18,6 @@ inline void z88_state::plot_pixel(bitmap_ind16 &bitmap, int x, int y, uint16_t c bitmap.pix(y, x) = color; } -// convert absolute offset into correct address to get data from -inline uint8_t* z88_state::convert_address(uint32_t offset) -{ - if (offset < 0x080000) // rom - return m_bios + (offset & 0x7ffff); - else if (offset < 0x100000) // slot0 - return m_ram_base + (offset & 0x7ffff); - else if (offset < 0x200000) // slot1 - return m_carts[1]->get_cart_base() + (offset & 0xfffff); - else if (offset < 0x300000) // slot2 - return m_carts[2]->get_cart_base() + (offset & 0xfffff); - else if (offset < 0x400000) // slot3 - return m_carts[3]->get_cart_base() + (offset & 0xfffff); - - return nullptr; -} - /*************************************************************************** Start the video hardware emulation. ***************************************************************************/ @@ -49,11 +32,11 @@ void z88_state::z88_palette(palette_device &palette) const /* temp - change to gfxelement structure */ -void z88_state::vh_render_8x8(bitmap_ind16 &bitmap, int x, int y, uint16_t pen0, uint16_t pen1, uint8_t *gfx) +void z88_state::vh_render_8x8(address_space &space, bitmap_ind16 &bitmap, int x, int y, uint16_t pen0, uint16_t pen1, uint32_t offset) { for (int h = 0; h < 8; h++) { - uint8_t data = gfx[h]; + const uint8_t data = space.read_byte(offset + h); for (int b = 0; b < 8; b++) { @@ -62,11 +45,11 @@ void z88_state::vh_render_8x8(bitmap_ind16 &bitmap, int x, int y, uint16_t pen0, } } -void z88_state::vh_render_6x8(bitmap_ind16 &bitmap, int x, int y, uint16_t pen0, uint16_t pen1, uint8_t *gfx) +void z88_state::vh_render_6x8(address_space &space, bitmap_ind16 &bitmap, int x, int y, uint16_t pen0, uint16_t pen1, uint32_t offset) { for (int h = 0; h < 8; h++) { - uint8_t data = gfx[h] << 2; + const uint8_t data = space.read_byte(offset + h) << 2; for (int b = 0; b < 6; b++) { @@ -92,7 +75,8 @@ UPD65031_SCREEN_UPDATE(z88_state::lcd_update) } else { - uint8_t *vram = convert_address(sbf << 11); + address_space &space = m_banks[0]->space(); + const uint32_t vram = sbf << 11; for (int y = 0; y < (Z88_SCREEN_HEIGHT >> 3); y++) { @@ -100,8 +84,8 @@ UPD65031_SCREEN_UPDATE(z88_state::lcd_update) while (x < Z88_SCREEN_WIDTH) { - uint8_t byte0 = vram[(y * 0x100) + c]; - uint8_t byte1 = vram[(y * 0x100) + c + 1]; + const uint8_t byte0 = space.read_byte(vram + (y * 0x100) + c); + const uint8_t byte1 = space.read_byte(vram + (y * 0x100) + c + 1); // inverted graphics? uint16_t pen0 = 0; @@ -120,17 +104,17 @@ UPD65031_SCREEN_UPDATE(z88_state::lcd_update) // low-res 6x8 const uint16_t ch = (byte0 | (byte1 << 8)) & 0x1ff; - uint8_t *char_gfx; + uint32_t char_offset; if ((ch & 0x01c0) == 0x01c0) - char_gfx = convert_address(lores0 << 9) + ((ch & 0x3f) << 3); + char_offset = (lores0 << 9) + ((ch & 0x3f) << 3); else - char_gfx = convert_address(lores1 << 12) + (ch << 3); + char_offset = (lores1 << 12) + (ch << 3); // cursor flash if (flash && (byte1 & Z88_SCR_HW_CURS) == Z88_SCR_HW_CURS) - vh_render_6x8(bitmap, x, y << 3, pen1, pen0, char_gfx); + vh_render_6x8(space, bitmap, x, y << 3, pen1, pen0, char_offset); else - vh_render_6x8(bitmap, x, y << 3, pen0, pen1, char_gfx); + vh_render_6x8(space, bitmap, x, y << 3, pen0, pen1, char_offset); // underline? if (byte1 & Z88_SCR_HW_UND) @@ -143,17 +127,17 @@ UPD65031_SCREEN_UPDATE(z88_state::lcd_update) // high-res 8x8 const uint16_t ch = (byte0 | (byte1 << 8)) & 0x3ff; - uint8_t *char_gfx; + uint32_t char_offset; if (BIT(ch, 8)) - char_gfx = convert_address(hires1 << 11) + ((ch & 0xff) << 3); + char_offset = (hires1 << 11) + ((ch & 0xff) << 3); else - char_gfx = convert_address(hires0 << 13) + ((ch & 0xff) << 3); + char_offset = (hires0 << 13) + ((ch & 0xff) << 3); // flash if ((byte1 & Z88_SCR_HW_FLS) && flash) pen0 = pen1 = 0; - vh_render_8x8(bitmap, x, y << 3, pen0, pen1, char_gfx); + vh_render_8x8(space, bitmap, x, y << 3, pen0, pen1, char_offset); x += 8; } -- cgit v1.2.3