Z88: added serial port and NVRAM support. [Sandro Ronco]

This commit is contained in:
Sandro Ronco 2022-05-08 14:00:53 +02:00
parent a9f4cae377
commit b2aa336249
16 changed files with 859 additions and 267 deletions

View File

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

View File

@ -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();
}
/*-------------------------------------------------

View File

@ -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<intelfsh8_device> m_flash;
required_memory_region m_region;
};
// device type definition

View File

@ -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)
{
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<cpu_device> m_maincpu;
required_memory_region m_bios_region;
required_device<nvram_device> m_nvram;
required_device<ram_device> m_ram;
required_device<screen_device> m_screen;
required_device<palette_device> m_palette;
required_device<upd65031_device> m_blink;
required_ioport_array<8> m_lines;
required_memory_bank_array<5> m_banks;
required_device_array<address_map_bank_device, 4> m_banks;
optional_device_array<z88cart_slot_device, 4> 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

View File

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

View File

@ -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<void (bitmap_ind16 &bitmap, uint16_t sbf, uint16_t hires0, uint16_t hires1, uint16_t lores0, uint16_t lores1, int flash)> 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 <typename... T> void set_screen_update_callback(T &&... args) { m_screen_update_cb.set(std::forward<T>(args)...); }
template <typename... T> void set_memory_update_callback(T &&... args) { m_out_mem_cb.set(std::forward<T>(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

View File

@ -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<uint8_t>();
}
//-------------------------------------------------
// 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<uint8_t>();
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<uint8_t>();
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<uint8_t>();
m_mode = MODE_IDLE;
}

View File

@ -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 <queue>
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<uint8_t> m_queue;
};
DECLARE_DEVICE_TYPE(Z88_IMPEXP, z88_impexp_device)
#endif // MAME_MACHINE_Z88_IMPEXP_H

View File

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