diff options
Diffstat (limited to 'src/devices/bus/nabupc/adapter.cpp')
-rw-r--r-- | src/devices/bus/nabupc/adapter.cpp | 423 |
1 files changed, 423 insertions, 0 deletions
diff --git a/src/devices/bus/nabupc/adapter.cpp b/src/devices/bus/nabupc/adapter.cpp new file mode 100644 index 00000000000..0c6ecef575f --- /dev/null +++ b/src/devices/bus/nabupc/adapter.cpp @@ -0,0 +1,423 @@ +// license:BSD-3-Clause +// copyright-holders:Brian Johnson +/******************************************************************* + * + * NABU PC - Network Adapter Settop Box + * + *******************************************************************/ + +#include "emu.h" +#include "adapter.h" + +#include "emuopts.h" +#include "unzip.h" + +#include <tuple> + +#define VERBOSE 0 +#include "logmacro.h" + + +//************************************************************************** +// NABU PC NETWORK ADAPTER DEVICE +//************************************************************************** + +DEFINE_DEVICE_TYPE(NABUPC_NETWORK_ADAPTER, bus::nabupc::network_adapter, "nabu_net_adapter", "NABU Network Adapter") + +namespace bus::nabupc { + +//************************************************************************** +// SEGMENT FILE LOADING +//************************************************************************** + +// Load segment file from disk +std::error_condition network_adapter::segment_file::read_archive(util::core_file &stream, uint32_t segment_id) +{ + segment_id &= 0xffffff; + + util::core_file::ptr proxy; + std::error_condition err = util::core_file::open_proxy(stream, proxy); + if (err) + return err; + + util::archive_file::ptr zipfile; + err = util::archive_file::open_zip(std::move(proxy), zipfile); + if (err) + return err; + + std::string segment_filename = util::string_format("%06d.nabu", segment_id); + + if (zipfile->search(segment_filename, false) < 0) + return std::errc::no_such_file_or_directory; + + // determine the uncompressed length + uint64_t uncompressed_length_uint64 = zipfile->current_uncompressed_length(); + size_t uncompressed_length = (size_t)uncompressed_length_uint64; + if (uncompressed_length != uncompressed_length_uint64) + return std::errc::not_enough_memory; + + // prepare a buffer for the segment file + std::unique_ptr<char[]> segment_buffer(new (std::nothrow) char[uncompressed_length]); + if (!segment_buffer) + return std::errc::not_enough_memory; + + err = zipfile->decompress(&segment_buffer[0], uncompressed_length); + if (err) + return err; + + m_segment_id = segment_id; + return parse_segment(&segment_buffer[0], uncompressed_length); +} + +std::error_condition network_adapter::segment_file::parse_segment(char *data, size_t length) +{ + util::core_file::ptr fd; + pak current; + uint64_t offset = 0; + size_t actual; + uint16_t crc; + uint8_t npak = 0; + std::error_condition err; + + pak_list.clear(); + err = util::core_file::open_ram(data, length, OPEN_FLAG_READ, fd); + if (err) + return err; + + memset(current.data, 0, 991); + current.segment_id[0] = (m_segment_id & 0xff0000) >> 16; + current.segment_id[1] = (m_segment_id & 0x00ff00) >> 8; + current.segment_id[2] = (m_segment_id & 0x0000ff); + current.owner = 0x01; + current.tier[0] = 0x7f; + current.tier[1] = 0xff; + current.tier[2] = 0xff; + current.tier[3] = 0xff; + current.mbytes[0] = 0x7f; + current.mbytes[1] = 0x80; + std::tie(err, actual) = read_at(*fd, offset, current.data, 991); + do { + crc = 0xffff; + if (err) { + return err; + } + if (actual > 0) { + current.packet_number = npak; + current.pak_number[0] = npak; + current.pak_number[1] = 0; + current.type = 0x20; + if (offset == 0) + current.type |= 0x81; + if (actual < 991) + current.type |= 0x10; + current.offset[0] = ((offset) >> 8) & 0xff; + current.offset[1] = offset & 0xff; + for (int i = 0; i < 1007; ++i) { + crc = update_crc(crc, ((char *)¤t)[i]); + } + crc ^= 0xffff; + current.crc[0] = (crc >> 8) & 0xff; + current.crc[1] = crc & 0xff; + pak_list.push_back(current); + offset = (++npak * 991); + memset(current.data, 0, 991); + std::tie(err, actual) = read_at(*fd, offset, current.data, 991); + } + } while(actual > 0); + + return err; +} + +const network_adapter::segment_file::pak& network_adapter::segment_file::operator[](const int index) const +{ + assert(index >= 0 && index < size()); + + return pak_list[index]; +} + +// crc16 calculation +uint16_t network_adapter::segment_file::update_crc(uint16_t crc, uint8_t data) +{ + uint8_t bc; + + bc = (crc >> 8) ^ data; + + crc <<= 8; + crc ^= crc_table[bc]; + + return crc; +} + +//************************************************************************** +// INPUT PORTS +//************************************************************************** + +// CONFIG +static INPUT_PORTS_START( nabu_network_adapter ) + PORT_START("CONFIG") + PORT_CONFNAME(0x01, 0x00, "Prompt for channel?") + PORT_CONFSETTING(0x01, "Yes") + PORT_CONFSETTING(0x00, "No") +INPUT_PORTS_END + +//************************************************************************** +// DEVICE INITIALIZATION +//************************************************************************** + +network_adapter::network_adapter(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, NABUPC_NETWORK_ADAPTER, tag, owner, clock) + , device_buffered_serial_interface(mconfig, *this) + , device_rs232_port_interface(mconfig, *this) + , device_image_interface(mconfig, *this) + , m_config(*this, "CONFIG") + , m_channel(0) + , m_packet(0) + , m_segment(0) + , m_state(state::START) + , m_substate(0) + , m_segment_timer(nullptr) +{ +} + +void network_adapter::device_start() +{ + m_segment_timer = timer_alloc(FUNC(network_adapter::segment_tick), this); + + machine().save().register_postload(save_prepost_delegate(FUNC(network_adapter::postload), this)); + + save_item(NAME(m_state)); + save_item(NAME(m_substate)); + save_item(NAME(m_channel)); + save_item(NAME(m_packet)); + save_item(NAME(m_segment)); +} + +void network_adapter::device_reset() +{ + m_state = state::START; + m_substate = 0; + // initialise state + clear_fifo(); + + // configure device_buffered_serial_interface + set_data_frame(START_BIT_COUNT, DATA_BIT_COUNT, PARITY, STOP_BITS); + set_rate(BAUD); + receive_register_reset(); + transmit_register_reset(); +} + +void network_adapter::postload() +{ + if (is_loaded()) { + m_cache.read_archive(image_core_file(), m_segment); + } +} + +std::pair<std::error_condition, std::string> network_adapter::call_load() +{ + if (is_filetype("pak")) { + return std::make_pair(std::error_condition(), std::string()); + } + return std::make_pair(image_error::INVALIDIMAGE, std::string()); +} + +//************************************************************************** +// DEVICE CONFIGURATION +//************************************************************************** + +ioport_constructor network_adapter::device_input_ports() const +{ + return INPUT_PORTS_NAME( nabu_network_adapter ); +} + +//************************************************************************** +// SERIAL PROTOCOL +//************************************************************************** + +void network_adapter::input_txd(int state) +{ + device_buffered_serial_interface::rx_w(state); +} + +void network_adapter::tra_callback() +{ + output_rxd(transmit_register_get_data_bit()); +} + +void network_adapter::received_byte(uint8_t byte) +{ + LOG("Received Byte 0x%02X\n", byte); + switch (m_state) { + case state::START: + connect(byte, bool(m_config->read() & 1)); + break; + case state::IDLE: + idle(byte); + break; + case state::CHANNEL_REQUEST: + channel_request(byte); + break; + case state::SEGMENT_REQUEST: + segment_request(byte); + break; + case state::HEX81_REQUEST: + hex81_request(byte); + break; + case state::SEND_SEGMENT: + send_segment(byte); + break; + } +} + +//************************************************************************** +// STATE MACHINE +//************************************************************************** + +void network_adapter::connect(uint8_t byte, bool channel_request = true) +{ + if (byte == 0x83 && m_substate == 0) { + transmit_byte(0x10); + transmit_byte(0x06); + transmit_byte(0xe4); + } else if (byte == 0x82 && m_substate == 1) { + transmit_byte(0x10); + transmit_byte(0x06); + } else if (byte == 0x01 && m_substate == 2) { + transmit_byte(channel_request ? 0x9f : 0x1f); + transmit_byte(0x10); + transmit_byte(0xe1); + m_state = state::IDLE; + } else { + LOG("Unexpected byte: 0x%02X (%d), restarting Adapter.\n", byte, m_substate); + m_state = state::START; + m_substate = 0; + return; + } + ++m_substate; +} + +void network_adapter::idle(uint8_t byte) +{ + m_substate = 0; + switch (byte) { + case 0x85: + transmit_byte(0x10); + transmit_byte(0x06); + m_state = state::CHANNEL_REQUEST; + break; + case 0x84: + transmit_byte(0x10); + transmit_byte(0x06); + m_state = state::SEGMENT_REQUEST; + break; + case 0x83: + transmit_byte(0x10); + transmit_byte(0x06); + break; + case 0x82: + transmit_byte(0x10); + transmit_byte(0x06); + break; + case 0x81: + transmit_byte(0x10); + transmit_byte(0x06); + m_state = state::HEX81_REQUEST; + break; + case 0x01: + transmit_byte(0x10); + transmit_byte(0x06); + break; + } + +} + +void network_adapter::hex81_request(uint8_t byte) +{ + if (m_substate == 1) { + transmit_byte(0xe4); + m_state = state::IDLE; + } + ++m_substate; + +} + +void network_adapter::channel_request(uint8_t byte) +{ + if (m_substate == 0) { + m_channel = (m_channel & 0xff00) | (byte); + } else if (m_substate == 1) { + m_channel = (m_channel & 0xff) | (byte << 8); + transmit_byte(0xe4); + LOG("Channel: 0x%04X\n", m_channel); + m_state = state::IDLE; + } + ++m_substate; +} + +void network_adapter::segment_request(uint8_t byte) +{ + if (m_substate == 0) { + m_packet = byte; + } else if (m_substate == 1) { + m_segment = (m_segment & 0xffff00) | (byte); + } else if (m_substate == 2) { + m_segment = (m_segment & 0xff00ff) | (byte << 8); + } else if (m_substate == 3) { + m_segment = (m_segment & 0xffff) | (byte << 16); + transmit_byte(0xe4); + transmit_byte(0x91); + m_state = state::SEND_SEGMENT; + m_substate = 0; + LOG("Segment: 0x%06X, Packet: 0x%02X\n", m_segment, m_packet); + return; + } + ++m_substate; +} + +void network_adapter::send_segment(uint8_t byte) +{ + if (m_substate == 0) { + if (byte != 0x10) { + LOG("Expecting byte 0x10 got %02X, restarting.\n", byte); + m_state = state::START; + m_substate = 0; + return; + } + } else if (m_substate == 1) { + if (byte != 0x06) { + LOG("Expecting byte 0x06 got %02X, restarting.\n", byte); + m_state = state::START; + m_substate = 0; + return; + } + m_pak_offset = 0; + if (is_loaded() && !m_cache.read_archive(image_core_file(), m_segment)) { + m_segment_timer->adjust(attotime::zero, 0, attotime::from_hz(1'000)); + LOG("Segment sending, returning to idle state\n"); + } else { + LOG("Failed to find segment: %06d, restarting\n", m_segment); + transmit_byte(0x10); + transmit_byte(0x06); + transmit_byte(0xe4); + } + m_state = state::IDLE; + } + + ++m_substate; +} + +TIMER_CALLBACK_MEMBER(network_adapter::segment_tick) +{ + char * data = (char*)&m_cache[m_packet]; + if (data[m_pak_offset] == 0x10) { + transmit_byte(data[m_pak_offset]); + } + transmit_byte(data[m_pak_offset++]); + if (m_pak_offset >= sizeof(segment_file::pak)) { + transmit_byte(0x10); + transmit_byte(0xe1); + m_segment_timer->reset(); + } +} + +} // bus::nabupc |