// license:BSD-3-Clause // copyright-holders:Curt Coder /* COSMICOS http://retro.hansotten.nl/index.php?page=1802-cosmicos Press G to start, and to enable the debugger (if -debug used). The video options include 8-digit LEDs, 2-digit LEDs, and CRT, of which the default is the 8-digit LEDs. Unknown how to enable the others. HEX-monitor 0 - start user program 1 - inspect and/or change memory 2 - write memory block to cassette 3 - read memory block from cassette 4 - move memory block 5 - write memory block to EPROM C - start user program from address 0000 */ /* TODO: - display interface INH - 2 segment display - single step - ascii monitor - PPI 8255 - Floppy WD1793 - COM8017 UART to printer */ #include "emu.h" #include "includes/cosmicos.h" #include "screen.h" #include "speaker.h" #include "cosmicos.lh" enum { MODE_RUN, MODE_LOAD, MODE_PAUSE, MODE_RESET }; /* Read/Write Handlers */ uint8_t cosmicos_state::read(offs_t offset) { if (m_boot) offset |= 0xc000; uint8_t data = 0; if (offset < 0xc000) { // TODO } else if (offset < 0xd000) { data = m_rom->base()[offset & 0xfff]; } else if (!m_ram_disable && (offset >= 0xff00)) { data = m_ram->pointer()[offset & 0xff]; } return data; } void cosmicos_state::write(offs_t offset, uint8_t data) { if (m_boot) offset |= 0xc000; if (offset < 0xc000) { // TODO } else if (!m_ram_disable && !m_ram_protect && (offset >= 0xff00)) { m_ram->pointer()[offset & 0xff] = data; } } uint8_t cosmicos_state::video_off_r() { uint8_t data = 0; if (!m_q) { data = m_cti->dispoff_r(); } return data; } uint8_t cosmicos_state::video_on_r() { uint8_t data = 0; if (!m_q) { data = m_cti->dispon_r(); } return data; } void cosmicos_state::audio_latch_w(uint8_t data) { if (m_q) { m_cti->tone_latch_w(data); } } uint8_t cosmicos_state::hex_keyboard_r() { uint8_t data = 0; for (int i = 0; i < 4; i++) { if (BIT(m_keylatch, i)) { uint8_t keydata = m_key_row[i]->read(); if (BIT(keydata, 0)) data |= 0x01; if (BIT(keydata, 1)) data |= 0x02; if (BIT(keydata, 2)) data |= 0x04; if (BIT(keydata, 3)) data |= 0x06; } } return data; } void cosmicos_state::hex_keylatch_w(uint8_t data) { m_keylatch = data & 0x0f; } uint8_t cosmicos_state::reset_counter_r() { m_counter = 0; return 0; } void cosmicos_state::segment_w(uint8_t data) { m_counter++; if (m_counter == 10) { m_counter = 0; } if ((m_counter > 0) && (m_counter < 9)) { m_digits[10 - m_counter] = data; } } uint8_t cosmicos_state::data_r() { return m_data; } void cosmicos_state::display_w(uint8_t data) { m_segment = data; } /* Memory Maps */ void cosmicos_state::cosmicos_mem(address_map &map) { map(0x0000, 0xffff).rw(FUNC(cosmicos_state::read), FUNC(cosmicos_state::write)); } void cosmicos_state::cosmicos_io(address_map &map) { // map(0x00, 0x00) map(0x01, 0x01).r(FUNC(cosmicos_state::video_on_r)); map(0x02, 0x02).rw(FUNC(cosmicos_state::video_off_r), FUNC(cosmicos_state::audio_latch_w)); // map(0x03, 0x03) // map(0x04, 0x04) map(0x05, 0x05).rw(FUNC(cosmicos_state::hex_keyboard_r), FUNC(cosmicos_state::hex_keylatch_w)); map(0x06, 0x06).rw(FUNC(cosmicos_state::reset_counter_r), FUNC(cosmicos_state::segment_w)); map(0x07, 0x07).rw(FUNC(cosmicos_state::data_r), FUNC(cosmicos_state::display_w)); } /* Input Ports */ INPUT_CHANGED_MEMBER( cosmicos_state::data ) { uint8_t data = m_io_data->read(); int i; for (i = 0; i < 8; i++) { if (!BIT(data, i)) { m_data |= (1 << i); m_leds[LED_D0 - i] = 1; } } } INPUT_CHANGED_MEMBER( cosmicos_state::enter ) { if (!newval && !m_wait && !m_clear) { m_maincpu->set_input_line(COSMAC_INPUT_LINE_DMAIN, ASSERT_LINE); } } INPUT_CHANGED_MEMBER( cosmicos_state::single_step ) { // if in PAUSE mode, set RUN mode until TPB=active } void cosmicos_state::set_cdp1802_mode(int mode) { m_leds[LED_RUN] = 0; m_leds[LED_LOAD] = 0; m_leds[LED_PAUSE] = 0; m_leds[LED_RESET] = 0; switch (mode) { case MODE_RUN: m_leds[LED_RUN] = 1; m_wait = 1; m_clear = 1; break; case MODE_LOAD: m_leds[LED_LOAD] = 1; m_wait = 0; m_clear = 0; break; case MODE_PAUSE: m_leds[LED_PAUSE] = 1; m_wait = 1; m_clear = 0; break; case MODE_RESET: m_maincpu->set_input_line(COSMAC_INPUT_LINE_INT, CLEAR_LINE); m_maincpu->set_input_line(COSMAC_INPUT_LINE_DMAIN, CLEAR_LINE); m_wait = 1; m_clear = 0; m_boot = 1; m_leds[LED_RESET] = 1; break; } } INPUT_CHANGED_MEMBER( cosmicos_state::run ) { if (!newval) set_cdp1802_mode(MODE_RUN); } INPUT_CHANGED_MEMBER( cosmicos_state::load ) { if (!newval) set_cdp1802_mode(MODE_LOAD); } INPUT_CHANGED_MEMBER( cosmicos_state::cosmicos_pause ) { if (!newval) set_cdp1802_mode(MODE_PAUSE); } INPUT_CHANGED_MEMBER( cosmicos_state::reset ) { if (!newval) set_cdp1802_mode(MODE_RESET); } void cosmicos_state::clear_input_data() { int i; m_data = 0; for (i = 0; i < 8; i++) { m_leds[LED_D0 - i] = 0; } } INPUT_CHANGED_MEMBER( cosmicos_state::clear_data ) { clear_input_data(); } INPUT_CHANGED_MEMBER( cosmicos_state::memory_protect ) { m_ram_protect = newval; } INPUT_CHANGED_MEMBER( cosmicos_state::memory_disable ) { m_ram_disable = newval; } static INPUT_PORTS_START( cosmicos ) PORT_START("DATA") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D0") PORT_CODE(KEYCODE_0_PAD) PORT_CHAR('0') PORT_CHANGED_MEMBER(DEVICE_SELF, cosmicos_state, data, 0) PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D1") PORT_CODE(KEYCODE_1_PAD) PORT_CHAR('1') PORT_CHANGED_MEMBER(DEVICE_SELF, cosmicos_state, data, 0) PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D2") PORT_CODE(KEYCODE_2_PAD) PORT_CHAR('2') PORT_CHANGED_MEMBER(DEVICE_SELF, cosmicos_state, data, 0) PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D3") PORT_CODE(KEYCODE_3_PAD) PORT_CHAR('3') PORT_CHANGED_MEMBER(DEVICE_SELF, cosmicos_state, data, 0) PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D4") PORT_CODE(KEYCODE_4_PAD) PORT_CHAR('4') PORT_CHANGED_MEMBER(DEVICE_SELF, cosmicos_state, data, 0) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D5") PORT_CODE(KEYCODE_5_PAD) PORT_CHAR('5') PORT_CHANGED_MEMBER(DEVICE_SELF, cosmicos_state, data, 0) PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D6") PORT_CODE(KEYCODE_6_PAD) PORT_CHAR('6') PORT_CHANGED_MEMBER(DEVICE_SELF, cosmicos_state, data, 0) PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("D7") PORT_CODE(KEYCODE_7_PAD) PORT_CHAR('7') PORT_CHANGED_MEMBER(DEVICE_SELF, cosmicos_state, data, 0) PORT_START("BUTTONS") PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter") PORT_CHANGED_MEMBER(DEVICE_SELF, cosmicos_state, enter, 0) PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_SPACE) PORT_NAME("Single Step") PORT_CHANGED_MEMBER(DEVICE_SELF, cosmicos_state, single_step, 0) PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_G) PORT_NAME("Run") PORT_CHANGED_MEMBER(DEVICE_SELF, cosmicos_state, run, 0) PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L) PORT_NAME("Load") PORT_CHANGED_MEMBER(DEVICE_SELF, cosmicos_state, load, 0) PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_S) PORT_NAME(DEF_STR( Pause )) PORT_CHANGED_MEMBER(DEVICE_SELF, cosmicos_state, cosmicos_pause, 0) PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_R) PORT_NAME("Reset") PORT_CHANGED_MEMBER(DEVICE_SELF, cosmicos_state, reset, 0) PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_CODE(KEYCODE_DEL_PAD) PORT_NAME("Clear Data") PORT_CHANGED_MEMBER(DEVICE_SELF, cosmicos_state, clear_data, 0) PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_M) PORT_NAME("Memory Protect") PORT_CHANGED_MEMBER(DEVICE_SELF, cosmicos_state, memory_protect, 0) PORT_TOGGLE PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_N) PORT_NAME("Memory Disable") PORT_CHANGED_MEMBER(DEVICE_SELF, cosmicos_state, memory_disable, 0) PORT_TOGGLE PORT_START("Y1") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_0) PORT_CHAR('0') PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_1) PORT_CHAR('1') PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_2) PORT_CHAR('2') PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_3) PORT_CHAR('3') PORT_START("Y2") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_4) PORT_CHAR('4') PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_5) PORT_CHAR('5') PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_6) PORT_CHAR('6') PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_7) PORT_CHAR('7') PORT_START("Y3") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_8) PORT_CHAR('8') PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_9) PORT_CHAR('9') PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A) PORT_CHAR('A') PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_B) PORT_CHAR('B') PORT_START("Y4") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_C) PORT_CHAR('C') PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_D) PORT_CHAR('D') PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_E) PORT_CHAR('E') PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F) PORT_CHAR('F') PORT_START("SPECIAL") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("RET") PORT_CODE(KEYCODE_F1) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("DEC") PORT_CODE(KEYCODE_F2) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("REQ") PORT_CODE(KEYCODE_F3) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("SEQ") PORT_CODE(KEYCODE_F4) INPUT_PORTS_END /* Video */ TIMER_DEVICE_CALLBACK_MEMBER(cosmicos_state::digit_tick) { m_digit ^= 1; m_digits[m_digit] = m_segment; } TIMER_DEVICE_CALLBACK_MEMBER(cosmicos_state::int_tick) { m_maincpu->set_input_line(COSMAC_INPUT_LINE_INT, ASSERT_LINE); } WRITE_LINE_MEMBER( cosmicos_state::dmaout_w ) { m_dmaout = state; } WRITE_LINE_MEMBER( cosmicos_state::efx_w ) { m_efx = state; } /* CDP1802 Configuration */ READ_LINE_MEMBER( cosmicos_state::wait_r ) { return m_wait; } READ_LINE_MEMBER( cosmicos_state::clear_r ) { return m_clear; } READ_LINE_MEMBER( cosmicos_state::ef1_r ) { uint8_t special = m_special->read(); return BIT(special, 0); } READ_LINE_MEMBER( cosmicos_state::ef2_r ) { uint8_t special = m_special->read(); int casin = (m_cassette)->input() < 0.0; m_leds[LED_CASSETTE] = casin; return BIT(special, 1) | BIT(special, 3) | casin; } READ_LINE_MEMBER( cosmicos_state::ef3_r ) { uint8_t special = m_special->read(); return BIT(special, 2) | BIT(special, 3); } READ_LINE_MEMBER( cosmicos_state::ef4_r ) { return BIT(m_buttons->read(), 0); } WRITE_LINE_MEMBER( cosmicos_state::q_w ) { /* cassette */ m_cassette->output(state ? +1.0 : -1.0); /* boot */ if (state) m_boot = 0; /* CDP1864 audio enable */ m_cti->aoe_w(state); m_q = state; } uint8_t cosmicos_state::dma_r() { return m_data; } void cosmicos_state::sc_w(uint8_t data) { int sc1 = BIT(data, 1); if (m_sc1 && !sc1) { clear_input_data(); } if (sc1) { m_maincpu->set_input_line(COSMAC_INPUT_LINE_INT, CLEAR_LINE); m_maincpu->set_input_line(COSMAC_INPUT_LINE_DMAIN, CLEAR_LINE); } m_sc1 = sc1; } /* Machine Initialization */ void cosmicos_state::machine_start() { m_digits.resolve(); m_leds.resolve(); /* initialize LED display */ m_led->rbi_w(1); /* register for state saving */ save_item(NAME(m_wait)); save_item(NAME(m_clear)); save_item(NAME(m_sc1)); save_item(NAME(m_data)); save_item(NAME(m_boot)); save_item(NAME(m_ram_protect)); save_item(NAME(m_ram_disable)); save_item(NAME(m_keylatch)); save_item(NAME(m_segment)); save_item(NAME(m_digit)); save_item(NAME(m_counter)); save_item(NAME(m_q)); save_item(NAME(m_dmaout)); save_item(NAME(m_efx)); save_item(NAME(m_video_on)); } void cosmicos_state::machine_reset() { set_cdp1802_mode(MODE_RESET); } /* Quickload */ QUICKLOAD_LOAD_MEMBER(cosmicos_state::quickload_cb) { uint8_t *ptr = m_rom->base(); int size = image.length(); /* load image to RAM */ image.fread(ptr, size); return image_init_result::PASS; } /* Machine Driver */ void cosmicos_state::cosmicos(machine_config &config) { /* basic machine hardware */ CDP1802(config, m_maincpu, 1.75_MHz_XTAL); m_maincpu->set_addrmap(AS_PROGRAM, &cosmicos_state::cosmicos_mem); m_maincpu->set_addrmap(AS_IO, &cosmicos_state::cosmicos_io); m_maincpu->wait_cb().set(FUNC(cosmicos_state::wait_r)); m_maincpu->clear_cb().set(FUNC(cosmicos_state::clear_r)); m_maincpu->ef1_cb().set(FUNC(cosmicos_state::ef1_r)); m_maincpu->ef2_cb().set(FUNC(cosmicos_state::ef2_r)); m_maincpu->ef3_cb().set(FUNC(cosmicos_state::ef3_r)); m_maincpu->ef4_cb().set(FUNC(cosmicos_state::ef4_r)); m_maincpu->q_cb().set(FUNC(cosmicos_state::q_w)); m_maincpu->dma_rd_cb().set(FUNC(cosmicos_state::dma_r)); m_maincpu->sc_cb().set(FUNC(cosmicos_state::sc_w)); /* video hardware */ config.set_default_layout(layout_cosmicos); DM9368(config, m_led, 0); TIMER(config, "digit").configure_periodic(FUNC(cosmicos_state::digit_tick), attotime::from_hz(100)); TIMER(config, "interrupt").configure_periodic(FUNC(cosmicos_state::int_tick), attotime::from_hz(1000)); SCREEN(config, SCREEN_TAG, SCREEN_TYPE_RASTER); /* sound hardware */ SPEAKER(config, "mono").front_center(); SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25); CDP1864(config, m_cti, 1.75_MHz_XTAL).set_screen(SCREEN_TAG); m_cti->inlace_cb().set_constant(0); m_cti->int_cb().set_inputline(m_maincpu, COSMAC_INPUT_LINE_INT); m_cti->dma_out_cb().set(FUNC(cosmicos_state::dmaout_w)); m_cti->efx_cb().set(FUNC(cosmicos_state::efx_w)); m_cti->rdata_cb().set_constant(1); m_cti->gdata_cb().set_constant(1); m_cti->bdata_cb().set_constant(1); m_cti->set_chrominance(RES_K(2), 0, 0, 0); // R2 m_cti->add_route(ALL_OUTPUTS, "mono", 0.25); /* devices */ QUICKLOAD(config, "quickload", "bin").set_load_callback(FUNC(cosmicos_state::quickload_cb)); CASSETTE(config, m_cassette); m_cassette->set_default_state(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_ENABLED); m_cassette->add_route(ALL_OUTPUTS, "mono", 0.05); /* internal ram */ RAM(config, RAM_TAG).set_default_size("256").set_extra_options("4K,48K"); } /* ROMs */ ROM_START( cosmicos ) ROM_REGION( 0x1000, CDP1802_TAG, 0 ) ROM_SYSTEM_BIOS( 0, "hex", "Hex Monitor" ) ROMX_LOAD( "hex.ic6", 0x000, 0x400, BAD_DUMP CRC(d25124bf) SHA1(121215ba3a979e1962327ebe73cbadf784c568d9), ROM_BIOS(0) ) // typed in from manual ROMX_LOAD( "hex.ic7", 0x400, 0x400, BAD_DUMP CRC(364ac81b) SHA1(83936ee6a7ed44632eb290889b98fb9a500f15d4), ROM_BIOS(0) ) // typed in from manual ROM_SYSTEM_BIOS( 1, "ascii", "ASCII Monitor" ) ROMX_LOAD( "ascii.ic6", 0x000, 0x400, NO_DUMP, ROM_BIOS(1) ) ROMX_LOAD( "ascii.ic7", 0x400, 0x400, NO_DUMP, ROM_BIOS(1) ) ROM_END /* System Drivers */ // YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS COMP( 1979, cosmicos, 0, 0, cosmicos, cosmicos, cosmicos_state, empty_init, "Radio Bulletin", "Cosmicos", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_GRAPHICS )