// license:BSD-3-Clause // copyright-holders:Robbbert /* Super80.cpp written by Robbbert, 2005-2009. See driver source for documentation. */ #include "emu.h" #include "includes/super80.h" #include "machine/z80bin.h" /**************************** PIO ******************************************************************************/ void super80_state::pio_port_a_w(u8 data) { m_keylatch = data; m_pio->port_b_write(pio_port_b_r()); // refresh kbd int } uint8_t super80_state::pio_port_b_r() { uint8_t data = 0xff; for (int i = 0; i < 8; i++) if (!BIT(m_keylatch, i)) data &= m_io_keyboard[i]->read(); m_key_pressed = 3; return data; } /**************************** CASSETTE ROUTINES *****************************************************************/ void super80_state::cassette_motor( bool motor_state ) { // relay sound if (BIT(m_last_data, 1) != motor_state) m_samples->start(0, motor_state ? 0 : 1); if (motor_state) m_cassette->change_state(CASSETTE_MOTOR_DISABLED,CASSETTE_MASK_MOTOR); else m_cassette->change_state(CASSETTE_MOTOR_ENABLED,CASSETTE_MASK_MOTOR); /* does user want to hear the sound? */ if (BIT(m_io_config->read(), 3)) m_cassette->change_state(CASSETTE_SPEAKER_ENABLED,CASSETTE_MASK_SPEAKER); else m_cassette->change_state(CASSETTE_SPEAKER_MUTED,CASSETTE_MASK_SPEAKER); } /********************************************* TIMER ************************************************/ // If normal keyboard scan has stopped, then do a scan to allow the interrupt key sequence TIMER_DEVICE_CALLBACK_MEMBER( super80_state::timer_k ) { if (m_key_pressed) m_key_pressed--; else if (!m_key_pressed) m_pio->port_b_write(pio_port_b_r()); } /* cassette load circuit This "emulates" U79 CD4046BCN PLL chip and U1 LM311P op-amp. U79 converts a frequency to a voltage, and U1 amplifies that voltage to digital levels. U1 has a trimpot connected, to set the midpoint. The MDS homebrew input circuit consists of 2 op-amps followed by a D-flipflop. The "read-any-system" cassette circuit is a CA3140 op-amp, the smarts being done in software. bit 0 = original system (U79 and U1) bit 1 = MDS fast system bit 2 = CA3140 */ TIMER_DEVICE_CALLBACK_MEMBER( super80_state::kansas_r ) { uint8_t cass_ws=0; m_cass_data[1]++; cass_ws = ((m_cassette)->input() > +0.03) ? 4 : 0; if (cass_ws != m_cass_data[0]) { if (cass_ws) m_cass_data[3] ^= 2; // the MDS flipflop m_cass_data[0] = cass_ws; m_cass_data[2] = ((m_cass_data[1] < 0x0d) ? 1 : 0) | cass_ws | m_cass_data[3]; m_cass_data[1] = 0; } } TIMER_DEVICE_CALLBACK_MEMBER( super80_state::timer_h ) { uint8_t go_fast = 0; if ( (!BIT(m_portf0, 2)) | (!BIT(m_io_config->read(), 1)) ) // bit 2 of port F0 is low, OR user turned on config switch go_fast++; // must be 1 at boot so banking works correctly /* code to slow down computer to 1 MHz by halting cpu on every second frame */ if (!go_fast) { if (!m_int_sw) m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE); // if going, stop it m_int_sw++; if (m_int_sw > 1) { m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE); // if stopped, start it m_int_sw = 0; } } else { if (m_int_sw < 8) // @2MHz, reset just once { m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE); m_int_sw = 8; // ...not every time } } } /*************************************** PRINTER ********************************************************/ /* The Super80 had an optional I/O card that plugged into the S-100 slot. The card had facility for running an 8-bit Centronics printer, and a serial device at 300, 600, or 1200 baud. The I/O address range was selectable via 4 dipswitches. The serial parameters (baud rate, parity, stop bits, etc) was chosen with more dipswitches. Regretably, no parameters could be set by software. Currently, the Centronics printer is emulated; the serial side of things may be done later, as will the dipswitches. The most commonly used I/O range is DC-DE (DC = centronics, DD = serial data, DE = serial control All the home-brew roms use this, except for super80e which uses BC-BE. */ /**************************** I/O PORTS *****************************************************************/ u8 super80v_state::port3e_r() { return 0xF8 | (m_fdc->intrq_r() << 0) | (m_fdc->drq_r() << 1) | 4; } // UFDC board can support 4 drives; we support 2 void super80v_state::port3f_w(u8 data) { // m_fdc->58(BIT(data, 0)); 5/8 pin not emulated in wd_fdc m_fdc->enmf_w(BIT(data,1)); floppy_image_device *floppy = nullptr; if (BIT(data, 2)) floppy = m_floppy0->get_device(); if (BIT(data, 3)) floppy = m_floppy1->get_device(); if (BIT(data, 4)) floppy = m_floppy2->get_device(); if (BIT(data, 5)) floppy = m_floppy3->get_device(); m_fdc->set_floppy(floppy); if (floppy) { floppy->mon_w(0); floppy->ss_w(BIT(data, 6)); } m_fdc->dden_w(BIT(data, 7)); } u8 super80_state::portf2_r() { u8 data = m_io_dsw->read() & 0xf0; // dip switches on pcb data |= m_cass_data[2]; // bit 0 = output of U1, bit 1 = MDS cass state, bit 2 = current wave_state data |= 0x08; // bit 3 - not used return data; } void super80_state::portdc_w(u8 data) { // hardware strobe driven from port select, bit 7..0 = data m_cent_data_out->write(data); m_centronics->write_strobe(0); m_centronics->write_strobe(1); } void super80_state::portf0_w(u8 data) { u8 bits = data ^ m_last_data; m_portf0 = data; m_speaker->level_w(BIT(data, 3)); // bit 3 - speaker if (BIT(bits, 1)) cassette_motor(BIT(data, 1)); // bit 1 - cassette motor m_cassette->output( BIT(data, 0) ? -1.0 : +1.0); // bit 0 - cass out m_last_data = data; } /**************************** BASIC MACHINE CONSTRUCTION ***********************************************************/ void super80_state::machine_start_common() { m_cass_led.resolve(); // register for savestates save_item(NAME(m_portf0)); save_item(NAME(m_s_options)); save_item(NAME(m_palette_index)); save_item(NAME(m_keylatch)); save_item(NAME(m_cass_data)); save_item(NAME(m_key_pressed)); save_item(NAME(m_boot_in_progress)); save_item(NAME(m_last_data)); } void super80_state::machine_start() { machine_start_common(); save_item(NAME(m_int_sw)); save_item(NAME(m_vidpg)); save_item(NAME(m_current_charset)); } void super80v_state::machine_start() { // zerofill m_vram = make_unique_clear(0x3000); save_pointer(NAME(m_vram), 0x3000); machine_start_common(); } void super80_state::machine_reset_common() { m_boot_in_progress = true; m_portf0 = 0; // must be 0 like real machine, or banking breaks on 32-col systems m_keylatch = 0xff; m_key_pressed = 0; m_palette_index = 0; address_space &program = m_maincpu->space(AS_PROGRAM); program.install_rom(0x0000, 0x0fff, m_rom); // do it here for F3 m_rom_shadow_tap = program.install_read_tap(0xc000, 0xcfff, "rom_shadow_r",[this](offs_t offset, u8 &data, u8 mem_mask) { if (!machine().side_effects_disabled()) { // delete this tap m_rom_shadow_tap->remove(); // reinstall ram over the rom shadow m_maincpu->space(AS_PROGRAM).install_ram(0x0000, 0x0fff, m_ram); } // return the original data return data; }); } void super80_state::machine_reset() { machine_reset_common(); m_vidpg = 0xfe00; } void super80v_state::machine_reset() { machine_reset_common(); } /*------------------------------------------------- QUICKLOAD_LOAD_MEMBER( super80_state, super80 ) -------------------------------------------------*/ QUICKLOAD_LOAD_MEMBER(super80_state::quickload_cb) { uint16_t exec_addr, start_addr, end_addr; // load the binary into memory if (z80bin_load_file(&image, m_maincpu->space(AS_PROGRAM), file_type, &exec_addr, &start_addr, &end_addr) != image_init_result::PASS) return image_init_result::FAIL; // is this file executable? if (exec_addr != 0xffff) // check to see if autorun is on if (BIT(m_io_config->read(), 0)) m_maincpu->set_pc(exec_addr); return image_init_result::PASS; }