// license:BSD-3-Clause // copyright-holders:Barry Rodewald /*************************************************************************** Hilger Analytical AB6089 Mk. 1 (LSI Octopus) 2013-07-26 Skeleton driver. http://computers.mcbx.netne.net/8bit/hilger/index.htm Below is an extract from the page: The computer has 2 CPUs: Z80 and 8088. Most circuits are dated 1985-1986, display circuitry is made by Signetics. Mainboard was manufactured by LSI Computers Ltd. under part numbers: 15000SS100 and 15000P4100. All steel parts of casing are grounded by wires. It's graphics card works in pass-through mode: It takes picture from mainboard's TTL output and adds image to it, then it puts it to monitor. Its ROM is prepared for hard disk and some type of network, yet no HDD controller nor network interfaces are present inside - it seems that they were added as expansion cards. UPDATE: It's re-branded LSI Octopus computer, a very well-expandable machine which was designed to "grow with a company". First stage was a computer which could be used even with TV set. As requirements increased, Octopus could be equipped with hard disk controller, network adapter, multi-terminal serial port card to act as a terminal server or even CPU cards to run concurrent systems. There were even tape backup devices for it. Octopus could run CP/M, MP/M (concurrent - multitasking-like OS, even with terminals), or even MS-DOS - CP/M or MP/M could be used with Z80 or 8080. There was also LSI ELSIE system, a concurrent DOS. Last British LSI machines were 386 computers which could be used as servers for Octopus computers. Manufacturer Hilger Analytical / LSI Computers Ltd. Origin UK Year of unit 1986? Year of introduction 1985 End of production ? CPU Z80, 8088 Speed 8MHz (8088) or 6MHz (Z80) RAM 128kB or 256kB, expandable to 768kB ROM 16kB (Basic) Colors: ?? Sound: Speaker. Beeps :) OS: CP/M 80 or 86 MP/M 80 o 86 Concurrent CP/M LSI ELSIE MS-DOS Text display: SCN2674B CRTC, SCB2675C for attributes Graphics: ?? (option board, ROM is dumped) Media: Two internal 5.25" floppy disk drives, DS DD, 96tpi. Probably hard disk Power supply: Built-in switching power supply. I/O: Serial port 2 parallel ports Video TTL Output Composite video output Possible upgrades: Many Software accessibility: Dedicated: Impossible. CP/M - Good DOS - Good. It won't take XT nor AT keyboard, but pinout is quite similar. UPDATE: I saw a few photos of keyboard. It's another Z80 computer! It has an EPROM, simple memory and CPU. After powering on, it should perform POST writing: TESTING... Main Processor PROM DMA Controllers RAM Interrupts Floppy Discs Hard Disc Controller (optionally - if installed) Waiting for hard Disc... (Optionally - if installed) Firmware versions: SYSTEM 18B (or other) GRAPHICS 4 (if graphic card installed) And probably it should boot or display: Insert System Disk. Or maybe: Nowhere to boot from. Load options: Floppy Pro Network Winchester Enter selection: This information was gained by studying boot ROM of the machine. It's a very rare computer. It has 2 processors, Z80 and 8088, so it can run both MS-DOS and CP/M. ****************************************************************************/ #include "emu.h" #include "cpu/i86/i86.h" #include "cpu/z80/z80.h" #include "imagedev/floppy.h" #include "machine/am9517a.h" #include "machine/bankdev.h" #include "machine/clock.h" #include "machine/i8251.h" #include "machine/i8255.h" #include "machine/mc146818.h" #include "machine/octo_kbd.h" #include "machine/pic8259.h" #include "machine/pit8253.h" #include "machine/ram.h" #include "machine/wd_fdc.h" #include "machine/z80sio.h" #include "sound/spkrdev.h" #include "video/scn2674.h" #include "bus/centronics/ctronics.h" #include "bus/centronics/comxpl80.h" #include "bus/centronics/epson_ex800.h" #include "bus/centronics/epson_lx800.h" #include "bus/centronics/epson_lx810l.h" #include "bus/centronics/printer.h" #include "screen.h" #include "softlist.h" #include "speaker.h" class octopus_state : public driver_device { public: octopus_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag), m_maincpu(*this, "maincpu"), m_subcpu(*this, "subcpu"), m_crtc(*this, "crtc"), m_vram(*this, "vram"), m_fontram(*this, "fram"), m_dma1(*this, "dma1"), m_dma2(*this, "dma2"), m_pic1(*this, "pic_master"), m_pic2(*this, "pic_slave"), m_rtc(*this, "rtc"), m_fdc(*this, "fdc"), m_floppy0(*this, "fdc:0"), m_floppy1(*this, "fdc:1"), m_kb_uart(*this, "keyboard"), m_pit(*this, "pit"), m_ppi(*this, "ppi"), m_speaker(*this, "speaker"), m_serial(*this, "serial"), m_parallel(*this, "parallel"), m_z80_bankdev(*this, "z80_bank"), m_ram(*this, "ram"), m_dswa(*this, "DSWA"), m_current_dma(-1), m_speaker_active(false), m_beep_active(false), m_z80_active(false) { } void octopus(machine_config &config); private: virtual void machine_reset() override; virtual void machine_start() override; virtual void video_start() override; SCN2674_DRAW_CHARACTER_MEMBER(display_pixels); DECLARE_READ8_MEMBER(vram_r); DECLARE_WRITE8_MEMBER(vram_w); DECLARE_READ8_MEMBER(get_slave_ack); DECLARE_WRITE_LINE_MEMBER(fdc_drq); DECLARE_READ8_MEMBER(bank_sel_r); DECLARE_WRITE8_MEMBER(bank_sel_w); DECLARE_READ8_MEMBER(dma_read); DECLARE_WRITE8_MEMBER(dma_write); DECLARE_WRITE_LINE_MEMBER(dma_hrq_changed); DECLARE_READ8_MEMBER(system_r); DECLARE_WRITE8_MEMBER(system_w); DECLARE_READ8_MEMBER(cntl_r); DECLARE_WRITE8_MEMBER(cntl_w); DECLARE_READ8_MEMBER(gpo_r); DECLARE_WRITE8_MEMBER(gpo_w); DECLARE_READ8_MEMBER(vidcontrol_r); DECLARE_WRITE8_MEMBER(vidcontrol_w); DECLARE_READ8_MEMBER(z80_io_r); DECLARE_WRITE8_MEMBER(z80_io_w); IRQ_CALLBACK_MEMBER(x86_irq_cb); DECLARE_READ8_MEMBER(rtc_r); DECLARE_WRITE8_MEMBER(rtc_w); DECLARE_READ8_MEMBER(z80_vector_r); DECLARE_WRITE8_MEMBER(z80_vector_w); DECLARE_READ8_MEMBER(parallel_r); DECLARE_WRITE8_MEMBER(parallel_w); DECLARE_READ8_MEMBER(video_latch_r); DECLARE_WRITE8_MEMBER(video_latch_w); DECLARE_WRITE_LINE_MEMBER(spk_w); DECLARE_WRITE_LINE_MEMBER(spk_freq_w); DECLARE_WRITE_LINE_MEMBER(beep_w); DECLARE_WRITE_LINE_MEMBER(serial_clock_w); DECLARE_WRITE_LINE_MEMBER(parallel_busy_w) { m_printer_busy = state; } DECLARE_WRITE_LINE_MEMBER(parallel_slctout_w) { m_printer_slctout = state; } DECLARE_WRITE_LINE_MEMBER(dack0_w) { m_dma1->hack_w(state ? 0 : 1); } // for all unused DMA channel? DECLARE_WRITE_LINE_MEMBER(dack1_w) { if(!state) m_current_dma = 1; else if(m_current_dma == 1) m_current_dma = -1; } // HD DECLARE_WRITE_LINE_MEMBER(dack2_w) { if(!state) m_current_dma = 2; else if(m_current_dma == 2) m_current_dma = -1; } // RAM refresh DECLARE_WRITE_LINE_MEMBER(dack3_w) { m_dma1->hack_w(state ? 0 : 1); } DECLARE_WRITE_LINE_MEMBER(dack4_w) { m_dma1->hack_w(state ? 0 : 1); } DECLARE_WRITE_LINE_MEMBER(dack5_w) { if(!state) m_current_dma = 5; else if(m_current_dma == 5) m_current_dma = -1; } // Floppy DECLARE_WRITE_LINE_MEMBER(dack6_w) { m_dma1->hack_w(state ? 0 : 1); } DECLARE_WRITE_LINE_MEMBER(dack7_w) { m_dma1->hack_w(state ? 0 : 1); } enum { BEEP_TIMER = 100 }; void octopus_io(address_map &map); void octopus_mem(address_map &map); void octopus_sub_io(address_map &map); void octopus_sub_mem(address_map &map); void octopus_vram(address_map &map); virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; required_device m_maincpu; required_device m_subcpu; required_device m_crtc; required_shared_ptr m_vram; required_shared_ptr m_fontram; required_device m_dma1; required_device m_dma2; required_device m_pic1; required_device m_pic2; required_device m_rtc; required_device m_fdc; required_device m_floppy0; required_device m_floppy1; required_device m_kb_uart; required_device m_pit; required_device m_ppi; required_device m_speaker; required_device m_serial; required_device m_parallel; required_device m_z80_bankdev; required_device m_ram; required_ioport m_dswa; uint8_t m_hd_bank; // HD bank select uint8_t m_fd_bank; // Floppy bank select uint8_t m_z80_bank; // Z80 bank / RAM refresh int8_t m_current_dma; // current DMA channel (-1 for none) uint8_t m_current_drive; uint8_t m_cntl; // RTC / FDC control (PPI port B) uint8_t m_gpo; // General purpose outputs (PPI port C) uint8_t m_vidctrl; bool m_speaker_active; bool m_beep_active; bool m_speaker_level; bool m_z80_active; bool m_rtc_address; bool m_rtc_data; uint8_t m_prev_cntl; uint8_t m_rs232_vector; uint8_t m_rs422_vector; bool m_printer_busy; bool m_printer_slctout; uint8_t m_char_latch_r; uint8_t m_attr_latch_r; uint8_t m_char_latch_w; uint8_t m_attr_latch_w; emu_timer* m_timer_beep; }; void octopus_state::octopus_mem(address_map &map) { map(0x00000, 0xcffff).bankrw("main_ram_bank"); map(0xd0000, 0xdffff).ram().share("vram"); map(0xe0000, 0xe3fff).noprw(); map(0xe4000, 0xe5fff).ram().share("fram"); map(0xe6000, 0xe7fff).rom().region("chargen", 0); map(0xe8000, 0xfbfff).noprw(); map(0xfc000, 0xfffff).rom().region("user1", 0); } void octopus_state::octopus_io(address_map &map) { map.unmap_value_high(); map(0x00, 0x0f).rw(m_dma1, FUNC(am9517a_device::read), FUNC(am9517a_device::write)); map(0x10, 0x1f).rw(m_dma2, FUNC(am9517a_device::read), FUNC(am9517a_device::write)); map(0x20, 0x20).portr("DSWA"); map(0x21, 0x2f).rw(FUNC(octopus_state::system_r), FUNC(octopus_state::system_w)); map(0x31, 0x33).rw(FUNC(octopus_state::bank_sel_r), FUNC(octopus_state::bank_sel_w)); map(0x50, 0x51).rw(m_kb_uart, FUNC(i8251_device::read), FUNC(i8251_device::write)); // 0x70-73: HD controller map(0x80, 0x83).rw(m_pit, FUNC(pit8253_device::read), FUNC(pit8253_device::write)); map(0xa0, 0xa0).rw(m_serial, FUNC(z80sio_device::da_r), FUNC(z80sio_device::da_w)); map(0xa1, 0xa1).rw(m_serial, FUNC(z80sio_device::ca_r), FUNC(z80sio_device::ca_w)); map(0xa2, 0xa2).rw(m_serial, FUNC(z80sio_device::db_r), FUNC(z80sio_device::db_w)); map(0xa3, 0xa3).rw(m_serial, FUNC(z80sio_device::cb_r), FUNC(z80sio_device::cb_w)); map(0xb0, 0xb1).rw(m_pic1, FUNC(pic8259_device::read), FUNC(pic8259_device::write)); map(0xb4, 0xb5).rw(m_pic2, FUNC(pic8259_device::read), FUNC(pic8259_device::write)); map(0xc0, 0xc7).rw(m_crtc, FUNC(scn2674_device::read), FUNC(scn2674_device::write)); map(0xc8, 0xc8).rw(FUNC(octopus_state::vidcontrol_r), FUNC(octopus_state::vidcontrol_w)); map(0xc9, 0xca).rw(FUNC(octopus_state::video_latch_r), FUNC(octopus_state::video_latch_w)); // 0xcf: mode control map(0xd0, 0xd3).rw(m_fdc, FUNC(fd1793_device::read), FUNC(fd1793_device::write)); map(0xe0, 0xe4).rw(FUNC(octopus_state::z80_vector_r), FUNC(octopus_state::z80_vector_w)); map(0xf0, 0xf1).rw(FUNC(octopus_state::parallel_r), FUNC(octopus_state::parallel_w)); map(0xf8, 0xff).rw(m_ppi, FUNC(i8255_device::read), FUNC(i8255_device::write)); } void octopus_state::octopus_sub_mem(address_map &map) { map(0x0000, 0xffff).rw(m_z80_bankdev, FUNC(address_map_bank_device::read8), FUNC(address_map_bank_device::write8)); } void octopus_state::octopus_sub_io(address_map &map) { map.unmap_value_high(); map(0x0000, 0xffff).rw(FUNC(octopus_state::z80_io_r), FUNC(octopus_state::z80_io_w)); } void octopus_state::octopus_vram(address_map &map) { map(0x0000, 0xffff).rw(FUNC(octopus_state::vram_r), FUNC(octopus_state::vram_w)); } /* Input ports */ static INPUT_PORTS_START( octopus ) PORT_START("DSWA") PORT_DIPNAME( 0x03, 0x02, "Number of floppy drives" ) PORT_DIPLOCATION("SWA:1,2") PORT_DIPSETTING( 0x00, "None" ) PORT_DIPSETTING( 0x01, "1 Floppy" ) PORT_DIPSETTING( 0x02, "2 Floppies" ) PORT_DIPSETTING( 0x03, "Not used" ) PORT_DIPNAME( 0x04, 0x00, "Quad drives" ) PORT_DIPLOCATION("SWA:3") PORT_DIPSETTING( 0x00, "Disabled" ) PORT_DIPSETTING( 0x04, "Enabled" ) PORT_DIPNAME( 0x38, 0x00, "Winchester drive type" ) PORT_DIPLOCATION("SWA:4,5,6") PORT_DIPSETTING( 0x00, "None" ) PORT_DIPSETTING( 0x08, "RO201" ) PORT_DIPSETTING( 0x10, "RO202" ) PORT_DIPSETTING( 0x18, "Reserved" ) PORT_DIPSETTING( 0x20, "RO204" ) PORT_DIPSETTING( 0x28, "Reserved" ) PORT_DIPSETTING( 0x30, "RO208" ) PORT_DIPSETTING( 0x38, "Reserved" ) PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unused ) ) PORT_DIPLOCATION("SWA:7") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x40, DEF_STR( On ) ) PORT_DIPNAME( 0x80, 0x80, "Colour monitor connected" ) PORT_DIPLOCATION("SWA:8") PORT_DIPSETTING( 0x00, DEF_STR( No ) ) PORT_DIPSETTING( 0x80, DEF_STR( Yes ) ) INPUT_PORTS_END void octopus_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { switch(id) { case BEEP_TIMER: // switch off speaker m_beep_active = false; break; } } WRITE8_MEMBER(octopus_state::vram_w) { m_vram[offset] = m_char_latch_w; m_vram[offset+0x1000] = m_attr_latch_w; } READ8_MEMBER(octopus_state::vram_r) { m_char_latch_r = m_vram[offset]; m_attr_latch_r = m_vram[offset+0x1000]; return m_vram[offset]; } WRITE_LINE_MEMBER(octopus_state::fdc_drq) { // TODO } READ8_MEMBER(octopus_state::bank_sel_r) { switch(offset) { case 0: return m_hd_bank; case 1: return m_fd_bank; case 2: return m_z80_bank; } return 0xff; } WRITE8_MEMBER(octopus_state::bank_sel_w) { switch(offset) { case 0: m_hd_bank = data; logerror("HD bank = %i\n",data); break; case 1: m_fd_bank = data; logerror("Floppy bank = %i\n",data); break; case 2: m_z80_bank = data; m_z80_bankdev->set_bank(m_z80_bank & 0x0f); logerror("Z80/RAM bank = %i\n",data); break; } } // System control // 0x20: read: System type, write: Z80 NMI // 0x21: read: bit5=SLCTOUT from parallel interface, bit6=option board parity fail, bit7=main board parity fail // write: parity fail reset // ports 0x20 and 0x21 read out the DIP switch configuration (the firmware function to get system config simply does IN AX,20h) // 0x28: write: Z80 enable WRITE8_MEMBER(octopus_state::system_w) { logerror("SYS: System control offset %i data %02x\n",offset+1,data); switch(offset) { case 7: // enable Z80, halt 8088 m_subcpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE); m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE); m_z80_active = true; break; } } READ8_MEMBER(octopus_state::system_r) { uint8_t val = 0x00; switch(offset) { case 0: val = 0x1f; if(m_printer_slctout) val |= 0x20; return val; // do bits 0-4 mean anything? Language DIPs? } return 0xff; } // Any I/O cycle relinquishes control of the bus READ8_MEMBER(octopus_state::z80_io_r) { z80_io_w(space,offset,0); return 0x00; } WRITE8_MEMBER(octopus_state::z80_io_w) { m_subcpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE); m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE); m_z80_active = false; } // Z80 vector for RS232 and RS422 READ8_MEMBER(octopus_state::z80_vector_r) { switch(offset) { case 0: return m_rs232_vector; case 4: return m_rs422_vector; default: return 0xff; } return 0xff; } WRITE8_MEMBER(octopus_state::z80_vector_w) { switch(offset) { case 0: m_rs232_vector = data; logerror("RS232 vector set to %02x\n",data); break; case 4: m_rs422_vector = data; logerror("RS422 vector set to %02x\n",data); break; default: logerror("Read invalid vector port 0x%02x\n",0xe0 + offset); } } // RTC data and I/O - PPI port A // bits 0-3 of RTC/FDC control go to control lines of the MC146818 // The technical manual does not mention what is connected to each bit // This is an educated guess, based on the BIOS code // bit 0 = ? (Pulsed low after writing to an RTC register) // bit 1 = PPI Port A strobe? // bit 2 = Data strobe? // bit 3 = Address strobe? READ8_MEMBER(octopus_state::rtc_r) { uint8_t ret = 0xff; if(m_rtc_data) ret = m_rtc->read(1); else if(m_rtc_address) ret = m_rtc->read(0); return ret; } WRITE8_MEMBER(octopus_state::rtc_w) { if(m_rtc_data) m_rtc->write(1,data); else if(m_rtc_address) m_rtc->write(0,data); } // RTC/FDC control - PPI port B // bits0-3: RTC control lines // bit4-5: write precomp. // bit6-7: drive select READ8_MEMBER(octopus_state::cntl_r) { return m_cntl; } WRITE8_MEMBER(octopus_state::cntl_w) { m_cntl = data; if((m_cntl & 0x08) && !(m_prev_cntl & 0x08)) { m_rtc_address = true; m_rtc_data = false; } if((data & 0x04) && !(m_prev_cntl & 0x04)) { m_rtc_address = false; m_rtc_data = true; } m_ppi->pc4_w(data & 0x02); m_prev_cntl = m_cntl; m_current_drive = (data & 0xc0) >> 6; switch(m_current_drive) { case 1: m_fdc->set_floppy(m_floppy0->get_device()); m_floppy0->get_device()->mon_w(0); break; case 2: m_fdc->set_floppy(m_floppy1->get_device()); m_floppy1->get_device()->mon_w(0); break; } logerror("Selected floppy drive %i (%02x)\n",m_current_drive,data); } // General Purpose Outputs - PPI port C // bit 2 - floppy side select // bit 1 - parallel data I/O (0 = output) // bit 0 - parallel control I/O (0 = output) READ8_MEMBER(octopus_state::gpo_r) { return m_gpo; } WRITE8_MEMBER(octopus_state::gpo_w) { m_gpo = data; switch(m_current_drive) { case 1: m_floppy0->get_device()->ss_w((data & 0x04) >> 2); break; case 2: m_floppy1->get_device()->ss_w((data & 0x04) >> 2); break; default: logerror("Attempted to set side on unknown drive %i\n",m_current_drive); } } // Video control register // bit 0 - video dot clock - 0=17.6MHz, 1=16MHz // bit 2 - floppy DDEN line // bit 3 - floppy FCLOCK line - 0=1MHz, 1=2MHz // bits 4-5 - character width - 0=10 dots, 1=6 dots, 2=8 dots, 3=9 dots // bit 6 - cursor mode (colour only) - 0=inverse cursor, 1=white cursor (normal) // bit 7 - 1=monochrome mode, 0=colour mode // Is bit 7 writable, or just mirrors DIP switch setting? Tech manual is unclear. READ8_MEMBER(octopus_state::vidcontrol_r) { return m_vidctrl; } WRITE8_MEMBER(octopus_state::vidcontrol_w) { m_fdc->dden_w(BIT(data, 2)); m_fdc->set_unscaled_clock(16_MHz_XTAL / (BIT(data, 3) ? 16 : 8)); if (((m_vidctrl ^ data) & 0x31) != 0) { unsigned dots = 4 + ((data & 0x30) >> 3); if ((data & 0x30) == 0) dots = 10; else if ((data & 0x30) == 0x30) dots = 9; auto dotclk = BIT(data, 0) ? 16_MHz_XTAL : 17.6_MHz_XTAL; m_crtc->set_character_width(dots); m_crtc->set_unscaled_clock(dotclk / dots); } m_vidctrl = data; } // Sound hardware // Sound level provided by i8253 timer 2 // Enabled by /DTR signal from i8251 // 100ms beep triggered by pulsing /CTS signal low on i8251 WRITE_LINE_MEMBER(octopus_state::spk_w) { m_speaker_active = !state; m_speaker->level_w(((m_speaker_active || m_beep_active) && m_speaker_level) ? 1 : 0); } WRITE_LINE_MEMBER(octopus_state::spk_freq_w) { m_speaker_level = state; m_speaker->level_w(((m_speaker_active || m_beep_active) && m_speaker_level) ? 1 : 0); } WRITE_LINE_MEMBER(octopus_state::beep_w) { if(!state) // active low { m_beep_active = true; m_speaker->level_w(((m_speaker_active || m_beep_active) && m_speaker_level) ? 1 : 0); m_timer_beep->adjust(attotime::from_msec(100)); } } WRITE_LINE_MEMBER(octopus_state::serial_clock_w) { m_serial->rxca_w(state); m_serial->txca_w(state); } // Parallel Centronics port // 0xf0 : data // 0xf1 : control // bit 2 = INIT? On boot, bits 0 and 1 are set high, bit 2 is set low then high again, all other bits are set low // can generate interrupts - tech manual suggests that Strobe, Init, Ack, and Busy can trigger an interrupt (IRQ14) READ8_MEMBER(octopus_state::parallel_r) { switch(offset) { case 0: return 0; case 1: return m_printer_busy ? 0x01 : 0x00; // correct? Tech manual doesn't explain which bit is which } return 0xff; } WRITE8_MEMBER(octopus_state::parallel_w) { switch(offset) { case 0: // data if(!(m_gpo & 0x02)) // parallel data direction { m_parallel->write_data0(BIT(data,0)); m_parallel->write_data1(BIT(data,1)); m_parallel->write_data2(BIT(data,2)); m_parallel->write_data3(BIT(data,3)); m_parallel->write_data4(BIT(data,4)); m_parallel->write_data5(BIT(data,5)); m_parallel->write_data6(BIT(data,6)); m_parallel->write_data7(BIT(data,7)); } break; case 1: // control (bit order unknown?) if(!(m_gpo & 0x01)) // parallel control direction { m_parallel->write_init(BIT(data,2)); m_pic2->ir6_w(!BIT(data,2)); } break; } } READ8_MEMBER(octopus_state::dma_read) { uint8_t byte; address_space& prog_space = m_maincpu->space(AS_PROGRAM); // get the right address space if(m_current_dma == -1) return 0; byte = prog_space.read_byte((m_fd_bank << 16) + offset); return byte; } WRITE8_MEMBER(octopus_state::dma_write) { address_space& prog_space = m_maincpu->space(AS_PROGRAM); // get the right address space if(m_current_dma == -1) return; prog_space.write_byte((m_fd_bank << 16) + offset, data); } WRITE_LINE_MEMBER( octopus_state::dma_hrq_changed ) { m_maincpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE); /* Assert HLDA */ m_dma2->hack_w(state); } // Any interrupt will also give bus control back to the 8088 IRQ_CALLBACK_MEMBER(octopus_state::x86_irq_cb) { uint8_t vector; m_subcpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE); m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE); m_z80_active = false; vector = m_pic1->inta_cb(device,irqline); if(vector == 0x61) // if we have hit a serial comms IRQ, then also have the Z80SIO/2 acknowledge the interrupt vector = m_serial->m1_r(); return vector; } void octopus_state::machine_start() { m_timer_beep = timer_alloc(BEEP_TIMER); m_vidctrl = 0xff; // install RAM m_maincpu->space(AS_PROGRAM).install_readwrite_bank(0x0000,m_ram->size()-1,"main_ram_bank"); m_maincpu->space(AS_PROGRAM).nop_readwrite(m_ram->size(),0xcffff); } void octopus_state::machine_reset() { m_subcpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE); // halt Z80 to start with m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE); m_z80_active = false; m_current_dma = -1; m_current_drive = 0; m_rtc_address = true; m_rtc_data = false; membank("main_ram_bank")->set_base(m_ram->pointer()); m_kb_uart->write_dsr(1); // DSR is used to determine if a keyboard is connected? If DSR is high, then the CHAR_OUT BIOS function will not output to the screen. } void octopus_state::video_start() { m_vram.allocate(0x10000); } READ8_MEMBER(octopus_state::video_latch_r) { if(offset & 0x01) return m_attr_latch_r; else return m_char_latch_r; } WRITE8_MEMBER(octopus_state::video_latch_w) { if(offset & 0x01) m_attr_latch_w = data; else m_char_latch_w = data; } SCN2674_DRAW_CHARACTER_MEMBER(octopus_state::display_pixels) { // Attributes: // - common bits // b7 : blink // b3 : underline // - Monochrome // b6 : GP1 (general purpose) // b5 : reverse video // b4 : GP2 (general purpose) // b2 : High intensity // b1 : Grey background // b0 : Blank (TODO) // - Colour // b6,5,4 : background colour (RGB) // b2,1,0 : foreground colour (RGB) if(!lg) { uint8_t tile = m_vram[address & 0x0fff]; uint8_t attr = m_vram[(address & 0x0fff) + 0x1000]; uint8_t data = m_fontram[(tile * 16) + linecount]; rgb_t fg,bg; if(m_dswa->read() & 0x80) // monochrome or colour mode is selected by switch 8 of system DIP switches { // colour (is the background at half intensity?) bg.set_r((attr & 0x40) ? 0x7f : 0x00); bg.set_g((attr & 0x20) ? 0x7f : 0x00); bg.set_b((attr & 0x10) ? 0x7f : 0x00); fg.set_r((attr & 0x04) ? 0xff : 0x00); fg.set_g((attr & 0x02) ? 0xff : 0x00); fg.set_b((attr & 0x01) ? 0xff : 0x00); } else { // monochrome if(attr & 0x02) fg = 0xffffff; else fg = 0x7f7f7f; if(attr & 0x04) bg = 0x7f7f7f; else bg = 0x000000; if(attr & 0x20) // reverse video data = ~data; } if(ul && (attr & 0x08)) data = 0xff; if(blink && (attr & 0x80)) data = 0x00; if(cursor && !blink) { bool inverse = true; if(!(m_dswa->read() & 0x80)) // not available in monochrome mode inverse = false; if(m_vidctrl & 0x40) // not enabled inverse = false; if(inverse) data = ~data; else data = 0xff; } for (int z=0;z<8;z++) bitmap.pix32(y,x + z) = BIT(data,z) ? fg : bg; } } READ8_MEMBER( octopus_state::get_slave_ack ) { if (offset==7) return m_pic2->acknowledge(); return 0x00; } static void octopus_floppies(device_slot_interface &device) { device.option_add("525dd", FLOPPY_525_DD); } static void keyboard(device_slot_interface &device) { device.option_add("octopus", OCTOPUS_KEYBOARD); } void octopus_centronics_devices(device_slot_interface &device) { device.option_add("pl80", COMX_PL80); device.option_add("ex800", EPSON_EX800); device.option_add("lx800", EPSON_LX800); device.option_add("lx810l", EPSON_LX810L); device.option_add("ap2000", EPSON_AP2000); device.option_add("printer", CENTRONICS_PRINTER); } void octopus_state::octopus(machine_config &config) { /* basic machine hardware */ I8088(config, m_maincpu, 24_MHz_XTAL / 3); // 8MHz m_maincpu->set_addrmap(AS_PROGRAM, &octopus_state::octopus_mem); m_maincpu->set_addrmap(AS_IO, &octopus_state::octopus_io); m_maincpu->set_irq_acknowledge_callback(FUNC(octopus_state::x86_irq_cb)); Z80(config, m_subcpu, 24_MHz_XTAL / 4); // 6MHz m_subcpu->set_addrmap(AS_PROGRAM, &octopus_state::octopus_sub_mem); m_subcpu->set_addrmap(AS_IO, &octopus_state::octopus_sub_io); AM9517A(config, m_dma1, 24_MHz_XTAL / 6); // 4MHz m_dma1->out_hreq_callback().set(m_dma2, FUNC(am9517a_device::dreq0_w)); m_dma1->in_memr_callback().set(FUNC(octopus_state::dma_read)); m_dma1->out_memw_callback().set(FUNC(octopus_state::dma_write)); //m_dma1->in_ior_callback<0>().set_nop(); //m_dma1->in_ior_callback<1>().set_nop(); // HDC //m_dma1->in_ior_callback<2>().set_nop(); // RAM Refresh //m_dma1->in_ior_callback<3>().set_nop(); //m_dma1->out_iow_callback<0>().set_nop(); //m_dma1->out_iow_callback<1>().set_nop(); // HDC //m_dma1->out_iow_callback<2>().set_nop(); // RAM Refresh //m_dma1->out_iow_callback<3>().set_nop(); m_dma1->out_dack_callback<0>().set(FUNC(octopus_state::dack0_w)); m_dma1->out_dack_callback<1>().set(FUNC(octopus_state::dack1_w)); m_dma1->out_dack_callback<2>().set(FUNC(octopus_state::dack2_w)); m_dma1->out_dack_callback<3>().set(FUNC(octopus_state::dack3_w)); AM9517A(config, m_dma2, 24_MHz_XTAL / 6); // 4MHz m_dma2->out_hreq_callback().set(FUNC(octopus_state::dma_hrq_changed)); m_dma2->in_memr_callback().set(FUNC(octopus_state::dma_read)); m_dma2->out_memw_callback().set(FUNC(octopus_state::dma_write)); //m_dma2->in_ior_callback<0>().set_nop(); m_dma2->in_ior_callback<1>().set(m_fdc, FUNC(fd1793_device::data_r)); // FDC //m_dma2->in_ior_callback<2>().set_nop(); //m_dma2->in_ior_callback<3>().set_nop(); //m_dma2->out_iow_callback<0>().set_nop(); m_dma2->out_iow_callback<1>().set(m_fdc, FUNC(fd1793_device::data_w)); // FDC //m_dma2->out_iow_callback<2>().set_nop(); //m_dma2->out_iow_callback<3>().set_nop(); m_dma2->out_dack_callback<0>().set(FUNC(octopus_state::dack4_w)); m_dma2->out_dack_callback<1>().set(FUNC(octopus_state::dack5_w)); m_dma2->out_dack_callback<2>().set(FUNC(octopus_state::dack6_w)); m_dma2->out_dack_callback<3>().set(FUNC(octopus_state::dack7_w)); PIC8259(config, m_pic1, 0); m_pic1->out_int_callback().set_inputline(m_maincpu, 0); m_pic1->in_sp_callback().set_constant(1); m_pic1->read_slave_ack_callback().set(FUNC(octopus_state::get_slave_ack)); PIC8259(config, m_pic2, 0); m_pic2->out_int_callback().set(m_pic1, FUNC(pic8259_device::ir7_w)); m_pic2->in_sp_callback().set_constant(0); // RTC (MC146818 via i8255 PPI) I8255(config, m_ppi, 0); m_ppi->in_pa_callback().set(FUNC(octopus_state::rtc_r)); m_ppi->in_pb_callback().set(FUNC(octopus_state::cntl_r)); m_ppi->in_pc_callback().set(FUNC(octopus_state::gpo_r)); m_ppi->out_pa_callback().set(FUNC(octopus_state::rtc_w)); m_ppi->out_pb_callback().set(FUNC(octopus_state::cntl_w)); m_ppi->out_pc_callback().set(FUNC(octopus_state::gpo_w)); MC146818(config, m_rtc, 32.768_kHz_XTAL); m_rtc->irq().set(m_pic2, FUNC(pic8259_device::ir2_w)); // Keyboard UART I8251(config, m_kb_uart, 0); m_kb_uart->rxrdy_handler().set("pic_slave", FUNC(pic8259_device::ir4_w)); m_kb_uart->dtr_handler().set(FUNC(octopus_state::spk_w)); m_kb_uart->rts_handler().set(FUNC(octopus_state::beep_w)); rs232_port_device &keyboard_port(RS232_PORT(config, "keyboard_port", keyboard, "octopus")); keyboard_port.rxd_handler().set(m_kb_uart, FUNC(i8251_device::write_rxd)); clock_device &keyboard_clock_rx(CLOCK(config, "keyboard_clock_rx", 9600 * 64)); keyboard_clock_rx.signal_handler().set(m_kb_uart, FUNC(i8251_device::write_rxc)); clock_device &keyboard_clock_tx(CLOCK(config, "keyboard_clock_tx", 1200 * 64)); keyboard_clock_tx.signal_handler().set(m_kb_uart, FUNC(i8251_device::write_txc)); FD1793(config, m_fdc, 16_MHz_XTAL / 8); m_fdc->intrq_wr_callback().set(m_pic1, FUNC(pic8259_device::ir5_w)); m_fdc->drq_wr_callback().set(m_dma2, FUNC(am9517a_device::dreq1_w)); FLOPPY_CONNECTOR(config, "fdc:0", octopus_floppies, "525dd", floppy_image_device::default_floppy_formats); FLOPPY_CONNECTOR(config, "fdc:1", octopus_floppies, "525dd", floppy_image_device::default_floppy_formats); SOFTWARE_LIST(config, "fd_list").set_original("octopus"); PIT8253(config, m_pit, 0); m_pit->set_clk<0>(4.9152_MHz_XTAL / 2); // DART channel A m_pit->out_handler<0>().set(FUNC(octopus_state::serial_clock_w)); // being able to write both Rx and Tx clocks at one time would be nice m_pit->set_clk<1>(4.9152_MHz_XTAL / 2); // DART channel B m_pit->out_handler<1>().set(m_serial, FUNC(z80sio_device::rxtxcb_w)); m_pit->set_clk<2>(4.9152_MHz_XTAL / 2); // speaker frequency m_pit->out_handler<2>().set(FUNC(octopus_state::spk_freq_w)); SPEAKER(config, "mono").front_center(); SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50); Z80SIO(config, m_serial, 16_MHz_XTAL / 4); // clock rate not mentioned in tech manual m_serial->out_int_callback().set(m_pic1, FUNC(pic8259_device::ir1_w)); m_serial->out_txda_callback().set("serial_a", FUNC(rs232_port_device::write_txd)); m_serial->out_txdb_callback().set("serial_b", FUNC(rs232_port_device::write_txd)); m_serial->out_rtsa_callback().set("serial_a", FUNC(rs232_port_device::write_rts)); m_serial->out_rtsb_callback().set("serial_b", FUNC(rs232_port_device::write_rts)); rs232_port_device &serial_a(RS232_PORT(config, "serial_a", default_rs232_devices, nullptr)); serial_a.rxd_handler().set(m_serial, FUNC(z80sio_device::rxa_w)); serial_a.cts_handler().set(m_serial, FUNC(z80sio_device::ctsa_w)).invert(); //serial_a.ri_handler().set(m_serial, FUNC(z80sio_device::ria_w)).invert(); rs232_port_device &serial_b(RS232_PORT(config, "serial_b", default_rs232_devices, nullptr)); serial_b.rxd_handler().set(m_serial, FUNC(z80sio_device::rxb_w)); serial_b.cts_handler().set(m_serial, FUNC(z80sio_device::ctsb_w)).invert(); //serial_b.ri_handler().set(m_serial, FUNC(z80sio_device::rib_w)).invert(); CENTRONICS(config, m_parallel, octopus_centronics_devices, "printer"); m_parallel->busy_handler().set(FUNC(octopus_state::parallel_busy_w)); m_parallel->select_handler().set(FUNC(octopus_state::parallel_slctout_w)); // TODO: Winchester HD controller (Xebec/SASI compatible? uses TTL logic) /* video hardware */ screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); screen.set_raw(16_MHz_XTAL, 918, 0, 729, 350, 0, 325); //screen.set_raw(17.6_MHz_XTAL, 1008, 0, 792, 348, 0, 319); screen.set_screen_update("crtc", FUNC(scn2674_device::screen_update)); SCN2674(config, m_crtc, 16_MHz_XTAL / 9); // dot clock and character width are both selectable m_crtc->intr_callback().set("pic_slave", FUNC(pic8259_device::ir0_w)); m_crtc->set_character_width(9); m_crtc->set_display_callback(FUNC(octopus_state::display_pixels)); m_crtc->set_addrmap(0, &octopus_state::octopus_vram); m_crtc->set_screen("screen"); ADDRESS_MAP_BANK(config, "z80_bank").set_map(&octopus_state::octopus_mem).set_options(ENDIANNESS_LITTLE, 8, 32, 0x10000); RAM(config, "ram").set_default_size("256K").set_extra_options("128K,512K,768K"); } /* ROM definition */ ROM_START( octopus ) ROM_REGION( 0x4000, "user1", 0 ) ROM_LOAD( "octopus_main_prom", 0x0000, 0x4000, CRC(b5b4518d) SHA1(41b8729c4c9074914fd4ea181c8b6d4805ee2b93) ) // This rom was on the graphics card (yes, it has slots) ROM_REGION( 0x2000, "chargen", 0 ) ROM_LOAD( "octopus_gfx_card", 0x0000, 0x2000, CRC(b2386534) SHA1(5e3c4682afb4eb222e48a7203269a16d26911836) ) ROM_END /* Driver */ // YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS COMP( 1986, octopus, 0, 0, octopus, octopus, octopus_state, empty_init, "Digital Microsystems", "LSI Octopus", MACHINE_NOT_WORKING)