// license:MAME // copyright-holders:Robbbert /*************************************************************************** BINBUG and DG640 2013-01-14 Driver created All input must be in uppercase. Commands: A - See and alter memory B - Set breakpoint (2 permitted) C - Clear breakpoint D - cassette save G - Go to address, run L - cassette load S - See and alter registers BINBUG is an alternate bios to PIPBUG, however it uses its own video output. Method of output is through a DG640 board (sold by Applied Technology) which uses a MCM6574 as a character generator. The DG640 also supports blinking, reverse-video, and LORES graphics. It is a S100 card, also known as ETI-640. Keyboard input, like PIPBUG, is via a serial device. The baud rate is 300, 8N1. The SENSE and FLAG lines are used for 300 baud cassette, in conjunction with unknown hardware. There are 3 versions of BINBUG: - 3.6 300 baud tape routines, 300 baud keyboard, memory-mapped VDU - 4.4 300 baud keyboard, ACOS tape system, advanced video routines - 5.2 ACOS tape system, 1200 baud terminal Status: - DG640 is completely emulated, even though BINBUG doesn't use most of its features. - BINBUG works except that the cassette interface isn't the same as real hardware. But you can save, and load it back. ToDo: - Need dumps of 4.4 and 5.2. - Fix cassette ****************************************************************************/ #include "bus/rs232/keyboard.h" #include "cpu/s2650/s2650.h" #include "imagedev/cassette.h" #include "sound/wave.h" #include "imagedev/snapquik.h" #define KEYBOARD_TAG "keyboard" class binbug_state : public driver_device { public: binbug_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag), m_rs232(*this, KEYBOARD_TAG), m_cass(*this, "cassette"), m_p_videoram(*this, "videoram"), m_p_attribram(*this, "attribram"), m_maincpu(*this, "maincpu") { } DECLARE_WRITE8_MEMBER(binbug_ctrl_w); DECLARE_READ8_MEMBER(binbug_serial_r); DECLARE_WRITE_LINE_MEMBER(binbug_serial_w); const UINT8 *m_p_chargen; UINT8 m_framecnt; virtual void video_start(); UINT32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); optional_device m_rs232; required_device m_cass; required_shared_ptr m_p_videoram; required_shared_ptr m_p_attribram; required_device m_maincpu; DECLARE_QUICKLOAD_LOAD_MEMBER( binbug ); }; WRITE8_MEMBER( binbug_state::binbug_ctrl_w ) { } READ8_MEMBER( binbug_state::binbug_serial_r ) { return m_rs232->rxd_r() & (m_cass->input() < 0.03); } WRITE_LINE_MEMBER( binbug_state::binbug_serial_w ) { m_cass->output(state ? -1.0 : +1.0); } static ADDRESS_MAP_START(binbug_mem, AS_PROGRAM, 8, binbug_state) ADDRESS_MAP_UNMAP_HIGH AM_RANGE( 0x0000, 0x03ff) AM_ROM AM_RANGE( 0x0400, 0x77ff) AM_RAM AM_RANGE( 0x7800, 0x7bff) AM_RAM AM_SHARE("videoram") AM_RANGE( 0x7c00, 0x7fff) AM_RAM AM_SHARE("attribram") ADDRESS_MAP_END static ADDRESS_MAP_START(binbug_io, AS_IO, 8, binbug_state) ADDRESS_MAP_UNMAP_HIGH AM_RANGE(S2650_CTRL_PORT, S2650_CTRL_PORT) AM_WRITE(binbug_ctrl_w) AM_RANGE(S2650_SENSE_PORT, S2650_SENSE_PORT) AM_READ(binbug_serial_r) ADDRESS_MAP_END /* Input ports */ static INPUT_PORTS_START( binbug ) INPUT_PORTS_END void binbug_state::video_start() { m_p_chargen = memregion("chargen")->base(); } UINT32 binbug_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { // attributes bit 0 = flash, bit 1 = lores. Also bit 7 of the character = reverse-video (text only). UINT8 y,ra,chr,gfx,attr,inv,gfxbit; UINT16 sy=0,ma=0,x; bool flash; m_framecnt++; for (y = 0; y < 16; y++) { for (ra = 0; ra < 16; ra++) { UINT16 *p = &bitmap.pix16(sy++); for (x = ma; x < ma + 64; x++) { attr = m_p_attribram[x]; chr = m_p_videoram[x]; flash = BIT(m_framecnt, 4) & BIT(attr, 0); if (BIT(attr, 1)) // lores gfx - can flash { if (flash) chr = 0; // blank part of flashing gfxbit = (ra & 0x0c)>>1; /* Display one line of a lores character (8 pixels) */ *p++ = BIT(chr, gfxbit); *p++ = BIT(chr, gfxbit); *p++ = BIT(chr, gfxbit); *p++ = BIT(chr, gfxbit); gfxbit++; *p++ = BIT(chr, gfxbit); *p++ = BIT(chr, gfxbit); *p++ = BIT(chr, gfxbit); *p++ = BIT(chr, gfxbit); } else { gfx = 0; if (!flash) { inv = BIT(chr, 7) ? 0xff : 0; // text with bit 7 high is reversed chr &= 0x7f; gfx = inv; // if g,j,p,q,y; lower the descender if ((chr==0x2c)||(chr==0x3b)||(chr==0x67)||(chr==0x6a)||(chr==0x70)||(chr==0x71)||(chr==0x79)) { if (ra > 6) gfx = m_p_chargen[(chr<<4) | (ra-7) ] ^ inv; } else { if ((ra > 3) & (ra < 13)) gfx = m_p_chargen[(chr<<4) | (ra-4) ] ^ inv; } } /* Display a scanline of a character */ *p++ = BIT(gfx, 7); *p++ = BIT(gfx, 6); *p++ = BIT(gfx, 5); *p++ = BIT(gfx, 4); *p++ = BIT(gfx, 3); *p++ = BIT(gfx, 2); *p++ = BIT(gfx, 1); *p++ = BIT(gfx, 0); } } } ma+=64; } return 0; } /* F4 Character Displayer */ static const gfx_layout dg640_charlayout = { 7, 9, /* 7 x 9 characters */ 128, /* 128 characters */ 1, /* 1 bits per pixel */ { 0 }, /* no bitplanes */ /* x offsets */ { 0, 1, 2, 3, 4, 5, 6 }, /* y offsets */ { 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8, 8*8 }, 8*16 /* every char takes 16 bytes */ }; static GFXDECODE_START( dg640 ) GFXDECODE_ENTRY( "chargen", 0x0000, dg640_charlayout, 0, 1 ) GFXDECODE_END QUICKLOAD_LOAD_MEMBER( binbug_state, binbug ) { address_space &space = m_maincpu->space(AS_PROGRAM); int i; int quick_addr = 0x440; int exec_addr; int quick_length; dynamic_buffer quick_data; int read_; int result = IMAGE_INIT_FAIL; quick_length = image.length(); if (quick_length < 0x0444) { image.seterror(IMAGE_ERROR_INVALIDIMAGE, "File too short"); image.message(" File too short"); } else if (quick_length > 0x8000) { image.seterror(IMAGE_ERROR_INVALIDIMAGE, "File too long"); image.message(" File too long"); } else { quick_data.resize(quick_length); read_ = image.fread( quick_data, quick_length); if (read_ != quick_length) { image.seterror(IMAGE_ERROR_INVALIDIMAGE, "Cannot read the file"); image.message(" Cannot read the file"); } else if (quick_data[0] != 0xc4) { image.seterror(IMAGE_ERROR_INVALIDIMAGE, "Invalid header"); image.message(" Invalid header"); } else { exec_addr = quick_data[1] * 256 + quick_data[2]; if (exec_addr >= quick_length) { image.seterror(IMAGE_ERROR_INVALIDIMAGE, "Exec address beyond end of file"); image.message(" Exec address beyond end of file"); } else { for (i = quick_addr; i < read_; i++) space.write_byte(i, quick_data[i]); /* display a message about the loaded quickload */ image.message(" Quickload: size=%04X : exec=%04X",quick_length,exec_addr); // Start the quickload m_maincpu->set_state_int(S2650_PC, exec_addr); result = IMAGE_INIT_PASS; } } } return result; } static DEVICE_INPUT_DEFAULTS_START( keyboard ) DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_300 ) DEVICE_INPUT_DEFAULTS( "RS232_STARTBITS", 0xff, RS232_STARTBITS_1 ) 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 MACHINE_CONFIG_START( binbug, binbug_state ) /* basic machine hardware */ MCFG_CPU_ADD("maincpu",S2650, XTAL_1MHz) MCFG_CPU_PROGRAM_MAP(binbug_mem) MCFG_CPU_IO_MAP(binbug_io) MCFG_S2650_FLAG_HANDLER(WRITELINE(binbug_state, binbug_serial_w)) /* video hardware */ MCFG_SCREEN_ADD("screen", RASTER) MCFG_SCREEN_REFRESH_RATE(50) MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) /* not accurate */ MCFG_SCREEN_UPDATE_DRIVER(binbug_state, screen_update) MCFG_SCREEN_SIZE(512, 256) MCFG_SCREEN_VISIBLE_AREA(0, 511, 0, 255) MCFG_SCREEN_PALETTE("palette") MCFG_GFXDECODE_ADD("gfxdecode", "palette", dg640) MCFG_PALETTE_ADD_MONOCHROME_AMBER("palette") /* Keyboard */ MCFG_RS232_PORT_ADD(KEYBOARD_TAG, default_rs232_devices, "keyboard") MCFG_DEVICE_CARD_DEVICE_INPUT_DEFAULTS("keyboard", keyboard) /* Cassette */ MCFG_CASSETTE_ADD( "cassette" ) MCFG_SPEAKER_STANDARD_MONO("mono") MCFG_SOUND_WAVE_ADD(WAVE_TAG, "cassette") MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) /* quickload */ MCFG_QUICKLOAD_ADD("quickload", binbug_state, binbug, "pgm", 1) MACHINE_CONFIG_END /* ROM definition */ ROM_START( binbug ) ROM_REGION( 0x8000, "maincpu", ROMREGION_ERASEFF ) ROM_LOAD( "binbug.rom", 0x0000, 0x0400, CRC(2cb1ac6e) SHA1(a969883fc767484d6b0fa103cfa4b4129b90441b) ) ROM_REGION( 0x0800, "chargen", 0 ) ROM_LOAD( "6574.bin", 0x0000, 0x0800, CRC(fd75df4f) SHA1(4d09aae2f933478532b7d3d1a2dee7123d9828ca) ) ROM_FILL(0, 16, 0) ROM_END /* Driver */ /* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS */ COMP( 1980, binbug, pipbug, 0, binbug, binbug, driver_device, 0, "MicroByte", "BINBUG 3.6", 0 ) /******************************************************************************************************************/ /* DG680 (ETI-680), using the DGOS-Z80 operating system. This is a S100 card. In some ways, this system is the ancestor of the original Microbee. ROM is typed in from a PDF, therefore marked a bad dump. No schematic available, most of this is guesswork. Port 0 is the input from an ascii keyboard. Port 2 is the cassette interface. Port 8 controls some kind of memory protection scheme. The code indicates that B is the page to protect, and A is the code (0x08 = inhibit; 0x0B = unprotect; 0x0C = enable; 0x0E = protect). There are 256 pages so each page is 256 bytes. The clock is controlled by the byte in D80D. Monitor Commands: C (compare)* E (edit)* F (fill)* G - Go to address I - Inhibit CTC M (move)* P (clear screen)* R (read tape)* S (search)* T hhmm [ss] - Set the time W (write tape)* X - protection status XC - clear ram XD - same as X XE - enable facilities XF - disable facilities XP - protect block XU - unprotect block Z - go to 0000. * These commands are identical to the Microbee ones. ToDo: - dips - leds - need schematic to find out what else is missing - cassette - ctc / clock */ #include "cpu/z80/z80.h" #include "machine/z80ctc.h" #include "machine/z80pio.h" #include "cpu/z80/z80daisy.h" class dg680_state : public binbug_state { public: dg680_state(const machine_config &mconfig, device_type type, const char *tag) : binbug_state(mconfig, type, tag), m_maincpu(*this, "maincpu"), m_ctc(*this, "z80ctc"), m_pio(*this, "z80pio") { } DECLARE_READ8_MEMBER(porta_r); DECLARE_READ8_MEMBER(portb_r); DECLARE_WRITE8_MEMBER(portb_w); DECLARE_READ8_MEMBER(port08_r); DECLARE_WRITE8_MEMBER(port08_w); DECLARE_WRITE8_MEMBER(kbd_put); TIMER_DEVICE_CALLBACK_MEMBER(time_tick); TIMER_DEVICE_CALLBACK_MEMBER(uart_tick); UINT8 m_pio_b; UINT8 m_term_data; UINT8 m_protection[0x100]; virtual void machine_reset(); required_device m_maincpu; required_device m_ctc; required_device m_pio; }; static ADDRESS_MAP_START(dg680_mem, AS_PROGRAM, 8, dg680_state) ADDRESS_MAP_UNMAP_HIGH AM_RANGE( 0x0000, 0xcfff) AM_RAM AM_RANGE( 0xd000, 0xd7ff) AM_ROM AM_RANGE( 0xd800, 0xefff) AM_RAM AM_RANGE( 0xf000, 0xf3ff) AM_RAM AM_SHARE("videoram") AM_RANGE( 0xf400, 0xf7ff) AM_RAM AM_SHARE("attribram") ADDRESS_MAP_END static ADDRESS_MAP_START(dg680_io, AS_IO, 8, dg680_state) ADDRESS_MAP_UNMAP_HIGH ADDRESS_MAP_GLOBAL_MASK(0xff) AM_RANGE(0x00,0x03) AM_DEVREADWRITE("z80pio", z80pio_device, read_alt, write_alt) AM_RANGE(0x04,0x07) AM_DEVREADWRITE("z80ctc", z80ctc_device, read, write) AM_RANGE(0x08,0x08) AM_READWRITE(port08_r,port08_w) //SWP Control and Status //AM_RANGE(0x09,0x09) parallel input port // Optional AM9519 Programmable Interrupt Controller (port c = data, port d = control) //AM_RANGE(0x0c,0x0d) AM_DEVREADWRITE("am9519", am9519_device, read, write) ADDRESS_MAP_END void dg680_state::machine_reset() { m_maincpu->set_pc(0xd000); } // this is a guess there is no information available static const z80_daisy_config dg680_daisy_chain[] = { { "z80ctc" }, { "z80pio" }, { NULL } }; /* Input ports */ static INPUT_PORTS_START( dg680 ) INPUT_PORTS_END WRITE8_MEMBER( dg680_state::kbd_put ) { m_term_data = data; /* strobe in keyboard data */ m_pio->strobe_a(0); m_pio->strobe_a(1); } READ8_MEMBER( dg680_state::porta_r ) { UINT8 data = m_term_data; m_term_data = 0; return data; } READ8_MEMBER( dg680_state::portb_r ) { return m_pio_b | (m_cass->input() > 0.03); } // bit 1 = cassout; bit 2 = motor on WRITE8_MEMBER( dg680_state::portb_w ) { m_pio_b = data & 0xfe; m_cass->output(BIT(data, 1) ? -1.0 : +1.0); } READ8_MEMBER( dg680_state::port08_r ) { UINT8 breg = m_maincpu->state_int(Z80_B); return m_protection[breg]; } WRITE8_MEMBER( dg680_state::port08_w ) { UINT8 breg = m_maincpu->state_int(Z80_B); m_protection[breg] = data; } TIMER_DEVICE_CALLBACK_MEMBER(dg680_state::time_tick) { // ch0 is for the clock m_ctc->trg0(1); m_ctc->trg0(0); // no idea about ch2 m_ctc->trg2(1); m_ctc->trg2(0); } TIMER_DEVICE_CALLBACK_MEMBER(dg680_state::uart_tick) { // ch3 is for cassette m_ctc->trg3(1); m_ctc->trg3(0); } static MACHINE_CONFIG_START( dg680, dg680_state ) /* basic machine hardware */ MCFG_CPU_ADD("maincpu",Z80, XTAL_8MHz / 4) MCFG_CPU_PROGRAM_MAP(dg680_mem) MCFG_CPU_IO_MAP(dg680_io) MCFG_CPU_CONFIG(dg680_daisy_chain) /* video hardware */ MCFG_SCREEN_ADD("screen", RASTER) MCFG_SCREEN_REFRESH_RATE(50) MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500)) /* not accurate */ MCFG_SCREEN_UPDATE_DRIVER(binbug_state, screen_update) MCFG_SCREEN_SIZE(512, 256) MCFG_SCREEN_VISIBLE_AREA(0, 511, 0, 255) MCFG_SCREEN_PALETTE("palette") MCFG_GFXDECODE_ADD("gfxdecode", "palette", dg640) MCFG_PALETTE_ADD_MONOCHROME_AMBER("palette") /* Keyboard */ MCFG_DEVICE_ADD("keyb", GENERIC_KEYBOARD, 0) MCFG_GENERIC_KEYBOARD_CB(WRITE8(dg680_state, kbd_put)) /* Cassette */ MCFG_CASSETTE_ADD( "cassette" ) MCFG_SPEAKER_STANDARD_MONO("mono") MCFG_SOUND_WAVE_ADD(WAVE_TAG, "cassette") MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) /* Devices */ MCFG_DEVICE_ADD("z80ctc", Z80CTC, XTAL_8MHz / 4) MCFG_Z80CTC_INTR_CB(INPUTLINE("maincpu", INPUT_LINE_IRQ0)) MCFG_Z80CTC_ZC0_CB(DEVWRITELINE("z80ctc", z80ctc_device, trg1)) MCFG_DEVICE_ADD("z80pio", Z80PIO, XTAL_8MHz / 4) MCFG_Z80PIO_OUT_INT_CB(INPUTLINE("maincpu", INPUT_LINE_IRQ0)) MCFG_Z80PIO_IN_PA_CB(READ8(dg680_state, porta_r)) // OUT_ARDY - this activates to ask for kbd data but not known if actually used MCFG_Z80PIO_IN_PB_CB(READ8(dg680_state, portb_r)) MCFG_Z80PIO_OUT_PB_CB(WRITE8(dg680_state, portb_w)) MCFG_TIMER_DRIVER_ADD_PERIODIC("ctc0", dg680_state, time_tick, attotime::from_hz(200)) MCFG_TIMER_DRIVER_ADD_PERIODIC("ctc3", dg680_state, uart_tick, attotime::from_hz(4800)) MACHINE_CONFIG_END /* ROM definition */ ROM_START( dg680 ) ROM_REGION( 0x10000, "maincpu", 0 ) ROM_LOAD( "dg680.rom", 0xd000, 0x0800, BAD_DUMP CRC(c1aaef6a) SHA1(1508ca8315452edfb984718e795ccbe79a0c0b58) ) ROM_REGION( 0x0800, "chargen", 0 ) ROM_LOAD( "6574.bin", 0x0000, 0x0800, CRC(fd75df4f) SHA1(4d09aae2f933478532b7d3d1a2dee7123d9828ca) ) ROM_REGION( 0x0020, "proms", 0 ) ROM_LOAD( "82s123.bin", 0x0000, 0x0020, NO_DUMP ) ROM_END /* Driver */ /* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS */ COMP( 1980, dg680, 0, 0, dg680, dg680, driver_device, 0, "David Griffiths", "DG680 with DGOS-Z80 1.4", GAME_NOT_WORKING | GAME_NO_SOUND_HW )