// license:BSD-3-Clause
// copyright-holders:Frank Palazzolo
// MAME driver for Grant Searle's Simple CP/M Computer
// http://www.searle.wales/
// This driver uses a compact flash card as a hard disk device.
// To create a virtual disk file, use the following (for a 128MB card):
// chdman createhd -s 134217728 -o filename.chd
// (or use -s 67108864 for 64MB card)
//
// Then, run MAME with -hard filename.chd, or mount it using the GUI
// and restart the driver
//
#include "emu.h"
#include "cpu/z80/z80.h"
#include "bus/ata/ataintf.h"
#include "machine/z80sio.h"
#include "machine/ram.h"
#include "machine/clock.h"
#include "bus/rs232/rs232.h"
class gscpm_state : public driver_device
{
public:
gscpm_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
, m_maincpu(*this, "maincpu")
, m_ram(*this, "ram")
, m_ide(*this, "ide")
, m_sio(*this, "sio")
{ }
void gscpm(machine_config &config);
protected:
void machine_reset() override;
void gscpm_mem(address_map &map);
void gscpm_io(address_map &map);
uint8_t cflash_r(offs_t offset);
void cflash_w(offs_t offset, uint8_t data);
uint8_t sio_r(offs_t offset);
void sio_w(offs_t offset, uint8_t data);
void switch_to_ram_w(uint8_t data);
required_device<z80_device> m_maincpu;
required_device<ram_device> m_ram;
required_device<ata_interface_device> m_ide;
required_device<z80sio_device> m_sio;
};
void gscpm_state::gscpm_mem(address_map &map)
{
//map(0x0000, 0x3fff).rom("maincpu"); // This is ROM after reset, and RAM is switched in when CP/M is booted
// (will install handlers dynamically)
map(0x4000, 0xffff).ram();
}
void gscpm_state::gscpm_io(address_map &map)
{
map.global_mask(0xff); // use 8-bit ports
map.unmap_value_high(); // unmapped addresses return 0xff
map(0x00, 0x07).rw(FUNC(gscpm_state::sio_r), FUNC(gscpm_state::sio_w));
map(0x10, 0x17).rw(FUNC(gscpm_state::cflash_r), FUNC(gscpm_state::cflash_w)); // compact flash
map(0x38, 0x3f).w(FUNC(gscpm_state::switch_to_ram_w));
}
uint8_t gscpm_state::cflash_r(offs_t offset)
{
return m_ide->cs0_r(offset, 0xff);
}
void gscpm_state::cflash_w(offs_t offset, uint8_t data)
{
m_ide->cs0_w(offset, data, 0xff);
}
uint8_t gscpm_state::sio_r(offs_t offset)
{
switch (offset & 3)
{
case 0x00:
return m_sio->da_r();
case 0x01:
return m_sio->db_r();
case 0x02:
return m_sio->ca_r();
case 0x03:
return m_sio->cb_r();
}
return 0x00; // can't happen
}
void gscpm_state::sio_w(offs_t offset, uint8_t data)
{
switch (offset & 3)
{
case 0x00:
m_sio->da_w(data);
break;
case 0x01:
m_sio->db_w(data);
break;
case 0x02:
m_sio->ca_w(data);
break;
case 0x03:
m_sio->cb_w(data);
break;
}
}
void gscpm_state::switch_to_ram_w(uint8_t data)
{
// Install the RAM handler here
m_maincpu->space(AS_PROGRAM).unmap_readwrite(0x0000, 0x3fff); // Unmap the ROM handler
m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x3fff, m_ram->pointer());
}
void gscpm_state::machine_reset()
{
driver_device::machine_reset();
// Install the ROM handler here
m_maincpu->space(AS_PROGRAM).unmap_readwrite(0x0000, 0x3fff); // Unmap RAM handler if being rebooted
m_maincpu->space(AS_PROGRAM).install_rom(0x0000, 0x3fff, memregion("maincpu")->base());
}
// This is here only to configure our terminal for interactive use
static DEVICE_INPUT_DEFAULTS_START( terminal )
DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_115200 )
DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_115200 )
DEVICE_INPUT_DEFAULTS( "RS232_STARTBITS", 0xff, RS232_STARTBITS_1 )
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_END
static const z80_daisy_config gscpm_daisy_chain[] =
{
{ "sio" },
{ nullptr }
};
void gscpm_state::gscpm(machine_config &config)
{
/* basic machine hardware */
Z80(config, m_maincpu, XTAL(7'372'800));
m_maincpu->set_addrmap(AS_PROGRAM, &gscpm_state::gscpm_mem);
m_maincpu->set_addrmap(AS_IO, &gscpm_state::gscpm_io);
m_maincpu->set_daisy_config(gscpm_daisy_chain);
/* compact flash hard drive */
ATA_INTERFACE(config, m_ide).options(ata_devices, "hdd", nullptr, false);
RAM(config, m_ram).set_default_size("16K"); // This shadows the ROM
Z80SIO(config, m_sio, 0);
m_sio->out_txdb_callback().set("rs232", FUNC(rs232_port_device::write_txd));
m_sio->out_rtsb_callback().set("rs232", FUNC(rs232_port_device::write_rts));
m_sio->out_int_callback().set_inputline("maincpu", INPUT_LINE_IRQ0); // Connect interrupt pin to our Z80 INT line
clock_device &sio_clock(CLOCK(config, "sio_clock", 7'372'800));
sio_clock.signal_handler().set("sio", FUNC(z80sio_device::txca_w));
sio_clock.signal_handler().append("sio", FUNC(z80sio_device::rxca_w));
sio_clock.signal_handler().append("sio", FUNC(z80sio_device::txcb_w));
sio_clock.signal_handler().append("sio", FUNC(z80sio_device::rxcb_w));
rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
rs232.rxd_handler().set("sio", FUNC(z80sio_device::rxb_w));
rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal)); // must be below the DEVICE_INPUT_DEFAULTS_START block
}
// ROM mapping is trivial, this binary was created from the HEX file on Grant's website
ROM_START(gscpm)
ROM_REGION(0x4000, "maincpu",0)
ROM_LOAD("gscpm.bin", 0x0000, 0x4000, CRC(35ae0d43) SHA1(7fae4df419d38a1787a4a97cbef558f402109959))
ROM_END
// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
COMP( 201?, gscpm, 0, 0, gscpm, 0, gscpm_state, empty_init, "Grant Searle", "Simple CP/M Machine", MACHINE_NO_SOUND_HW )