diff options
Diffstat (limited to 'src/mame/memotech/mtx_m.cpp')
-rw-r--r-- | src/mame/memotech/mtx_m.cpp | 570 |
1 files changed, 570 insertions, 0 deletions
diff --git a/src/mame/memotech/mtx_m.cpp b/src/mame/memotech/mtx_m.cpp new file mode 100644 index 00000000000..3f5763a096d --- /dev/null +++ b/src/mame/memotech/mtx_m.cpp @@ -0,0 +1,570 @@ +// license:BSD-3-Clause +// copyright-holders:Lee Ward, Dirk Best, Curt Coder +/************************************************************************* + + Memotech MTX 500, MTX 512 and RS 128 + +**************************************************************************/ + +#include "emu.h" +#include "mtx.h" + +#include "multibyte.h" + +/*************************************************************************** + READ/WRITE HANDLERS +***************************************************************************/ + +/*------------------------------------------------- + mtx_strobe_r - centronics strobe +-------------------------------------------------*/ + +uint8_t mtx_state::mtx_strobe_r() +{ + /* set STROBE low */ + m_centronics->write_strobe(false); + + return 0xff; +} + +/*------------------------------------------------- + mtx_subpage_w - rom2 subpages +-------------------------------------------------*/ + +/* + The original ROM card supported 4 8KB ROM chips. These appeared in + ROM slot 2 in subpages 0 to 3. The subpage register starts as 0, but + is changed by attempting to write to 0x0000-0x1fff whilst in RELCPMH=0 + mode (ie. attempting to write to the OS ROM). Videowalls could use a + later ROM card with 4 32KB ROMs. These also appeared in ROM slot 2 + in subpages 0 to 15. +*/ + +void mtx_state::mtx_subpage_w(uint8_t data) +{ + if (m_extrom->exists()) + { + if ((data * 0x2000) < m_extrom->get_rom_size()) + m_rommap_bank1->configure_entry(2, m_extrom->get_rom_base() + 0x2000 * data); + else + m_rommap_bank1->configure_entry(2, memregion("user2")->base() + 0x4000); + m_rommap_bank1->set_entry(2); + } +} + +/*------------------------------------------------- + mtx_bankswitch_w - bankswitch +-------------------------------------------------*/ + +/* + There are two memory models on the MTX, the standard one and a + CBM mode. In standard mode, the memory map is defined as: + + 0x0000 - 0x1fff OSROM + 0x2000 - 0x3fff Paged ROM + 0x4000 - 0x7fff Paged RAM + 0x8000 - 0xbfff Paged RAM + 0xc000 - 0xffff RAM + + Banks are selected by output port 0. Bits 0-3 define the RAM page + and bits 4-6 the ROM page. + + CBM mode is selected by bit 7 of output port 0. ROM is replaced + by RAM in this mode. +*/ + +void mtx_state::bankswitch(uint8_t data) +{ + /* + + bit description + + 0 P0 + 1 P1 + 2 P2 + 3 P3 + 4 R0 + 5 R1 + 6 R2 + 7 RELCPMH + + */ + address_space &program = m_maincpu->space(AS_PROGRAM); + + uint8_t cbm_mode = (data >> 7) & 0x01; + uint8_t rom_page = (data >> 4) & 0x07; + uint8_t ram_page = (data >> 0) & 0x0f; + + if (cbm_mode) + { + /* ram based memory map */ + program.install_readwrite_bank(0x0000, 0x3fff, m_rammap_bank1); + program.install_readwrite_bank(0x4000, 0x7fff, m_rammap_bank2); + program.install_readwrite_bank(0x8000, 0xbfff, m_rammap_bank3); + + /* set ram bank, for invalid pages a nop-handler will be installed */ + if ((ram_page == 0 && m_ram->size() > 0xc000) || (ram_page > 0 && m_ram->size() > 0x4000 + ram_page * 0xc000)) + m_rammap_bank1->set_entry(ram_page); + else + program.nop_readwrite(0x0000, 0x3fff); + + if ((ram_page == 0 && m_ram->size() > 0x8000) || (ram_page > 0 && m_ram->size() > 0x8000 + ram_page * 0xc000)) + m_rammap_bank2->set_entry(ram_page); + else + program.nop_readwrite(0x4000, 0x7fff); + + if ((ram_page == 0 && m_ram->size() > 0x4000) || (ram_page > 0 && m_ram->size() > 0xc000 + ram_page * 0xc000)) + m_rammap_bank3->set_entry(ram_page); + else + program.nop_readwrite(0x8000, 0xbfff); + } + else + { + /* rom based memory map */ + program.install_rom(0x0000, 0x1fff, memregion("user1")->base()); + program.install_write_handler(0x0000, 0x1fff, write8smo_delegate(*this, FUNC(mtx_state::mtx_subpage_w))); + program.install_read_bank(0x2000, 0x3fff, m_rommap_bank1); + program.unmap_write(0x2000, 0x3fff); + program.install_readwrite_bank(0x4000, 0x7fff, m_rommap_bank2); + program.install_readwrite_bank(0x8000, 0xbfff, m_rommap_bank3); + + /* set rom bank (switches between basic and assembler rom or cartridges) */ + m_rommap_bank1->set_entry(rom_page); + + /* set ram bank, for invalid pages a nop-handler will be installed */ + if (m_ram->size() > 0x8000 + ram_page * 0x8000) + m_rommap_bank2->set_entry(ram_page); + else + program.nop_readwrite(0x4000, 0x7fff); + + if (m_ram->size() > 0x4000 + ram_page * 0x8000) + m_rommap_bank3->set_entry(ram_page); + else + program.nop_readwrite(0x8000, 0xbfff); + } +} + +void mtx_state::mtx_bankswitch_w(uint8_t data) +{ + bankswitch(data); + + m_exp_int->bankswitch(data); + m_exp_ext->bankswitch(data); +} + +/*------------------------------------------------- + mtx_sound_strobe_r - sound strobe +-------------------------------------------------*/ + +uint8_t mtx_state::mtx_sound_strobe_r() +{ + m_sn->write(m_sound_latch); + return 0xff; +} + +/*------------------------------------------------- + mtx_sound_latch_w - sound latch write +-------------------------------------------------*/ + +void mtx_state::mtx_sound_latch_w(uint8_t data) +{ + m_sound_latch = data; +} + +/*------------------------------------------------- + mtx_cst_w - cassette write +-------------------------------------------------*/ + +void mtx_state::mtx_cst_w(uint8_t data) +{ + m_cassette->output( BIT(data, 0) ? -1 : 1); +} + +/*------------------------------------------------- + mtx_cst_motor_w - cassette motor +-------------------------------------------------*/ + +void mtx_state::mtx_cst_motor_w(uint8_t data) +{ + /* supported in the MTX ROM */ + switch (data) + { + case 0xaa: + m_cassette->change_state(CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR); + break; + case 0x55: + m_cassette->change_state(CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR); + break; + } +} + +/*------------------------------------------------- + mtx_prt_r - centronics status +-------------------------------------------------*/ + +void mtx_state::write_centronics_busy(int state) +{ + m_centronics_busy = state; +} + +void mtx_state::write_centronics_fault(int state) +{ + m_centronics_fault = state; +} + +void mtx_state::write_centronics_perror(int state) +{ + m_centronics_perror = state; +} + +void mtx_state::write_centronics_select(int state) +{ + m_centronics_select = state; +} + +uint8_t mtx_state::mtx_prt_r() +{ + /* + + bit description + + 0 BUSY + 1 ERROR + 2 PE + 3 SLCT + 4 + 5 + 6 + 7 + + */ + + uint8_t data = 0; + + /* reset STROBE to high */ + m_centronics->write_strobe( true); + + data |= m_centronics_busy << 0; + data |= m_centronics_fault << 1; + data |= m_centronics_perror << 2; + data |= m_centronics_select << 3; + + return data; +} + +/*------------------------------------------------- + mtx_sense_w - keyboard sense write +-------------------------------------------------*/ + +void mtx_state::mtx_sense_w(uint8_t data) +{ + m_key_sense = data; +} + +/*------------------------------------------------- + mtx_key_lo_r - keyboard low read +-------------------------------------------------*/ + +uint8_t mtx_state::mtx_key_lo_r() +{ + uint16_t data = 0xffff; + + for (int row = 0; row < 8; row++) + { + if (!(m_key_sense & (1 << row))) + { + data &= m_keyboard[row]->read(); + if ((row == 7 && BIT(m_joysticks->read(), 0)) || (row != 7 && BIT(m_joysticks->read(), 1))) + data &= m_joystick[row]->read(); + } + } + + return data & 0xff; +} + +/*------------------------------------------------- + mtx_key_lo_r - keyboard high read +-------------------------------------------------*/ + +uint8_t mtx_state::mtx_key_hi_r() +{ + uint16_t data = 0xffff; + + for (int row = 0; row < 8; row++) + { + if (!(m_key_sense & (1 << row))) + { + data &= m_keyboard[row]->read(); + if ((row == 7 && BIT(m_joysticks->read(), 0)) || (row != 7 && BIT(m_joysticks->read(), 1))) + data &= m_joystick[row]->read(); + } + } + + return (m_country->read() | data) >> 8; +} + +/*------------------------------------------------- + hrx_address_w - HRX video RAM address +-------------------------------------------------*/ + +void mtx_state::hrx_address_w(offs_t offset, uint8_t data) +{ + if (offset) + { + /* + + bit description + + 0 A8 + 1 A9 + 2 A10 + 3 + 4 + 5 attribute memory write enable + 6 ASCII memory write enable + 7 cycle (0=read/1=write) + + */ + } + else + { + /* + + bit description + + 0 A0 + 1 A1 + 2 A2 + 3 A3 + 4 A4 + 5 A5 + 6 A6 + 7 A7 + + */ + } +} + +/*------------------------------------------------- + hrx_data_r - HRX data read +-------------------------------------------------*/ + +uint8_t mtx_state::hrx_data_r() +{ + return 0; +} + +/*------------------------------------------------- + hrx_data_w - HRX data write +-------------------------------------------------*/ + +void mtx_state::hrx_data_w(uint8_t data) +{ +} + +/*------------------------------------------------- + hrx_attr_r - HRX attribute read +-------------------------------------------------*/ + +uint8_t mtx_state::hrx_attr_r() +{ + return 0; +} + +/*------------------------------------------------- + hrx_attr_r - HRX attribute write +-------------------------------------------------*/ + +void mtx_state::hrx_attr_w(uint8_t data) +{ + /* + + bit description + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + */ +} + +/*************************************************************************** + EXTENSION BOARD ROMS +***************************************************************************/ + +DEVICE_IMAGE_LOAD_MEMBER( mtx_state::extrom_load ) +{ + uint32_t size = m_extrom->common_get_size("rom"); + + if (size > 0x80000) + return std::make_pair(image_error::INVALIDLENGTH, "Unsupported ROM size (only 512K supported)"); + + m_extrom->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE); + m_extrom->common_load_rom(m_extrom->get_rom_base(), size, "rom"); + + return std::make_pair(std::error_condition(), std::string()); +} + +/*************************************************************************** + SNAPSHOT +***************************************************************************/ + +// this only works for some of the files, nothing which tries to load +// more data from tape. TODO: tapes which autorun after loading +SNAPSHOT_LOAD_MEMBER(mtx_state::snapshot_cb) +{ + uint64_t length = image.length(); + + if (length < 18) + return std::make_pair(image_error::INVALIDLENGTH, "File too short (must be at least 18 bytes)"); + + if (length >= 0x10000 - 0x4000 + 18) + return std::make_pair(image_error::INVALIDLENGTH, "File too long (must be at no more than 48K data)"); + + auto data = std::make_unique<uint8_t []>(length); + if (image.fread(data.get(), length) != length) + return std::make_pair(image_error::UNSPECIFIED, "Error reading file"); + + // verify first byte + if (data[0] != 0xff) + return std::make_pair(image_error::INVALIDIMAGE, std::string()); + + // get tape name + char tape_name[16]; + memcpy(&tape_name, &data[1], 15); + tape_name[15] = '\0'; + image.message("Loading '%s'", tape_name); + + address_space &program = m_maincpu->space(AS_PROGRAM); + + // reset memory map + bankswitch(0); + + // start of system variables area + uint16_t system_variables_base = get_u16le(&data[16]); + + // write system variables + uint16_t system_variables_size = 0; + + if (system_variables_base != 0) + { + system_variables_size = 0xfb4b - system_variables_base; + for (int i = 0; i < system_variables_size; i++) + program.write_byte(system_variables_base + i, data[18 + i]); + } + + // write actual image data + uint16_t data_size = image.length() - 18 - system_variables_size; + for (int i = 0; i < data_size; i++) + program.write_byte(0x4000 + i, data[18 + system_variables_size + i]); + + logerror("snapshot name = '%s', system_size = 0x%04x, data_size = 0x%04x\n", tape_name, system_variables_size, data_size); + + return std::make_pair(std::error_condition(), std::string()); +} + +/*************************************************************************** + QUICKLOAD +***************************************************************************/ + +QUICKLOAD_LOAD_MEMBER(mtx_state::quickload_cb) +{ + uint64_t const length = image.length(); + + if (length < 4) + return std::make_pair(image_error::INVALIDLENGTH, "File too short (must be at least 4 bytes)"); + else if (length >= 0x10000 - 0x4000 + 4) + return std::make_pair(image_error::INVALIDLENGTH, "File too long (must be at no more than 48K data)"); + + auto data = std::make_unique<uint8_t []>(length); + if (image.fread(data.get(), length) != length) + return std::make_pair(image_error::UNSPECIFIED, "Error reading file"); + + uint16_t const code_base = get_u16le(&data[0]); + uint16_t const code_length = get_u16le(&data[2]); + + if (length < (code_length + 4)) + return std::make_pair(image_error::INVALIDIMAGE, "File too short for code length in header"); + + if (code_base < 0x4000) + { + return std::make_pair( + image_error::INVALIDIMAGE, + util::string_format("Invalid code base 0x%04X (must be no less than 0x4000)", code_base)); + } + else if ((code_base + code_length) > 0x10000) + { + return std::make_pair( + image_error::INVALIDIMAGE, + util::string_format("Loading 0x%04X bytes of code at base 0x%04X would overflow RAM", code_length, code_base)); + } + + // reset memory map + bankswitch(0); + + // write image data + address_space &program = m_maincpu->space(AS_PROGRAM); + for (int i = 0; i < code_length; i++) + program.write_byte(code_base + i, data[4 + i]); + + m_maincpu->set_pc(code_base); + + return std::make_pair(std::error_condition(), std::string()); +} + +/*************************************************************************** + MACHINE INITIALIZATION +***************************************************************************/ + +/*------------------------------------------------- + MACHINE_START( mtx512 ) +-------------------------------------------------*/ + +void mtx_state::machine_start() +{ + address_space &program = m_maincpu->space(AS_PROGRAM); + + /* setup banks for rom based memory map */ + program.install_read_bank(0x2000, 0x3fff, m_rommap_bank1); + program.install_readwrite_bank(0x4000, 0x7fff, m_rommap_bank2); + program.install_readwrite_bank(0x8000, 0xbfff, m_rommap_bank3); + + m_rommap_bank1->configure_entries(0, 8, memregion("user2")->base(), 0x2000); + m_rommap_bank2->configure_entry(0, m_ram->pointer() + 0x8000); + m_rommap_bank2->configure_entries(1, 15, m_ram->pointer() + 0x10000, 0x8000); + m_rommap_bank3->configure_entry(0, m_ram->pointer() + 0x4000); + m_rommap_bank3->configure_entries(1, 15, m_ram->pointer() + 0xc000, 0x8000); + + /* setup banks for ram based memory map */ + program.install_readwrite_bank(0x0000, 0x3fff, m_rammap_bank1); + program.install_readwrite_bank(0x4000, 0x7fff, m_rammap_bank2); + program.install_readwrite_bank(0x8000, 0xbfff, m_rammap_bank3); + + m_rammap_bank1->configure_entry(0, m_ram->pointer() + 0xc000); + m_rammap_bank1->configure_entries(1, 15, m_ram->pointer() + 0x10000, 0xc000); + m_rammap_bank2->configure_entry(0, m_ram->pointer() + 0x8000); + m_rammap_bank2->configure_entries(1, 15, m_ram->pointer() + 0x14000, 0xc000); + m_rammap_bank3->configure_entry(0, m_ram->pointer() + 0x4000); + m_rammap_bank3->configure_entries(1, 15, m_ram->pointer() + 0x18000, 0xc000); + + /* install 4000h bytes common block */ + program.install_ram(0xc000, 0xffff, m_ram->pointer()); +} + +void mtx_state::machine_reset() +{ + /* extension board ROMs */ + if (m_extrom->exists()) + m_rommap_bank1->configure_entry(2, m_extrom->get_rom_base()); + /* keyboard ROMs */ + if (ioport("keyboard_rom")->read()) + m_rommap_bank1->configure_entry(7, memregion("keyboard_rom")->base() + (ioport("keyboard_rom")->read() - 1) * 0x2000); + + /* bank switching */ + bankswitch(0); +} |