1 // license:BSD-3-Clause
2 // copyright-holders:Curt Coder, Mike Naberezny
3 /****************************************************************************
4 
5     SSE HardBox emulation
6 
7 ****************************************************************************/
8 
9 /*
10     http://mikenaberezny.com/hardware/pet-cbm/sse-hardbox-corvus-interface/
11 
12     The HardBox provides a CBM DOS interface for a Corvus hard disk.  Before
13     it can be used, a hard disk image must first be created and formatted.
14     Use the CHDMAN utility to create a 20MB image:
15 
16     $ chdman createhd -o /path/to/corvus20mb.chd -chs 388,5,20 -ss 512
17 
18     Start the pet8032 emulator with the HardBox attached as device 9,
19     with the new CHD and the utilities floppy mounted:
20 
21     $ mame pet8032 -ieee9 hardbox \
22                    -hard1 /path/to/corvus20mb.chd \
23                    -flop1 /path/to/hardbox-utils.d80
24 
25     Load and run the "configure" program from the floppy.  When prompted
26     for the HardBox device number, enter "9".
27 
28     Select "q" for quick configure at the menu.  It will present a default
29     drive size and ask if you want to alter it.  If the size is not 20,
30     change it to 20.
31 
32     After accepting the drive size, it will prompt if you want to perform
33     a format check.  This is optional.  If you enter "y" to proceed with
34     the format check, it will always report no bad sectors.
35 
36     Enter "y" to proceed with the format.  After it has completed, the
37     program will exit back to BASIC.  The drive should now be usable.
38 */
39 
40 
41 #include "emu.h"
42 #include "hardbox.h"
43 
44 
45 
46 //**************************************************************************
47 //  MACROS / CONSTANTS
48 //**************************************************************************
49 
50 #define Z80_TAG         "z80"
51 #define I8255_0_TAG     "ic17"
52 #define I8255_1_TAG     "ic16"
53 #define CORVUS_HDC_TAG  "corvus"
54 
55 
56 
57 //**************************************************************************
58 //  DEVICE DEFINITIONS
59 //**************************************************************************
60 
61 DEFINE_DEVICE_TYPE(HARDBOX, hardbox_device, "hardbox", "SSE HardBox")
62 
63 
64 //-------------------------------------------------
65 //  ROM( hardbox )
66 //-------------------------------------------------
67 
ROM_START(hardbox)68 ROM_START( hardbox )
69 	ROM_REGION( 0x2000, Z80_TAG, 0 )
70 	ROM_DEFAULT_BIOS("v2.4")
71 
72 	ROM_SYSTEM_BIOS( 0, "v2.3", "Version 2.3 (Corvus)" )
73 	ROMX_LOAD( "295-2.3.ic3", 0x0000, 0x1000, CRC(a3eb5fc2) SHA1(39941b45b0696db928615c41c7eae18d951d9ada), ROM_BIOS(0) )
74 	ROMX_LOAD( "296-2.3.ic4", 0x1000, 0x1000, CRC(fb55b058) SHA1(8f9ec313ec6beaf7b513edf39d9628e6abcc7bc3), ROM_BIOS(0) )
75 
76 	ROM_SYSTEM_BIOS( 1, "v2.4", "Version 2.4 (Corvus)" )
77 	ROMX_LOAD( "289.ic3", 0x0000, 0x1000, CRC(c39e058f) SHA1(45b390d7125a40f84c7b411a479218baff079746), ROM_BIOS(1) )
78 	ROMX_LOAD( "290.ic4", 0x1000, 0x1000, CRC(62f51405) SHA1(fdfa0d7b7e8d0182f2df0aa8163c790506104dcf), ROM_BIOS(1) )
79 
80 	ROM_SYSTEM_BIOS( 2, "v3.1", "Version 3.1 (Sunol)" )
81 	ROMX_LOAD( "295-3.1.ic3", 0x0000, 0x1000, CRC(654a5db1) SHA1(c40859526921e3d8bfd58fc28cc9cc64e59ec638), ROM_BIOS(2) )
82 	ROMX_LOAD( "296-3.1.ic4", 0x1000, 0x1000, CRC(4c62ddc0) SHA1(151f99dc554d3762b805fc8383cf1b3e1455784f), ROM_BIOS(2) )
83 
84 	/* Note: Two sets of EPROMs were found marked only "295" and "296" but they have different contents.
85 	         The version numbers listed are the ROM version reported by the HardBox diagnostics program.
86 	         Disassembling the ROMs showed that v2.3 and v2.4 are for Corvus Systems drives but v3.1 is
87 	         for Sunol Systems drives.  Both types use the Corvus flat cable interface but there may be
88 	         some programming differences, e.g. the v3.1 firmware for Sunol does not have the park heads
89 	     routine in the Corvus versions.  MAME emulates a Corvus drive so we default to the last
90 	     known HardBox firmware for Corvus (v2.4). */
91 ROM_END
92 
93 
94 //-------------------------------------------------
95 //  rom_region - device-specific ROM region
96 //-------------------------------------------------
97 
98 const tiny_rom_entry *hardbox_device::device_rom_region() const
99 {
100 	return ROM_NAME( hardbox );
101 }
102 
103 
104 //-------------------------------------------------
105 //  ADDRESS_MAP( hardbox_mem )
106 //-------------------------------------------------
107 
hardbox_mem(address_map & map)108 void hardbox_device::hardbox_mem(address_map &map)
109 {
110 	map(0x0000, 0x3fff).ram();
111 	map(0xe000, 0xffff).rom().region(Z80_TAG, 0);
112 }
113 
114 
115 //-------------------------------------------------
116 //  ADDRESS_MAP( hardbox_io )
117 //-------------------------------------------------
118 
hardbox_io(address_map & map)119 void hardbox_device::hardbox_io(address_map &map)
120 {
121 	map.global_mask(0xff);
122 	map(0x10, 0x13).rw(I8255_0_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write));
123 	map(0x14, 0x17).rw(I8255_1_TAG, FUNC(i8255_device::read), FUNC(i8255_device::write));
124 	map(0x18, 0x18).rw(CORVUS_HDC_TAG, FUNC(corvus_hdc_device::read), FUNC(corvus_hdc_device::write));
125 }
126 
127 
128 //-------------------------------------------------
129 //  I8255A 0 interface
130 //-------------------------------------------------
131 
ppi0_pa_r()132 uint8_t hardbox_device::ppi0_pa_r()
133 {
134 	return m_bus->dio_r() ^ 0xff;
135 }
136 
ppi0_pb_w(uint8_t data)137 void hardbox_device::ppi0_pb_w(uint8_t data)
138 {
139 	m_bus->dio_w(this, data ^ 0xff);
140 }
141 
ppi0_pc_r()142 uint8_t hardbox_device::ppi0_pc_r()
143 {
144 	uint8_t data = ioport("SW1")->read();
145 
146 	/* DIP switches on PC1,PC2,PC3 configure the IEEE-488 primary address.
147 	   We get the address from the slot instead. */
148 	data |= ((m_slot->get_address() - 8) << 1) ^ 0xff;
149 
150 	return data;
151 }
152 
153 //-------------------------------------------------
154 //  I8255A 1 interface
155 //-------------------------------------------------
156 
ppi1_pa_r()157 uint8_t hardbox_device::ppi1_pa_r()
158 {
159 	/*
160 
161 	  bit     description
162 
163 	  PA0     ATN
164 	  PA1     DAV
165 	  PA2     NDAC
166 	  PA3     NRFD
167 	  PA4     EOI
168 	  PA5     SRQ
169 	  PA6     REN
170 	  PA7     IFC
171 
172 	*/
173 
174 	uint8_t data = 0;
175 
176 	data |= !m_bus->atn_r();
177 	data |= !m_bus->dav_r() << 1;
178 	data |= !m_bus->ndac_r() << 2;
179 	data |= !m_bus->nrfd_r() << 3;
180 	data |= !m_bus->eoi_r() << 4;
181 	data |= !m_bus->srq_r() << 5;
182 	data |= !m_bus->ren_r() << 6;
183 	data |= !m_bus->ifc_r() << 7;
184 
185 	return data;
186 }
187 
ppi1_pb_w(uint8_t data)188 void hardbox_device::ppi1_pb_w(uint8_t data)
189 {
190 	/*
191 
192 	  bit     description
193 
194 	  PB0     ATN
195 	  PB1     DAV
196 	  PB2     NDAC
197 	  PB3     NRFD
198 	  PB4     EOI
199 	  PB5     SRQ
200 	  PB6     REN
201 	  PB7     IFC
202 
203 	  Note: When the PCB is configured as a HardBox instead of a SoftBox,
204 	        IFC is read only.  Do not connect IFC for output here.
205 
206 	*/
207 
208 	m_bus->atn_w(this, !BIT(data, 0));
209 	m_bus->dav_w(this, !BIT(data, 1));
210 	m_bus->ndac_w(this, !BIT(data, 2));
211 	m_bus->nrfd_w(this, !BIT(data, 3));
212 	m_bus->eoi_w(this, !BIT(data, 4));
213 	m_bus->srq_w(this, !BIT(data, 5));
214 	m_bus->ren_w(this, !BIT(data, 6));
215 }
216 
ppi1_pc_r()217 uint8_t hardbox_device::ppi1_pc_r()
218 {
219 	/*
220 
221 	  bit     description
222 
223 	  PC0
224 	  PC1
225 	  PC2
226 	  PC3
227 	  PC4     Corvus READY
228 	  PC5     Corvus DIRC
229 	  PC6
230 	  PC7
231 
232 	*/
233 
234 	uint8_t status = m_hdc->status_r();
235 	uint8_t data = 0;
236 
237 	data |= (status & corvus_hdc_device::CONTROLLER_BUSY) ? 0 : 0x10;
238 	data |= (status & corvus_hdc_device::CONTROLLER_DIRECTION) ? 0 : 0x20;
239 
240 	return data;
241 }
242 
ppi1_pc_w(uint8_t data)243 void hardbox_device::ppi1_pc_w(uint8_t data)
244 {
245 	/*
246 
247 	  bit     description
248 
249 	  PC0     LED "A"
250 	  PC1     LED "B"
251 	  PC2     LED "READY"
252 	  PC3
253 	  PC4
254 	  PC5
255 	  PC6
256 	  PC7
257 
258 	*/
259 
260 	m_leds[LED_A] = BIT(~data, 0);
261 	m_leds[LED_B] = BIT(~data, 1);
262 	m_leds[LED_READY] = BIT(~data, 2);
263 }
264 
265 
266 //-------------------------------------------------
267 //  device_add_mconfig - add device configuration
268 //-------------------------------------------------
269 
device_add_mconfig(machine_config & config)270 void hardbox_device::device_add_mconfig(machine_config &config)
271 {
272 	// basic machine hardware
273 	Z80(config, m_maincpu, XTAL(8'000'000)/2);
274 	m_maincpu->set_addrmap(AS_PROGRAM, &hardbox_device::hardbox_mem);
275 	m_maincpu->set_addrmap(AS_IO, &hardbox_device::hardbox_io);
276 
277 	// devices
278 	i8255_device &ppi0(I8255A(config, I8255_0_TAG));
279 	ppi0.in_pa_callback().set(FUNC(hardbox_device::ppi0_pa_r));
280 	ppi0.out_pb_callback().set(FUNC(hardbox_device::ppi0_pb_w));
281 	ppi0.in_pc_callback().set(FUNC(hardbox_device::ppi0_pc_r));
282 
283 	i8255_device &ppi1(I8255A(config, I8255_1_TAG));
284 	ppi1.in_pa_callback().set(FUNC(hardbox_device::ppi1_pa_r));
285 	ppi1.out_pb_callback().set(FUNC(hardbox_device::ppi1_pb_w));
286 	ppi1.in_pc_callback().set(FUNC(hardbox_device::ppi1_pc_r));
287 	ppi1.out_pc_callback().set(FUNC(hardbox_device::ppi1_pc_w));
288 
289 	CORVUS_HDC(config, m_hdc, 0);
290 	HARDDISK(config, "harddisk1", "corvus_hdd");
291 	HARDDISK(config, "harddisk2", "corvus_hdd");
292 	HARDDISK(config, "harddisk3", "corvus_hdd");
293 	HARDDISK(config, "harddisk4", "corvus_hdd");
294 }
295 
296 
297 //-------------------------------------------------
298 //  INPUT_PORTS( hardbox )
299 //-------------------------------------------------
300 
301 INPUT_PORTS_START( hardbox )
302 	PORT_START("SW1")
303 	PORT_DIPUNKNOWN_DIPLOC( 0x01, IP_ACTIVE_LOW, "SW1:1" )
304 	PORT_DIPNAME( 0x0e, 0x0c, "Device Address" ) PORT_DIPLOCATION("SW1:2,3,4")
305 	PORT_DIPSETTING(    0x0e, "8" )
306 	PORT_DIPSETTING(    0x0c, "9" )
307 	PORT_DIPSETTING(    0x0a, "10" )
308 	PORT_DIPSETTING(    0x08, "11" )
309 	PORT_DIPSETTING(    0x06, "12" )
310 	PORT_DIPSETTING(    0x04, "13" )
311 	PORT_DIPSETTING(    0x02, "14" )
312 	PORT_DIPSETTING(    0x00, "15" )
313 	PORT_DIPUNKNOWN_DIPLOC( 0x10, IP_ACTIVE_LOW, "SW1:5" )
314 	PORT_DIPUNKNOWN_DIPLOC( 0x20, IP_ACTIVE_LOW, "SW1:6" )
315 	PORT_DIPUNKNOWN_DIPLOC( 0x40, IP_ACTIVE_LOW, "SW1:7" )
316 	PORT_DIPUNKNOWN_DIPLOC( 0x80, IP_ACTIVE_LOW, "SW1:8" )
317 INPUT_PORTS_END
318 
319 
320 //-------------------------------------------------
321 //  input_ports - device-specific input ports
322 //-------------------------------------------------
323 
device_input_ports() const324 ioport_constructor hardbox_device::device_input_ports() const
325 {
326 	return INPUT_PORTS_NAME( hardbox );
327 }
328 
329 
330 
331 //**************************************************************************
332 //  LIVE DEVICE
333 //**************************************************************************
334 
335 //-------------------------------------------------
336 //  hardbox_device - constructor
337 //-------------------------------------------------
338 
hardbox_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)339 hardbox_device::hardbox_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
340 	: device_t(mconfig, HARDBOX, tag, owner, clock)
341 	, device_ieee488_interface(mconfig, *this)
342 	, m_maincpu(*this, Z80_TAG)
343 	, m_hdc(*this, CORVUS_HDC_TAG)
344 	, m_leds(*this, "led%u", 0U)
345 	, m_ifc(0)
346 {
347 }
348 
349 
350 //-------------------------------------------------
351 //  device_start - device-specific startup
352 //-------------------------------------------------
353 
device_start()354 void hardbox_device::device_start()
355 {
356 	m_leds.resolve();
357 }
358 
359 
360 //-------------------------------------------------
361 //  device_reset_after_children - device-specific
362 //    reset that must happen after child devices
363 //    have performed their resets
364 //-------------------------------------------------
365 
device_reset_after_children()366 void hardbox_device::device_reset_after_children()
367 {
368 	/* The Z80 starts at address 0x0000 but the HardBox has RAM there and
369 	   needs to start from the BIOS at 0xe000.  The PCB has logic and a
370 	   74S287 PROM that temporarily changes the memory map so that the
371 	   IC3 EPROM at 0xe000 is mapped to 0x0000 for the first instruction
372 	   fetch only.  The instruction normally at 0xe000 is an absolute jump
373 	   into the ROM.  On reset, the Z80 will fetch it from 0x0000 and set
374 	   its PC, then the normal map will be restored before the next
375 	   instruction fetch.  Here we just set the PC to 0xe000 after the Z80
376 	   resets, which has the same effect. */
377 
378 	m_maincpu->set_state_int(Z80_PC, 0xe000);
379 }
380 
381 
382 //-------------------------------------------------
383 //  ieee488_ifc - interface clear (reset)
384 //-------------------------------------------------
385 
ieee488_ifc(int state)386 void hardbox_device::ieee488_ifc(int state)
387 {
388 	if (!m_ifc && state)
389 	{
390 		device_reset();
391 	}
392 
393 	m_ifc = state;
394 }
395