1 // license:GPL-2.0+
2 // copyright-holders:Dirk Best
3 /***************************************************************************
4
5 ACT Apricot PC/Xi
6
7 TODO:
8 - External RS232 data transfers to the Apricot are usually garbage (but
9 sending to an external target works fine)
10 - Dump of the keyboard MCU ROM needed (can be dumped using test mode)
11
12 ***************************************************************************/
13
14 #include "emu.h"
15 #include "bus/apricot/expansion/expansion.h"
16 #include "bus/apricot/keyboard/keyboard.h"
17 #include "bus/centronics/ctronics.h"
18 #include "bus/rs232/rs232.h"
19 #include "cpu/i86/i86.h"
20 #include "cpu/i8089/i8089.h"
21 #include "formats/apridisk.h"
22 #include "imagedev/floppy.h"
23 #include "machine/clock.h"
24 #include "machine/ram.h"
25 #include "machine/74153.h"
26 #include "machine/i8255.h"
27 #include "machine/pic8259.h"
28 #include "machine/pit8253.h"
29 #include "machine/wd_fdc.h"
30 #include "machine/z80sio.h"
31 #include "sound/sn76496.h"
32 #include "video/mc6845.h"
33 #include "emupal.h"
34 #include "screen.h"
35 #include "softlist.h"
36 #include "speaker.h"
37
38
39 //**************************************************************************
40 // TYPE DEFINITIONS
41 //**************************************************************************
42
43 class apricot_state : public driver_device
44 {
45 public:
apricot_state(const machine_config & mconfig,device_type type,const char * tag)46 apricot_state(const machine_config &mconfig, device_type type, const char *tag) :
47 driver_device(mconfig, type, tag),
48 m_cpu(*this, "ic91"),
49 m_iop(*this, "ic71"),
50 m_ram(*this, RAM_TAG),
51 m_crtc(*this, "ic30"),
52 m_ppi(*this, "ic17"),
53 m_pic(*this, "ic31"),
54 m_pit(*this, "ic16"),
55 m_sio(*this, "ic15"),
56 m_rs232(*this, "rs232"),
57 m_centronics(*this, "centronics"),
58 m_fdc(*this, "ic68"),
59 m_floppy0(*this, "ic68:0"),
60 m_floppy1(*this, "ic68:1"),
61 m_palette(*this, "palette"),
62 m_exp(*this, "exp"),
63 m_screen_buffer(*this, "screen_buffer"),
64 m_video_mode(0),
65 m_display_on(1),
66 m_display_enabled(0),
67 m_centronics_fault(1),
68 m_centronics_perror(1),
69 m_bus_locked(0)
70 { }
71
72 void apricot(machine_config &config);
73 void apricotxi(machine_config &config);
74
75 private:
76 DECLARE_FLOPPY_FORMATS(floppy_formats);
77
78 DECLARE_WRITE_LINE_MEMBER(i8086_lock_w);
79 void i8089_ca1_w(uint8_t data);
80 void i8089_ca2_w(uint8_t data);
81 void i8255_portb_w(uint8_t data);
82 uint8_t i8255_portc_r();
83 void i8255_portc_w(uint8_t data);
84 DECLARE_WRITE_LINE_MEMBER(fdc_intrq_w);
85 uint8_t sio_da_r();
86 uint8_t sio_ca_r();
87 uint8_t sio_db_r();
88 uint8_t sio_cb_r();
89
90 DECLARE_WRITE_LINE_MEMBER(write_centronics_fault);
91 DECLARE_WRITE_LINE_MEMBER(write_centronics_perror);
92
DECLARE_WRITE_LINE_MEMBER(apricot_hd6845_de)93 DECLARE_WRITE_LINE_MEMBER(apricot_hd6845_de) { m_display_enabled = state; };
94
95 MC6845_UPDATE_ROW(crtc_update_row);
96 uint32_t screen_update_apricot(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
97
98 void apricot_io(address_map &map);
99 void apricot_mem(address_map &map);
100
101 virtual void machine_start() override;
102
103 required_device<i8086_cpu_device> m_cpu;
104 required_device<i8089_device> m_iop;
105 required_device<ram_device> m_ram;
106 required_device<hd6845s_device> m_crtc;
107 required_device<i8255_device> m_ppi;
108 required_device<pic8259_device> m_pic;
109 required_device<pit8253_device> m_pit;
110 required_device<z80sio_device> m_sio;
111 required_device<rs232_port_device> m_rs232;
112 required_device<centronics_device> m_centronics;
113 required_device<wd2797_device> m_fdc;
114 required_device<floppy_connector> m_floppy0;
115 required_device<floppy_connector> m_floppy1;
116 required_device<palette_device> m_palette;
117 required_device<apricot_expansion_bus_device> m_exp;
118 required_shared_ptr<uint16_t> m_screen_buffer;
119
120 bool m_video_mode;
121 bool m_display_on;
122
123 int m_display_enabled;
124
125 int m_centronics_fault;
126 int m_centronics_perror;
127
128 int m_bus_locked;
129 };
130
131
132 //**************************************************************************
133 // I/O
134 //**************************************************************************
135
i8089_ca1_w(uint8_t data)136 void apricot_state::i8089_ca1_w(uint8_t data)
137 {
138 m_iop->sel_w(0);
139 m_iop->ca_w(1);
140 m_iop->ca_w(0);
141 }
142
i8089_ca2_w(uint8_t data)143 void apricot_state::i8089_ca2_w(uint8_t data)
144 {
145 m_iop->sel_w(1);
146 m_iop->ca_w(1);
147 m_iop->ca_w(0);
148 }
149
WRITE_LINE_MEMBER(apricot_state::write_centronics_fault)150 WRITE_LINE_MEMBER( apricot_state::write_centronics_fault )
151 {
152 m_centronics_fault = state;
153 m_sio->syncb_w(state);
154 m_ppi->pc2_w(state);
155 }
156
WRITE_LINE_MEMBER(apricot_state::write_centronics_perror)157 WRITE_LINE_MEMBER( apricot_state::write_centronics_perror )
158 {
159 m_centronics_perror = state;
160 }
161
i8255_portc_r()162 uint8_t apricot_state::i8255_portc_r()
163 {
164 uint8_t data = 0;
165
166 data |= m_centronics_perror << 0;
167 // schematic page 294 says pc1 is centronics pin 34, which is n/c.
168 data |= m_centronics_fault << 2;
169 data |= m_display_enabled << 3;
170
171 return data;
172 }
173
i8255_portb_w(uint8_t data)174 void apricot_state::i8255_portb_w(uint8_t data)
175 {
176 // bit 0, crt reset
177 // bit 1, not connected
178
179 m_display_on = BIT(data, 3);
180 m_video_mode = BIT(data, 4);
181
182 floppy_image_device *floppy = nullptr;
183
184 // bit 5, enable disk select
185 // bit 6, disk select
186 if (!BIT(data, 5))
187 floppy = BIT(data, 6) ? m_floppy1->get_device() : m_floppy0->get_device();
188
189 m_fdc->set_floppy(floppy);
190
191 // bit 2, head load (motor on is wired to be active once a disk has been inserted)
192 // we just let the motor run all the time for now
193 if (floppy)
194 floppy->mon_w(0);
195
196 // switch video modes
197 m_crtc->set_unscaled_clock(15_MHz_XTAL / (m_video_mode ? 10 : 16));
198 m_crtc->set_hpixels_per_column(m_video_mode ? 10 : 16);
199
200 // PB7 Centronics transceiver direction. 0 = output, 1 = input
201 }
202
i8255_portc_w(uint8_t data)203 void apricot_state::i8255_portc_w(uint8_t data)
204 {
205 // schematic page 294 says pc4 outputs to centronics pin 13, which is the "select" output from the printer.
206 m_centronics->write_strobe(BIT(data, 5));
207 // schematic page 294 says pc6 outputs to centronics pin 15, which is unused
208 }
209
sio_da_r()210 uint8_t apricot_state::sio_da_r()
211 {
212 if (m_bus_locked)
213 return m_sio->m1_r();
214
215 return m_sio->da_r();
216 }
217
sio_ca_r()218 uint8_t apricot_state::sio_ca_r()
219 {
220 if (m_bus_locked)
221 return m_sio->m1_r();
222
223 return m_sio->ca_r();
224 }
225
sio_cb_r()226 uint8_t apricot_state::sio_cb_r()
227 {
228 if (m_bus_locked)
229 return m_sio->m1_r();
230
231 return m_sio->cb_r();
232 }
233
sio_db_r()234 uint8_t apricot_state::sio_db_r()
235 {
236 if (m_bus_locked)
237 return m_sio->m1_r();
238
239 return m_sio->db_r();
240 }
241
242
243 //**************************************************************************
244 // FLOPPY
245 //**************************************************************************
246
WRITE_LINE_MEMBER(apricot_state::fdc_intrq_w)247 WRITE_LINE_MEMBER( apricot_state::fdc_intrq_w )
248 {
249 m_pic->ir4_w(state);
250 m_iop->ext1_w(state);
251 }
252
FLOPPY_FORMATS_MEMBER(apricot_state::floppy_formats)253 FLOPPY_FORMATS_MEMBER( apricot_state::floppy_formats )
254 FLOPPY_APRIDISK_FORMAT
255 FLOPPY_FORMATS_END
256
257 static void apricot_floppies(device_slot_interface &device)
258 {
259 device.option_add("d31v", SONY_OA_D31V);
260 device.option_add("d32w", SONY_OA_D32W);
261 }
262
263
264 //**************************************************************************
265 // VIDEO EMULATION
266 //**************************************************************************
267
screen_update_apricot(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)268 uint32_t apricot_state::screen_update_apricot(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
269 {
270 if (!m_display_on)
271 m_crtc->screen_update(screen, bitmap, cliprect);
272 else
273 bitmap.fill(rgb_t::black(), cliprect);
274
275 return 0;
276 }
277
MC6845_UPDATE_ROW(apricot_state::crtc_update_row)278 MC6845_UPDATE_ROW( apricot_state::crtc_update_row )
279 {
280 const pen_t *pen = m_palette->pens();
281
282 for (int i = 0; i < x_count; i++)
283 {
284 uint16_t code = m_screen_buffer[(ma + i) & 0x7ff];
285 uint16_t offset = ((code & 0x7ff) << 5) | (ra << 1);
286 uint16_t data = m_cpu->space(AS_PROGRAM).read_word(offset);
287
288 if (m_video_mode)
289 {
290 int fill = 0;
291
292 if (i == cursor_x) fill = 1; // cursor?
293 if (BIT(code, 12) && BIT(data, 14)) fill = 1; // strike-through?
294 if (BIT(code, 13) && BIT(data, 15)) fill = 1; // underline?
295
296 // draw 10 pixels of the character
297 for (int x = 0; x <= 10; x++)
298 {
299 int color = fill ? 1 : BIT(data, x);
300 color ^= BIT(code, 15); // reverse?
301 bitmap.pix(y, x + i*10) = pen[color ? 1 + BIT(code, 14) : 0];
302 }
303 }
304 else
305 {
306 // draw 16 pixels of the cell
307 for (int x = 0; x <= 16; x++)
308 bitmap.pix(y, x + i*16) = pen[BIT(data, x)];
309 }
310 }
311 }
312
313 //**************************************************************************
314 // MACHINE EMULATION
315 //**************************************************************************
316
machine_start()317 void apricot_state::machine_start()
318 {
319 membank("ram")->set_base(m_ram->pointer());
320 }
321
WRITE_LINE_MEMBER(apricot_state::i8086_lock_w)322 WRITE_LINE_MEMBER( apricot_state::i8086_lock_w )
323 {
324 m_bus_locked = state;
325 }
326
327
328 //**************************************************************************
329 // ADDRESS MAPS
330 //**************************************************************************
331
apricot_mem(address_map & map)332 void apricot_state::apricot_mem(address_map &map)
333 {
334 map(0x00000, 0x3ffff).bankrw("ram");
335 map(0xf0000, 0xf0fff).mirror(0x7000).ram().share("screen_buffer");
336 map(0xf8000, 0xfbfff).mirror(0x4000).rom().region("bootstrap", 0);
337 }
338
apricot_io(address_map & map)339 void apricot_state::apricot_io(address_map &map)
340 {
341 map(0x00, 0x03).rw(m_pic, FUNC(pic8259_device::read), FUNC(pic8259_device::write)).umask16(0x00ff);
342 map(0x40, 0x47).rw(m_fdc, FUNC(wd2797_device::read), FUNC(wd2797_device::write)).umask16(0x00ff);
343 map(0x48, 0x4f).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0x00ff);
344 map(0x50, 0x50).mirror(0x06).w("ic7", FUNC(sn76489_device::write));
345 map(0x58, 0x5f).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write)).umask16(0x00ff);
346 map(0x60, 0x60).r(FUNC(apricot_state::sio_da_r)).w(m_sio, FUNC(z80sio_device::da_w)).umask16(0x00ff);
347 map(0x62, 0x62).r(FUNC(apricot_state::sio_ca_r)).w(m_sio, FUNC(z80sio_device::ca_w)).umask16(0x00ff);
348 map(0x64, 0x64).r(FUNC(apricot_state::sio_db_r)).w(m_sio, FUNC(z80sio_device::db_w)).umask16(0x00ff);
349 map(0x66, 0x66).r(FUNC(apricot_state::sio_cb_r)).w(m_sio, FUNC(z80sio_device::cb_w)).umask16(0x00ff);
350 map(0x68, 0x68).mirror(0x04).w(m_crtc, FUNC(hd6845s_device::address_w));
351 map(0x6a, 0x6a).mirror(0x04).rw(m_crtc, FUNC(hd6845s_device::register_r), FUNC(hd6845s_device::register_w));
352 map(0x70, 0x70).mirror(0x04).w(FUNC(apricot_state::i8089_ca1_w));
353 map(0x72, 0x72).mirror(0x04).w(FUNC(apricot_state::i8089_ca2_w));
354 map(0x78, 0x7f).noprw(); // unavailable
355 }
356
357
358 //**************************************************************************
359 // MACHINE DRIVERS
360 //**************************************************************************
361
apricot(machine_config & config)362 void apricot_state::apricot(machine_config &config)
363 {
364 // main cpu
365 I8086(config, m_cpu, 15_MHz_XTAL / 3);
366 m_cpu->set_addrmap(AS_PROGRAM, &apricot_state::apricot_mem);
367 m_cpu->set_addrmap(AS_IO, &apricot_state::apricot_io);
368 m_cpu->set_irq_acknowledge_callback("ic31", FUNC(pic8259_device::inta_cb));
369 m_cpu->lock_handler().set(FUNC(apricot_state::i8086_lock_w));
370
371 // i/o cpu
372 I8089(config, m_iop, 15_MHz_XTAL / 3);
373 m_iop->set_addrmap(AS_PROGRAM, &apricot_state::apricot_mem);
374 m_iop->set_addrmap(AS_IO, &apricot_state::apricot_io);
375 m_iop->set_data_width(16);
376 m_iop->sintr1().set(m_pic, FUNC(pic8259_device::ir0_w));
377 m_iop->sintr2().set(m_pic, FUNC(pic8259_device::ir1_w));
378
379 // ram
380 RAM(config, RAM_TAG).set_default_size("256K");
381
382 // video hardware
383 screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
384 screen.set_color(rgb_t::green());
385 screen.set_size(800, 400);
386 screen.set_visarea(0, 800-1, 0, 400-1);
387 screen.set_refresh_hz(72);
388 screen.set_screen_update(FUNC(apricot_state::screen_update_apricot));
389
390 PALETTE(config, m_palette, palette_device::MONOCHROME_HIGHLIGHT);
391
392 HD6845S(config, m_crtc, 15_MHz_XTAL / 10);
393 m_crtc->set_screen("screen");
394 m_crtc->set_show_border_area(false);
395 m_crtc->set_char_width(10);
396 m_crtc->set_update_row_callback(FUNC(apricot_state::crtc_update_row));
397 m_crtc->out_de_callback().set(FUNC(apricot_state::apricot_hd6845_de));
398
399 // sound hardware
400 SPEAKER(config, "mono").front_center();
401 SN76489(config, "ic7", 4_MHz_XTAL / 2).add_route(ALL_OUTPUTS, "mono", 1.0);
402
403 // devices
404 I8255A(config, m_ppi, 0);
405 m_ppi->in_pa_callback().set("cent_data_in", FUNC(input_buffer_device::read));
406 m_ppi->out_pa_callback().set("cent_data_out", FUNC(output_latch_device::write));
407 m_ppi->out_pb_callback().set(FUNC(apricot_state::i8255_portb_w));
408 m_ppi->in_pc_callback().set(FUNC(apricot_state::i8255_portc_r));
409 m_ppi->out_pc_callback().set(FUNC(apricot_state::i8255_portc_w));
410
411 PIC8259(config, m_pic, 0);
412 m_pic->out_int_callback().set_inputline(m_cpu, 0);
413
414 PIT8253(config, m_pit, 0);
415 m_pit->set_clk<0>(4_MHz_XTAL / 16);
416 m_pit->out_handler<0>().set(m_pic, FUNC(pic8259_device::ir6_w));
417 m_pit->set_clk<1>(4_MHz_XTAL / 2);
418 m_pit->out_handler<1>().set("ic14", FUNC(ttl153_device::i0a_w));
419 m_pit->set_clk<2>(4_MHz_XTAL / 2);
420 m_pit->out_handler<2>().set("ic14", FUNC(ttl153_device::i0b_w));
421 m_pit->out_handler<2>().append("ic14", FUNC(ttl153_device::i2a_w));
422 m_pit->out_handler<2>().append("ic14", FUNC(ttl153_device::i2b_w));
423
424 ttl153_device &ttl74153(TTL153(config, "ic14"));
425 ttl74153.za_cb().set("ic15", FUNC(z80sio_device::rxca_w));
426 ttl74153.zb_cb().set("ic15", FUNC(z80sio_device::txca_w));
427
428 CLOCK(config, "ic15_rxtxcb", 4_MHz_XTAL / 16).signal_handler().set(m_sio, FUNC(z80sio_device::rxtxcb_w));
429
430 Z80SIO(config, m_sio, 15_MHz_XTAL / 6);
431 m_sio->set_cputag(m_cpu);
432 m_sio->out_txda_callback().set(m_rs232, FUNC(rs232_port_device::write_txd));
433 m_sio->out_dtra_callback().set(m_rs232, FUNC(rs232_port_device::write_dtr));
434 m_sio->out_rtsa_callback().set(m_rs232, FUNC(rs232_port_device::write_rts));
435 m_sio->out_wrdya_callback().set(m_iop, FUNC(i8089_device::drq2_w));
436 m_sio->out_txdb_callback().set("kbd", FUNC(apricot_keyboard_bus_device::out_w));
437 m_sio->out_dtrb_callback().set("ic14", FUNC(ttl153_device::s0_w));
438 m_sio->out_rtsb_callback().set("ic14", FUNC(ttl153_device::s1_w));
439 m_sio->out_int_callback().set(m_pic, FUNC(pic8259_device::ir5_w));
440
441 // rs232 port
442 RS232_PORT(config, m_rs232, default_rs232_devices, nullptr);
443 // note: missing a receive clock callback to support external clock mode (i1 to 153)
444 m_rs232->rxd_handler().set(m_sio, FUNC(z80sio_device::rxa_w));
445 m_rs232->dcd_handler().set(m_sio, FUNC(z80sio_device::dcda_w));
446 m_rs232->dsr_handler().set(m_sio, FUNC(z80sio_device::synca_w));
447 m_rs232->cts_handler().set(m_sio, FUNC(z80sio_device::ctsa_w)).invert();
448
449 // keyboard
450 APRICOT_KEYBOARD_INTERFACE(config, "kbd", apricot_keyboard_devices, "hle").in_handler().set(m_sio, FUNC(z80sio_device::rxb_w));
451
452 // centronics printer
453 CENTRONICS(config, m_centronics, centronics_devices, "printer");
454 m_centronics->set_data_input_buffer("cent_data_in");
455 m_centronics->ack_handler().set(m_sio, FUNC(z80sio_device::ctsb_w));
456 m_centronics->busy_handler().set(m_sio, FUNC(z80sio_device::dcdb_w));
457 m_centronics->fault_handler().set(FUNC(apricot_state::write_centronics_fault));
458 m_centronics->perror_handler().set(FUNC(apricot_state::write_centronics_perror));
459 //m_centronics->select_handler().set(); // schematic page 294 says this is connected to pc4, but that is an output to the printer
460
461 INPUT_BUFFER(config, "cent_data_in");
462
463 output_latch_device ¢_data_out(OUTPUT_LATCH(config, "cent_data_out"));
464 m_centronics->set_output_latch(cent_data_out);
465
466 // floppy
467 WD2797(config, m_fdc, 4_MHz_XTAL / 2);
468 m_fdc->intrq_wr_callback().set(FUNC(apricot_state::fdc_intrq_w));
469 m_fdc->drq_wr_callback().set(m_iop, FUNC(i8089_device::drq1_w));
470 FLOPPY_CONNECTOR(config, "ic68:0", apricot_floppies, "d32w", apricot_state::floppy_formats);
471 FLOPPY_CONNECTOR(config, "ic68:1", apricot_floppies, "d32w", apricot_state::floppy_formats);
472
473 SOFTWARE_LIST(config, "flop_list").set_original("apricot_flop");
474
475 // expansion bus
476 APRICOT_EXPANSION_BUS(config, m_exp, 0);
477 m_exp->set_program_space(m_cpu, AS_PROGRAM);
478 m_exp->set_io_space(m_cpu, AS_IO);
479 m_exp->set_program_iop_space(m_iop, AS_PROGRAM);
480 m_exp->set_io_iop_space(m_iop, AS_IO);
481 m_exp->int2().set(m_pic, FUNC(pic8259_device::ir2_w));
482 m_exp->int3().set(m_pic, FUNC(pic8259_device::ir3_w));
483 APRICOT_EXPANSION_SLOT(config, "exp:1", apricot_expansion_cards, nullptr);
484 APRICOT_EXPANSION_SLOT(config, "exp:2", apricot_expansion_cards, nullptr);
485 }
486
apricotxi(machine_config & config)487 void apricot_state::apricotxi(machine_config &config)
488 {
489 apricot(config);
490 }
491
492
493 //**************************************************************************
494 // ROM DEFINITIONS
495 //**************************************************************************
496
497 ROM_START( apricot )
498 ROM_REGION16_LE(0x4000, "bootstrap", 0)
499 ROM_LOAD16_BYTE("pc_bios_lo_001.bin", 0x0000, 0x2000, CRC(0c217cc2) SHA1(0d7a2b61e17966462b555115f962a175fadcf72a))
500 ROM_LOAD16_BYTE("pc_bios_hi_001.bin", 0x0001, 0x2000, CRC(7c27f36c) SHA1(c801bbf904815f76ec6463e948f57e0118a26292))
501 ROM_END
502
503 ROM_START( apricotxi )
504 ROM_REGION16_LE(0x4000, "bootstrap", 0)
505 ROM_LOAD16_BYTE("lo_ve007.u11", 0x0000, 0x2000, CRC(e74e14d1) SHA1(569133b0266ce3563b21ae36fa5727308797deee)) // LO Ve007 03.04.84
506 ROM_LOAD16_BYTE("hi_ve007.u9", 0x0001, 0x2000, CRC(b04fb83e) SHA1(cc2b2392f1b4c04bb6ec8ee26f8122242c02e572)) // HI Ve007 03.04.84
507 ROM_END
508
509
510 //**************************************************************************
511 // GAME DRIVERS
512 //**************************************************************************
513
514 // YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
515 COMP( 1983, apricot, 0, 0, apricot, 0, apricot_state, empty_init, "ACT", "Apricot PC", 0 )
516 COMP( 1984, apricotxi, apricot, 0, apricotxi, 0, apricot_state, empty_init, "ACT", "Apricot Xi", 0 )
517