// license:BSD-3-Clause // copyright-holders:Robbbert /*************************************************************************** Amust Compak - also known as Amust Executive 816. 2014-03-21 Skeleton driver. [Robbbert] An unusual-looking CP/M computer. There are no manuals or schematics known to exist. The entire driver is guesswork. The board has LH0080 (Z80A), 2x 8251, 2x 8255, 8253, uPD765A and a HD46505SP-2. The videoram is a 6116 RAM. There is a piezo beeper. There are 3 crystals, X1 = 4.9152 (serial chips?), X2 = 16 (CPU), X3 = 14.31818 MHz (Video?). There are numerous jumpers, all of which perform unknown functions. The keyboard is a plug-in unit, same idea as Kaypro and Zorba. It has these chips: INS8035N-6, F74145, 74LS373N, SN75451BP, 2716 rom with label KBD-3. Crystal: 3.579545 MHz The main rom is identical between the 2 halves, except that the initial crtc parameters are slightly different. I've chosen to ignore the first half. (perhaps 50/60 Hz selectable by jumper?) Preliminary I/O ports --------------------- 00-01 uart 1 02-03 uart 2 04-07 ppi 1 08-0b ppi 2 0d-0f crtc 10-11 fdc 14-17 pit PIT. Having the PIT on ports 14-17 seems to make sense. It sets counters 1 and 2 to mode 3, binary, initial count = 0x80. Counter 0 not used? Floppy Parameters: ------------------ Double Density Two Side 80 track 1024 byte sectors 5 sectors/track 800k capacity 128 directory entries 2k block size Skew 1,3,5,2,4 Stuff that doesn't make sense: ------------------------------ 1. To access the screen, it waits for IRQ presumably from sync pulse. It sets INT mode 0 which means a page-zero jump, but doesn't write anything to the zero-page ram. That's why I added a RETI at 0038 and set the vector to there. A bit later it writes a jump at 0000. Then it sets the interrupting device to the fdc (not sure how yet), then proceeds to overwrite all of page-zero with the disk contents. This of course kills the jump it just wrote, and my RETI. So it runs into the weeds at high speed. What should happen is after loading the boot sector succesfully it will jump to 0000, otherwise it will write BOOT NG to the screen and you're in the monitor. The bios contains no RETI instructions. 2. At F824 it copies itself to the same address which is presumably shadow ram. But it never switches to it. The ram is physically in the machine. Monitor Commands: ----------------- B = Boot from floppy (YES! Most useless monitor ever) ToDo: - Everything - Need software - Keyboard controller needs to be emulated - If booting straight to CP/M, the load message should be in the middle of the screen. ****************************************************************************/ #include "emu.h" #include "cpu/z80/z80.h" #include "machine/i8251.h" #include "machine/i8255.h" #include "machine/pit8253.h" #include "machine/clock.h" #include "bus/rs232/rs232.h" #include "machine/upd765.h" #include "sound/beep.h" #include "video/mc6845.h" #include "screen.h" #include "speaker.h" class amust_state : public driver_device { public: enum { TIMER_BEEP_OFF }; amust_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag) , m_palette(*this, "palette") , m_maincpu(*this, "maincpu") , m_p_videoram(*this, "videoram") , m_p_chargen(*this, "chargen") , m_beep(*this, "beeper") , m_fdc (*this, "fdc") , m_floppy0(*this, "fdc:0") , m_floppy1(*this, "fdc:1") { } void init_amust(); DECLARE_MACHINE_RESET(amust); DECLARE_READ8_MEMBER(port04_r); DECLARE_WRITE8_MEMBER(port04_w); DECLARE_READ8_MEMBER(port05_r); DECLARE_READ8_MEMBER(port06_r); DECLARE_WRITE8_MEMBER(port06_w); DECLARE_READ8_MEMBER(port08_r); DECLARE_WRITE8_MEMBER(port08_w); DECLARE_READ8_MEMBER(port09_r); DECLARE_READ8_MEMBER(port0a_r); DECLARE_WRITE8_MEMBER(port0a_w); DECLARE_WRITE8_MEMBER(port0d_w); void kbd_put(u8 data); INTERRUPT_GEN_MEMBER(irq_vs); MC6845_UPDATE_ROW(crtc_update_row); void amust(machine_config &config); void io_map(address_map &map); void mem_map(address_map &map); private: u8 m_port04; u8 m_port06; u8 m_port08; u8 m_port0a; u8 m_term_data; virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; required_device m_palette; required_device m_maincpu; required_region_ptr m_p_videoram; required_region_ptr m_p_chargen; required_device m_beep; required_device m_fdc; required_device m_floppy0; required_device m_floppy1; }; void amust_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { switch (id) { case TIMER_BEEP_OFF: m_beep->set_state(0); break; default: assert_always(false, "Unknown id in amust_state::device_timer"); } } //WRITE8_MEMBER( amust_state::port00_w ) //{ // membank("bankr0")->set_entry(BIT(data, 6)); // m_fdc->dden_w(BIT(data, 5)); // floppy_image_device *floppy = nullptr; // if (BIT(data, 0)) floppy = m_floppy0->get_device(); // m_fdc->set_floppy(floppy); // if (floppy) // floppy->ss_w(BIT(data, 4)); //} void amust_state::mem_map(address_map &map) { map.unmap_value_high(); map(0x0000, 0xf7ff).ram(); map(0xf800, 0xffff).bankr("bankr0").bankw("bankw0"); } void amust_state::io_map(address_map &map) { map.unmap_value_high(); map.global_mask(0xff); map(0x00, 0x00).rw("uart1", FUNC(i8251_device::data_r), FUNC(i8251_device::data_w)); map(0x01, 0x01).rw("uart1", FUNC(i8251_device::status_r), FUNC(i8251_device::control_w)); map(0x02, 0x02).rw("uart2", FUNC(i8251_device::data_r), FUNC(i8251_device::data_w)); map(0x03, 0x03).rw("uart2", FUNC(i8251_device::status_r), FUNC(i8251_device::control_w)); map(0x04, 0x07).rw("ppi1", FUNC(i8255_device::read), FUNC(i8255_device::write)); map(0x08, 0x0b).rw("ppi2", FUNC(i8255_device::read), FUNC(i8255_device::write)); map(0x0d, 0x0d).nopr().w(this, FUNC(amust_state::port0d_w)); map(0x0e, 0x0e).rw("crtc", FUNC(mc6845_device::status_r), FUNC(mc6845_device::address_w)); map(0x0f, 0x0f).rw("crtc", FUNC(mc6845_device::register_r), FUNC(mc6845_device::register_w)); map(0x10, 0x11).m(m_fdc, FUNC(upd765a_device::map)); map(0x14, 0x17).rw("pit", FUNC(pit8253_device::read), FUNC(pit8253_device::write)); } static void amust_floppies(device_slot_interface &device) { device.option_add("525qd", FLOPPY_525_QD); } /* Input ports */ static INPUT_PORTS_START( amust ) PORT_START("P9") // bits 6,7 not used? PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN ) // code @ FB83 PORT_DIPNAME( 0x10, 0x10, "Boot to Monitor" ) // code @ F895 PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x10, DEF_STR( On ) ) PORT_DIPNAME( 0x0f, 0x01, "Unknown" ) // code @ FC99 PORT_DIPSETTING( 0x01, "1" ) PORT_DIPSETTING( 0x02, "2" ) PORT_DIPSETTING( 0x04, "3" ) PORT_DIPSETTING( 0x08, "4" ) INPUT_PORTS_END // bodgy INTERRUPT_GEN_MEMBER( amust_state::irq_vs ) { m_maincpu->set_input_line_and_vector(INPUT_LINE_IRQ0, ASSERT_LINE, 0xff); } READ8_MEMBER( amust_state::port04_r ) { return m_port04; } WRITE8_MEMBER( amust_state::port04_w ) { m_port04 = data; } READ8_MEMBER( amust_state::port05_r ) { return 0; } READ8_MEMBER( amust_state::port06_r ) { return m_port06; } // BIT 5 low while writing to screen WRITE8_MEMBER( amust_state::port06_w ) { m_port06 = data; } READ8_MEMBER( amust_state::port08_r ) { return m_port08; } // lower 8 bits of video address WRITE8_MEMBER( amust_state::port08_w ) { m_port08 = data; } /* d0 - something to do with type of disk d1 - d2 - d3 - d4 - H = go to monitor; L = boot from disk d5 - status of disk-related; loops till NZ d6 - d7 - */ READ8_MEMBER( amust_state::port09_r ) { logerror("%s\n",machine().describe_context()); return ioport("P9")->read(); } READ8_MEMBER( amust_state::port0a_r ) { return m_port0a; } /* Bits 7,6,5,3 something to do with selecting which device causes interrupt? 50, 58 = video sync 70 disk D0 ? Bit 4 low = beeper. Lower 3 bits = upper part of video address */ WRITE8_MEMBER( amust_state::port0a_w ) { m_port0a = data; if (!BIT(data, 4)) { m_beep->set_state(1); timer_set(attotime::from_msec(150), TIMER_BEEP_OFF); } } WRITE8_MEMBER( amust_state::port0d_w ) { uint16_t video_address = m_port08 | ((m_port0a & 7) << 8); m_p_videoram[video_address] = data; } /* F4 Character Displayer */ static const gfx_layout amust_charlayout = { 8, 8, /* 8 x 8 characters */ 256, /* 256 characters */ 1, /* 1 bits per pixel */ { 0 }, /* no bitplanes */ /* x offsets */ { 0, 1, 2, 3, 4, 5, 6, 7 }, /* y offsets */ { 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 }, 8*8 /* every char takes 8 bytes */ }; static GFXDECODE_START( gfx_amust ) GFXDECODE_ENTRY( "chargen", 0x0000, amust_charlayout, 0, 1 ) GFXDECODE_END MC6845_UPDATE_ROW( amust_state::crtc_update_row ) { const rgb_t *palette = m_palette->palette()->entry_list_raw(); u8 chr,gfx,inv; u16 mem,x; u32 *p = &bitmap.pix32(y); for (x = 0; x < x_count; x++) { inv = (x == cursor_x) ? 0xff : 0; mem = (ma + x) & 0x7ff; chr = m_p_videoram[mem]; if (ra < 8) gfx = m_p_chargen[(chr<<3) | ra] ^ inv; else gfx = inv; /* Display a scanline of a character (8 pixels) */ *p++ = palette[BIT(gfx, 7)]; *p++ = palette[BIT(gfx, 6)]; *p++ = palette[BIT(gfx, 5)]; *p++ = palette[BIT(gfx, 4)]; *p++ = palette[BIT(gfx, 3)]; *p++ = palette[BIT(gfx, 2)]; *p++ = palette[BIT(gfx, 1)]; *p++ = palette[BIT(gfx, 0)]; } } MACHINE_RESET_MEMBER( amust_state, amust ) { membank("bankr0")->set_entry(0); // point at rom membank("bankw0")->set_entry(0); // always write to ram address_space &space = m_maincpu->space(AS_PROGRAM); space.write_byte(0x38, 0xed); space.write_byte(0x39, 0x4d); m_port04 = 0; m_port06 = 0; m_port08 = 0; m_port0a = 0; m_maincpu->set_state_int(Z80_PC, 0xf800); } void amust_state::init_amust() { u8 *main = memregion("maincpu")->base(); membank("bankr0")->configure_entry(1, &main[0xf800]); membank("bankr0")->configure_entry(0, &main[0x10800]); membank("bankw0")->configure_entry(0, &main[0xf800]); } MACHINE_CONFIG_START(amust_state::amust) /* basic machine hardware */ MCFG_DEVICE_ADD("maincpu",Z80, XTAL(16'000'000) / 4) MCFG_DEVICE_PROGRAM_MAP(mem_map) MCFG_DEVICE_IO_MAP(io_map) MCFG_DEVICE_VBLANK_INT_DRIVER("screen", amust_state, irq_vs) MCFG_MACHINE_RESET_OVERRIDE(amust_state, amust) /* video hardware */ MCFG_SCREEN_ADD_MONOCHROME("screen", RASTER, rgb_t::green()) MCFG_SCREEN_REFRESH_RATE(50) MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) /* not accurate */ MCFG_SCREEN_SIZE(640, 480) MCFG_SCREEN_VISIBLE_AREA(0, 640-1, 0, 480-1) MCFG_SCREEN_UPDATE_DEVICE("crtc", mc6845_device, screen_update) MCFG_PALETTE_ADD_MONOCHROME("palette") MCFG_DEVICE_ADD("gfxdecode", GFXDECODE, "palette", gfx_amust) /* sound hardware */ SPEAKER(config, "mono").front_center(); MCFG_DEVICE_ADD("beeper", BEEP, 800) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) /* Devices */ MCFG_MC6845_ADD("crtc", H46505, "screen", XTAL(14'318'181) / 8) MCFG_MC6845_SHOW_BORDER_AREA(false) MCFG_MC6845_CHAR_WIDTH(8) MCFG_MC6845_UPDATE_ROW_CB(amust_state, crtc_update_row) MCFG_UPD765A_ADD("fdc", false, true) MCFG_FLOPPY_DRIVE_ADD("fdc:0", amust_floppies, "525qd", floppy_image_device::default_floppy_formats) MCFG_FLOPPY_DRIVE_SOUND(true) MCFG_FLOPPY_DRIVE_ADD("fdc:1", amust_floppies, "525qd", floppy_image_device::default_floppy_formats) MCFG_FLOPPY_DRIVE_SOUND(true) MCFG_DEVICE_ADD("uart_clock", CLOCK, 153600) MCFG_CLOCK_SIGNAL_HANDLER(WRITELINE("uart1", i8251_device, write_txc)) MCFG_DEVCB_CHAIN_OUTPUT(WRITELINE("uart1", i8251_device, write_rxc)) MCFG_DEVICE_ADD("uart1", I8251, 0) MCFG_I8251_TXD_HANDLER(WRITELINE("rs232", rs232_port_device, write_txd)) MCFG_I8251_DTR_HANDLER(WRITELINE("rs232", rs232_port_device, write_dtr)) MCFG_I8251_RTS_HANDLER(WRITELINE("rs232", rs232_port_device, write_rts)) MCFG_DEVICE_ADD("rs232", RS232_PORT, default_rs232_devices, "keyboard") MCFG_RS232_RXD_HANDLER(WRITELINE("uart1", i8251_device, write_rxd)) MCFG_RS232_CTS_HANDLER(WRITELINE("uart1", i8251_device, write_cts)) MCFG_RS232_DSR_HANDLER(WRITELINE("uart1", i8251_device, write_dsr)) MCFG_DEVICE_ADD("uart2", I8251, 0) //MCFG_I8251_TXD_HANDLER(WRITELINE("rs232", rs232_port_device, write_txd)) //MCFG_I8251_DTR_HANDLER(WRITELINE("rs232", rs232_port_device, write_dtr)) //MCFG_I8251_RTS_HANDLER(WRITELINE("rs232", rs232_port_device, write_rts)) MCFG_DEVICE_ADD("pit", PIT8253, 0) MCFG_DEVICE_ADD("ppi1", I8255A, 0) MCFG_I8255_IN_PORTA_CB(READ8(*this, amust_state, port04_r)) MCFG_I8255_OUT_PORTA_CB(WRITE8(*this, amust_state, port04_w)) MCFG_I8255_IN_PORTB_CB(READ8(*this, amust_state, port05_r)) MCFG_I8255_IN_PORTC_CB(READ8(*this, amust_state, port06_r)) MCFG_I8255_OUT_PORTC_CB(WRITE8(*this, amust_state, port06_w)) MCFG_DEVICE_ADD("ppi2", I8255A, 0) MCFG_I8255_IN_PORTA_CB(READ8(*this, amust_state, port08_r)) MCFG_I8255_OUT_PORTA_CB(WRITE8(*this, amust_state, port08_w)) MCFG_I8255_IN_PORTB_CB(READ8(*this, amust_state, port09_r)) MCFG_I8255_IN_PORTC_CB(READ8(*this, amust_state, port0a_r)) MCFG_I8255_OUT_PORTC_CB(WRITE8(*this, amust_state, port0a_w)) MACHINE_CONFIG_END /* ROM definition */ ROM_START( amust ) ROM_REGION( 0x11000, "maincpu", ROMREGION_ERASEFF ) ROM_LOAD( "mon_h.rom", 0x10000, 0x1000, CRC(10dceac6) SHA1(1ef80039063f7a6455563d59f1bcc23e09eca369) ) ROM_REGION( 0x800, "chargen", 0 ) ROM_LOAD( "cg4.rom", 0x000, 0x800, CRC(52e7b9d8) SHA1(cc6d457634eb688ccef471f72bddf0424e64b045) ) ROM_REGION( 0x800, "keyboard", 0 ) ROM_LOAD( "kbd_3.rom", 0x000, 0x800, CRC(d9441b35) SHA1(ce250ab1e892a13fd75182703f259855388c6bf4) ) ROM_REGION( 0x800, "videoram", ROMREGION_ERASE00 ) ROM_END /* Driver */ // YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS COMP( 1983, amust, 0, 0, amust, amust, amust_state, init_amust, "Amust", "Amust Executive 816", MACHINE_NOT_WORKING )