summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/drivers/gscpm.cpp
blob: 0dbbebe454cc2ab94509cf83079b23ea3d8d0b90 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// 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_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 )