diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua index 6b95615a32a..4cbf0266db2 100644 --- a/scripts/src/bus.lua +++ b/scripts/src/bus.lua @@ -2416,6 +2416,8 @@ if (BUSES["A2BUS"]~=null) then MAME_DIR .. "src/devices/bus/a2bus/agat7ram.h", MAME_DIR .. "src/devices/bus/a2bus/agat840k_hle.cpp", MAME_DIR .. "src/devices/bus/a2bus/agat840k_hle.h", + MAME_DIR .. "src/devices/bus/a2bus/booti.cpp", + MAME_DIR .. "src/devices/bus/a2bus/booti.h", MAME_DIR .. "src/devices/bus/a2bus/byte8251.cpp", MAME_DIR .. "src/devices/bus/a2bus/byte8251.h", MAME_DIR .. "src/devices/bus/a2bus/ccs7710.cpp", diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index 25d518e5065..8be9fe1d911 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -836,6 +836,18 @@ if (MACHINES["AT28C16"]~=null) then } end +--------------------------------------------------- +-- +--@src/devices/machine/at28c64b.h,MACHINES["AT28C64B"] = true +--------------------------------------------------- + +if (MACHINES["AT28C64B"]~=null) then + files { + MAME_DIR .. "src/devices/machine/at28c64b.cpp", + MAME_DIR .. "src/devices/machine/at28c64b.h", + } +end + --------------------------------------------------- -- --@src/devices/machine/at29x.h,MACHINES["AT29X"] = true @@ -981,6 +993,17 @@ if (MACHINES["CDP1879"]~=null) then } end +--------------------------------------------------- +-- +--@src/devices/machine/ch376.h,MACHINES["CH376"] = true +--------------------------------------------------- + +if (MACHINES["CH376"]~=null) then + files { + MAME_DIR .. "src/devices/machine/ch376.cpp", + MAME_DIR .. "src/devices/machine/ch376.h", + } +end --------------------------------------------------- -- diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index b40f73b2c46..f01652ddbd2 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -478,6 +478,7 @@ MACHINES["AMIGAFDC"] = true MACHINES["ARM_IOMD"] = true MACHINES["AT_KEYBC"] = true MACHINES["AT28C16"] = true +MACHINES["AT28C64B"] = true MACHINES["AT29X"] = true MACHINES["AT45DBXX"] = true MACHINES["ATAFLASH"] = true @@ -490,6 +491,7 @@ MACHINES["CDP1852"] = true MACHINES["CDP1871"] = true MACHINES["CDP1879"] = true --MACHINES["CDU76S"] = true +MACHINES["CH376"] = true MACHINES["CHESSMACHINE"] = true MACHINES["CMOS40105"] = true MACHINES["COM52C50"] = true diff --git a/src/devices/bus/a2bus/booti.cpp b/src/devices/bus/a2bus/booti.cpp new file mode 100644 index 00000000000..e365ebe2492 --- /dev/null +++ b/src/devices/bus/a2bus/booti.cpp @@ -0,0 +1,171 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +/********************************************************************* + + booti.cpp + + Implementation of the BOOTI card + + The BOOTI is an Apple II interface to the CH376 USB module. + The CH376 is intended for use with small microcontrollers (or, + you know, the 6502) to give them access to FAT-formatted + flash drives. See ch376.cpp for details. + + C0n0: read/write data to CH376 + C0n1: read status/write command to CH376 + C0n4: $C800 ROM bank (0-3) + +*********************************************************************/ + +#include "emu.h" +#include "booti.h" + +/*************************************************************************** + PARAMETERS +***************************************************************************/ + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +DEFINE_DEVICE_TYPE(A2BUS_BOOTI, a2bus_booti_device, "a2booti", "Booti Card") + +ROM_START( booti ) + ROM_REGION(0x2000, "flash", 0) + ROM_LOAD( "bootifw09l.bin", 0x000000, 0x002000, CRC(be3f21ff) SHA1(f505ad4685cd44e4cce5b8d6d27b9c4fea159f11) ) +ROM_END + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +//------------------------------------------------- +// device_add_mconfig - add device configuration +//------------------------------------------------- + +void a2bus_booti_device::device_add_mconfig(machine_config &config) +{ + AT28C64B(config, "flash", 0); + + CH376(config, "ch376"); +} + +//------------------------------------------------- +// rom_region - device-specific ROM region +//------------------------------------------------- + +const tiny_rom_entry *a2bus_booti_device::device_rom_region() const +{ + return ROM_NAME( booti ); +} + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +a2bus_booti_device::a2bus_booti_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, type, tag, owner, clock), + device_a2bus_card_interface(mconfig, *this), + m_flash(*this, "flash"), + m_ch376(*this, "ch376"), + m_rombank(0) +{ +} + +a2bus_booti_device::a2bus_booti_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + a2bus_booti_device(mconfig, A2BUS_BOOTI, tag, owner, clock) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void a2bus_booti_device::device_start() +{ + save_item(NAME(m_rombank)); +} + +void a2bus_booti_device::device_reset() +{ + m_rombank = 0; +} + + +/*------------------------------------------------- + read_c0nx - called for reads from this card's c0nx space +-------------------------------------------------*/ + +uint8_t a2bus_booti_device::read_c0nx(uint8_t offset) +{ + switch (offset) + { + case 0: + case 1: + return m_ch376->read(offset); + + case 4: + return m_rombank / 0x800; + } + return 0xff; +} + + +/*------------------------------------------------- + write_c0nx - called for writes to this card's c0nx space +-------------------------------------------------*/ + +void a2bus_booti_device::write_c0nx(uint8_t offset, uint8_t data) +{ + switch (offset) + { + case 0: + case 1: + m_ch376->write(offset, data); + break; + + case 4: + m_rombank = 0x800 * (data & 3); + break; + + default: + printf("Write %02x to c0n%x (%s)\n", data, offset, machine().describe_context().c_str()); + break; + } +} + +/*------------------------------------------------- + read_cnxx - called for reads from this card's cnxx space +-------------------------------------------------*/ + +uint8_t a2bus_booti_device::read_cnxx(uint8_t offset) +{ + // slot image at 0 + return m_flash->read(offset); +} + +void a2bus_booti_device::write_cnxx(uint8_t offset, uint8_t data) +{ +} + +/*------------------------------------------------- + read_c800 - called for reads from this card's c800 space +-------------------------------------------------*/ + +uint8_t a2bus_booti_device::read_c800(uint16_t offset) +{ + return m_flash->read(offset + m_rombank); +} + +/*------------------------------------------------- + write_c800 - called for writes to this card's c800 space +-------------------------------------------------*/ +void a2bus_booti_device::write_c800(uint16_t offset, uint8_t data) +{ + // the card appears not to pass writes to $CFFF to the EEPROM, + // otherwise there's a false read of the next opcode after a $CFFF write and we crash. + if (offset < 0x7ff) + { + m_flash->write(offset + m_rombank, data); + } +} diff --git a/src/devices/bus/a2bus/booti.h b/src/devices/bus/a2bus/booti.h new file mode 100644 index 00000000000..571aa799e90 --- /dev/null +++ b/src/devices/bus/a2bus/booti.h @@ -0,0 +1,58 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +/********************************************************************* + + booti.h + + Implementation of the Booti card + +*********************************************************************/ + +#ifndef MAME_BUS_A2BUS_BOOTI_H +#define MAME_BUS_A2BUS_BOOTI_H + +#pragma once + +#include "a2bus.h" +#include "machine/at28c64b.h" +#include "machine/ch376.h" + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +class a2bus_booti_device: + public device_t, + public device_a2bus_card_interface +{ +public: + // construction/destruction + a2bus_booti_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + +protected: + a2bus_booti_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_add_mconfig(machine_config &config) override; + virtual const tiny_rom_entry *device_rom_region() const override; + + // overrides of standard a2bus slot functions + virtual uint8_t read_c0nx(uint8_t offset) override; + virtual void write_c0nx(uint8_t offset, uint8_t data) override; + virtual uint8_t read_cnxx(uint8_t offset) override; + virtual void write_cnxx(uint8_t offset, uint8_t data) override; + virtual uint8_t read_c800(uint16_t offset) override; + virtual void write_c800(uint16_t offset, uint8_t data) override; + +private: + required_device m_flash; + required_device m_ch376; + + int m_rombank; +}; + +// device type definition +DECLARE_DEVICE_TYPE(A2BUS_BOOTI, a2bus_booti_device) + +#endif // MAME_BUS_A2BUS_BOOTI_H diff --git a/src/devices/machine/at28c64b.cpp b/src/devices/machine/at28c64b.cpp new file mode 100644 index 00000000000..194711228a1 --- /dev/null +++ b/src/devices/machine/at28c64b.cpp @@ -0,0 +1,279 @@ +// license:BSD-3-Clause +// copyright-holders:smf, R. Belmont +/*************************************************************************** + ATMEL AT28C64B + + 64K ( 8K x 8 ) Parallel EEPROM with flash-like in-band signalling + +***************************************************************************/ + +#include "emu.h" +#include "machine/at28c64b.h" + +static constexpr int AT28C64B_DATA_BYTES = 0x10000; +static constexpr int AT28C64B_ID_BYTES = 0x40; +static constexpr int AT28C64B_TOTAL_BYTES = AT28C64B_DATA_BYTES + AT28C64B_ID_BYTES; + +static constexpr int AT28C64B_ID_OFFSET = 0x1fc0; +static constexpr int AT28C64B_SECTOR_SIZE = 0x40; + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +void at28c64b_device::at28c64b_map8(address_map &map) +{ + map(0x00000, 0x1003f).ram(); +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +// device type definition +DEFINE_DEVICE_TYPE(AT28C64B, at28c64b_device, "at28c64b", "AT28C64B 8Kx8 EEPROM") + +//------------------------------------------------- +// at28c64b_device - constructor +//------------------------------------------------- + +at28c64b_device::at28c64b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, AT28C64B, tag, owner, clock), + device_memory_interface(mconfig, *this), + device_nvram_interface(mconfig, *this), + m_space_config("at28c64b", ENDIANNESS_BIG, 8, 17, 0, address_map_constructor(FUNC(at28c64b_device::at28c64b_map8), this)), + m_a9_12v(0), + m_oe_12v(0), + m_last_write(-1), + m_state(0), + m_bytes_in_sector(0), + m_default_data(*this, DEVICE_SELF) +{ +} + + +//------------------------------------------------- +// memory_space_config - return a description of +// any address spaces owned by this device +//------------------------------------------------- + +device_memory_interface::space_config_vector at28c64b_device::memory_space_config() const +{ + return space_config_vector { + std::make_pair(0, &m_space_config) + }; +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void at28c64b_device::device_start() +{ + m_write_timer = timer_alloc(0); + + save_item( NAME(m_a9_12v) ); + save_item( NAME(m_oe_12v) ); + save_item( NAME(m_last_write) ); +} + + +//------------------------------------------------- +// nvram_default - called to initialize NVRAM to +// its default state +//------------------------------------------------- + +void at28c64b_device::nvram_default() +{ + uint16_t default_value = 0xff; + for( offs_t offs = 0; offs < AT28C64B_TOTAL_BYTES; offs++ ) + { + space(AS_PROGRAM).write_byte( offs, default_value ); + } + + /* populate from a memory region if present */ + printf("checking for default\n"); + if (m_default_data.found()) + { + printf("Got default data\n"); + for( offs_t offs = 0; offs < AT28C64B_DATA_BYTES; offs++ ) + space(AS_PROGRAM).write_byte(offs, m_default_data[offs]); + } +} + + +//------------------------------------------------- +// nvram_read - called to read NVRAM from the +// .nv file +//------------------------------------------------- + +void at28c64b_device::nvram_read( emu_file &file ) +{ + std::vector buffer( AT28C64B_TOTAL_BYTES ); + + file.read( &buffer[0], AT28C64B_TOTAL_BYTES ); + + for( offs_t offs = 0; offs < AT28C64B_TOTAL_BYTES; offs++ ) + { + space(AS_PROGRAM).write_byte( offs, buffer[ offs ] ); + } +} + +//------------------------------------------------- +// nvram_write - called to write NVRAM to the +// .nv file +//------------------------------------------------- + +void at28c64b_device::nvram_write( emu_file &file ) +{ + std::vector buffer ( AT28C64B_TOTAL_BYTES ); + + for( offs_t offs = 0; offs < AT28C64B_TOTAL_BYTES; offs++ ) + { + buffer[ offs ] = space(AS_PROGRAM).read_byte( offs ); + } + + file.write( &buffer[0], AT28C64B_TOTAL_BYTES ); +} + + + +//************************************************************************** +// READ/WRITE HANDLERS +//************************************************************************** + +void at28c64b_device::write(offs_t offset, uint8_t data) +{ + logerror("%s: AT28C64B: write( %04x, %02x ) state %d last_write %d\n", machine().describe_context(), offset, data, m_state, m_last_write); + + if (m_state == STATE_SECTOR_WRITE) + { + logerror("SECTOR WRITE: %02x @ %x\n", data, offset); + this->space(AS_PROGRAM).write_byte(offset, data); + m_last_write = data; + m_write_timer->adjust(attotime::from_usec(10)); + m_bytes_in_sector--; + if (m_bytes_in_sector == 0) + { + m_state = STATE_WRITE_PROTECT; + } + return; + } + + if( m_last_write >= 0 ) + { +// logerror( "%s: AT28C64B: write( %04x, %02x ) busy\n", machine().describe_context(), offset, data ); + } + else if( m_oe_12v ) + { +// logerror( "%s: AT28C64B: write( %04x, %02x ) erase\n", machine().describe_context(), offset, data ); + if( m_last_write < 0 ) + { + for( offs_t offs = 0; offs < AT28C64B_TOTAL_BYTES; offs++ ) + { + this->space(AS_PROGRAM).write_byte( offs, 0xff ); + } + + m_last_write = 0xff; + m_write_timer->adjust( attotime::from_usec( 10 ) ); + } + } + else + { + if ((offset == 0x1555) && (data == 0xaa)) + { + m_state = STATE_ID_1; + return; + } + + if ((m_state == STATE_ID_1) && (offset == 0xaaa) && (data == 0x55)) + { + m_state = STATE_ID_2; + return; + } + + if ((m_state == STATE_ID_2) && (offset == 0x1555) && (data == 0xa0)) + { + m_state = STATE_SECTOR_WRITE; + m_bytes_in_sector = AT28C64B_SECTOR_SIZE; + return; + } + + if (m_state == STATE_WRITE_PROTECT) + { + logerror("%s: write %02x to %x while write protected\n", machine().describe_context(), data, offset); + return; + } + + if ((m_a9_12v) && (offset >= AT28C64B_ID_OFFSET) && (offset < (AT28C64B_ID_OFFSET + AT28C64B_ID_BYTES))) + { + offset += AT28C64B_ID_BYTES; + } + + if( m_last_write < 0 && this->space(AS_PROGRAM).read_byte( offset ) != data ) + { + this->space(AS_PROGRAM).write_byte( offset, data ); + m_last_write = data; + m_write_timer->adjust( attotime::from_usec( 10 ) ); + } + } +} + + +uint8_t at28c64b_device::read(offs_t offset) +{ + if( m_last_write >= 0 ) + { + uint8_t data = m_last_write ^ 0x80; +// logerror( "%s: AT28C64B: read( %04x ) write status %02x\n", machine().describe_context(), offset, data ); + return data; + } + else + { + if( m_a9_12v && offset >= AT28C64B_ID_OFFSET ) + { + offset += AT28C64B_ID_BYTES; + } + + uint8_t data = this->space(AS_PROGRAM).read_byte( offset ); +// logerror( "%s: AT28C64B: read( %04x ) data %02x\n", machine().describe_context(), offset, data ); + return data; + } +} + + +WRITE_LINE_MEMBER( at28c64b_device::set_a9_12v ) +{ + state &= 1; + if( m_a9_12v != state ) + { +// logerror( "%s: AT28C64B: set_a9_12v( %d )\n", machine().describe_context(), state ); + m_a9_12v = state; + } +} + + +WRITE_LINE_MEMBER( at28c64b_device::set_oe_12v ) +{ + state &= 1; + if( m_oe_12v != state ) + { +// logerror( "%s: AT28C64B: set_oe_12v( %d )\n", machine().describe_context(), state ); + m_oe_12v = state; + } +} + + +void at28c64b_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch( id ) + { + case 0: + m_last_write = -1; + break; + } +} diff --git a/src/devices/machine/at28c64b.h b/src/devices/machine/at28c64b.h new file mode 100644 index 00000000000..994625bf42d --- /dev/null +++ b/src/devices/machine/at28c64b.h @@ -0,0 +1,80 @@ +// license:BSD-3-Clause +// copyright-holders:smf +/*************************************************************************** + + ATMEL AT28C64B + + 64K ( 8K x 8 ) Parallel EEPROM + +***************************************************************************/ + +#ifndef MAME_MACHINE_AT28C64B_H +#define MAME_MACHINE_AT28C64B_H + +#pragma once + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> at28c64b_device + +class at28c64b_device : + public device_t, + public device_memory_interface, + public device_nvram_interface +{ +public: + // construction/destruction + at28c64b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + void write(offs_t offset, u8 data); + u8 read(offs_t offset); + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + + // device_memory_interface overrides + virtual space_config_vector memory_space_config() const override; + + // device_nvram_interface overrides + virtual void nvram_default() override; + virtual void nvram_read(emu_file &file) override; + virtual void nvram_write(emu_file &file) override; + +private: + // internal state + enum + { + STATE_IDLE, + STATE_ID_1, + STATE_ID_2, + STATE_SECTOR_WRITE, + STATE_WRITE_PROTECT + }; + + address_space_config m_space_config; + emu_timer *m_write_timer; + int m_a9_12v; + int m_oe_12v; + int m_last_write; + int m_state; + int m_bytes_in_sector; + + optional_region_ptr m_default_data; + + // I/O operations + DECLARE_WRITE_LINE_MEMBER( set_a9_12v ); + DECLARE_WRITE_LINE_MEMBER( set_oe_12v ); + + void at28c64b_map8(address_map &map); +}; + + +// device type definition +DECLARE_DEVICE_TYPE(AT28C64B, at28c64b_device) + +#endif // MAME_MACHINE_AT28C64B_H diff --git a/src/devices/machine/ch376.cpp b/src/devices/machine/ch376.cpp new file mode 100644 index 00000000000..0b62e0a9076 --- /dev/null +++ b/src/devices/machine/ch376.cpp @@ -0,0 +1,505 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +/*************************************************************************** + + ch376.h + + "File manage and control chip CH376" + https://www.mpja.com/download/ch376ds1.pdf + + This is a module intended to offload USB and USB mass storage + I/O from a small microcontroller or microprocessor. + + It has 3 host interfaces: + 1) A regular 8-bit bus with 8 data lines, an address line, + chip select, read enable, and write enable lines + 2) An SPI bus interface (SCS, SCK, MOSI, MISO lines) + 3) An asynchronous serial interface (TxD and RxD lines) + + And 2 guest interfaces: USB 2.0 and a second 4-wire SPI bus for SD/MMC/TF cards. + + The module can give high level directory/open/close/read/write access to + files on a FAT12, FAT16, or FAT32 filesystem. + +***************************************************************************/ + +#include "emu.h" +#include "emuopts.h" +#include "machine/ch376.h" + +#include "logmacro.h" + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +// device type definition +DEFINE_DEVICE_TYPE(CH376, ch376_device, "ch376", "CH376 USB/file manager module") + +static constexpr u8 STATUS_USB_INT_SUCCESS = 0x14; +static constexpr u8 STATUS_USB_INT_CONNECT = 0x15; +static constexpr u8 STATUS_USB_INT_DISK_READ = 0x1d; +static constexpr u8 STATUS_USB_INT_DISK_WRITE = 0x1e; +static constexpr u8 STATUS_USB_INT_DISK_ERR = 0x1f; +static constexpr u8 STATUS_ERR_MISS_FILE = 0x42; + +static constexpr u8 CMD_GET_IC_VER = 0x01; +static constexpr u8 CMD_GET_FILE_SIZE = 0x0c; +static constexpr u8 CMD_SET_USB_MODE = 0x15; +static constexpr u8 CMD_GET_STATUS = 0x22; +static constexpr u8 CMD_RD_USB_DATA0 = 0x27; +static constexpr u8 CMD_WR_REQ_DATA = 0x2d; +static constexpr u8 CMD_SET_FILE_NAME = 0x2f; +static constexpr u8 CMD_DISK_MOUNT = 0x31; +static constexpr u8 CMD_FILE_OPEN = 0x32; +static constexpr u8 CMD_FILE_ENUM_GO = 0x33; +static constexpr u8 CMD_FILE_CLOSE = 0x36; +static constexpr u8 CMD_BYTE_LOCATE = 0x39; +static constexpr u8 CMD_BYTE_READ = 0x3a; +static constexpr u8 CMD_BYTE_READ_GO = 0x3b; +static constexpr u8 CMD_BYTE_WRITE = 0x3c; +static constexpr u8 CMD_BYTE_WR_GO = 0x3d; + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// ch376_device - constructor +//------------------------------------------------- + +ch376_device::ch376_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, CH376, tag, owner, clock), + m_state(0), + m_status(0), + m_int_status(0), + m_data(0), + m_last_command(0), + m_pointer(0), + m_file_pos(0), + m_dataPtr(0), + m_dataLen(0), + m_readLen(0), + m_cur_file_size(0) +{ + std::fill_n(&m_file_name[0], sizeof(m_file_name), 0); + std::fill_n(&m_dataBuffer[0], sizeof(m_dataBuffer), 0); + m_current_directory.clear(); +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void ch376_device::device_start() +{ + save_item(NAME(m_state)); + save_item(NAME(m_status)); + save_item(NAME(m_int_status)); + save_item(NAME(m_data)); + save_item(NAME(m_file_name)); + save_item(NAME(m_pointer)); + save_item(NAME(m_dataPtr)); + save_item(NAME(m_dataLen)); + save_item(NAME(m_readLen)); + + m_directory.reset(); + m_file.reset(); +} + +u8 ch376_device::read(offs_t offset) +{ + if (offset & 1) + { + return m_status; + } + + switch (m_state) + { + case STATE_GET_STATUS: + m_state = STATE_IDLE; + return m_int_status; + + case STATE_WRITE_SIZE_REPLY: + m_state = STATE_READ_WRITE_DATA; + m_dataPtr = 0; + return m_data; + } + + if ((m_dataLen > 0) && (m_dataPtr < m_dataLen)) + { + return m_dataBuffer[m_dataPtr++]; + } + + return m_data; +} + +void ch376_device::write(offs_t offset, u8 data) +{ + if (offset & 1) // write command + { + //printf("CH376: write command %02x\n", data); + m_last_command = data; + switch (data) + { + case CMD_GET_IC_VER: + m_dataBuffer[0] = 0x41; // + m_dataPtr = 0; + m_dataLen = 1; + break; + + case CMD_GET_FILE_SIZE: + m_state = STATE_GET_FILE_SIZE; + break; + + case CMD_SET_USB_MODE: + m_state = STATE_USB_MODE_SET; + break; + + case CMD_GET_STATUS: + m_state = STATE_GET_STATUS; + break; + + case CMD_RD_USB_DATA0: + m_int_status = STATUS_USB_INT_DISK_READ; + break; + + case CMD_WR_REQ_DATA: + { + int lenToWrite = (m_readLen >= 255) ? 255 : m_readLen; + m_data = (lenToWrite & 0xff); + m_readLen -= lenToWrite; + m_state = STATE_WRITE_SIZE_REPLY; + m_dataLen = lenToWrite; + } + break; + + case CMD_SET_FILE_NAME: + m_state = STATE_SET_FILE_NAME; + m_pointer = 0; + break; + + case CMD_DISK_MOUNT: + m_int_status = STATUS_USB_INT_CONNECT; + break; + + case CMD_FILE_OPEN: + /* + CH376 allows paths with a leading / or \ to indicate the root of the SD card. + Directories are chained by an unusual method: start with the root dir (leading path separator), + then open each subdirectory without a leading path separator, and finally open the file without + a leading path separator. + + The BOOTI card ignores directories entirely, so the implmentation here is not tested with that + scenario but it should work. + */ + if (m_file_name[strlen(m_file_name) - 1] == '*') + { + // it's a directory + // remove the wildcard + m_file_name[strlen(m_file_name) - 1] = '\0'; + std::string tmpPath(machine().options().share_directory()); + tmpPath.append(PATH_SEPARATOR); + if ((m_file_name[0] == '/') || (m_file_name[0] == '\\')) + { + tmpPath.append(&m_file_name[1]); + m_current_directory.clear(); + m_current_directory = tmpPath; + } + else + { + tmpPath.append(m_file_name); + m_current_directory.append(PATH_SEPARATOR); + m_current_directory.append(tmpPath); + } + m_directory = osd::directory::open(tmpPath); + if (generateNextDirEntry()) + { + m_int_status = STATUS_USB_INT_DISK_READ; + } + else + { + m_int_status = STATUS_USB_INT_DISK_ERR; + } + } + else + { + std::string tmpPath = m_current_directory; + u64 size; + + // compose this name with the last directory opened, unless it starts with a path separator + tmpPath.append(PATH_SEPARATOR); + if ((m_file_name[0] == '/') || (m_file_name[0] == '\\')) + { + tmpPath.append(&m_file_name[1]); + } + else + { + tmpPath.append(m_file_name); + } + + if (osd_file::open(tmpPath, OPEN_FLAG_READ|OPEN_FLAG_WRITE, m_file, size) == osd_file::error::NONE) + { + m_int_status = STATUS_USB_INT_SUCCESS; + m_cur_file_size = size & 0xffffffff; + } + else + { + m_int_status = STATUS_USB_INT_DISK_ERR; + m_cur_file_size = 0; + } + } + break; + + case CMD_FILE_ENUM_GO: + if (generateNextDirEntry()) + { + m_int_status = STATUS_USB_INT_DISK_READ; + } + else // end of directory + { + m_int_status = STATUS_ERR_MISS_FILE; + } + break; + + case CMD_FILE_CLOSE: + m_directory.reset(); + m_file.reset(); + m_int_status = STATUS_USB_INT_DISK_READ; + break; + + case CMD_BYTE_LOCATE: + m_state = STATE_SEEK; + break; + + case CMD_BYTE_READ: + m_state = STATE_READ_SIZE; + break; + + case CMD_BYTE_READ_GO: + if (m_readLen > 0) + { + doNextFileRead(); + m_int_status = STATUS_USB_INT_DISK_READ; + } + else + { + m_dataBuffer[0] = 0; + m_dataLen = 0; + m_int_status = STATUS_USB_INT_SUCCESS; + } + break; + + case CMD_BYTE_WRITE: + m_state = STATE_READ_SIZE; + break; + + case CMD_BYTE_WR_GO: + if (m_dataPtr > 0) + { + u32 actual; + m_state = STATE_IDLE; + m_file->write(&m_dataBuffer[0], m_file_pos, m_dataLen, actual); + m_file_pos += m_dataLen; + if (m_readLen > 0) + { + m_int_status = STATUS_USB_INT_DISK_WRITE; + } + else + { + m_int_status = STATUS_USB_INT_SUCCESS; + } + } + else + { + m_int_status = STATUS_USB_INT_SUCCESS; + } + break; + + default: + logerror("CH376: unhandled command %02x\n", data); + break; + } + } + else + { + switch (m_state) + { + case STATE_USB_MODE_SET: + m_state = STATE_IDLE; + break; + + case STATE_SET_FILE_NAME: + m_file_name[m_pointer++] = data; + if (m_pointer == 14) + { + m_state = STATE_IDLE; + // is this shorter than 8.3? + if (m_file_name[8] == '\0') + { + int idx = 8; + while (m_file_name[idx] == '\0') + { + idx--; + } + idx++; + for (int i = 0; i < 5; i++) + { + m_file_name[idx + i] = m_file_name[9 + i]; + } + } + } + break; + + case STATE_SEEK: + case STATE_SEEK1: + case STATE_SEEK2: + m_file_pos >>= 8; + m_file_pos |= (data << 24); + m_state++; + break; + + case STATE_SEEK3: + m_file_pos >>= 8; + m_file_pos |= (data << 24); + m_state = STATE_IDLE; + m_int_status = STATUS_USB_INT_SUCCESS; + break; + + case STATE_READ_SIZE: + m_readLen = data; + m_state++; + break; + + case STATE_READ_SIZE2: + m_readLen |= (data << 8); + m_state = STATE_IDLE; + m_int_status = STATUS_USB_INT_DISK_READ; + + if (m_last_command == 0x3a) + { + doNextFileRead(); + } + break; + + case STATE_READ_WRITE_DATA: + m_dataBuffer[m_dataPtr++] = data; + break; + + case STATE_GET_FILE_SIZE: + // the host must write 0x68 here; it's unclear what the real chip does if the value doesn't match. + // reply with the size of the currently open file. + m_dataBuffer[0] = m_cur_file_size & 0xff; + m_dataBuffer[1] = (m_cur_file_size >> 8) & 0xff; + m_dataBuffer[2] = (m_cur_file_size >> 16) & 0xff; + m_dataBuffer[3] = (m_cur_file_size >> 24) & 0xff; + m_dataPtr = 0; + m_dataLen = 4; + break; + } + } +} + +bool ch376_device::generateNextDirEntry() +{ + const osd::directory::entry *ourEntry; + + m_dataPtr = 0; + m_dataLen = 0; + + // no directory? + if (m_directory == nullptr) + { + return false; + } + + // is there an entry? + if ((ourEntry = m_directory->read()) == nullptr) + { + return false; + } + + std::fill_n(&m_dataBuffer[0], sizeof(m_dataBuffer), 0); + m_dataBuffer[0] = 32; + if (ourEntry->type == osd::directory::entry::entry_type::DIR) + { + strncpy(reinterpret_cast(&m_dataBuffer[1]), ourEntry->name, 8); + m_dataBuffer[0xc] == 0x10; // directory type + } + else if (ourEntry->type == osd::directory::entry::entry_type::FILE) + { + int dotIdx = 8; + for (int idx = (strlen(ourEntry->name) - 1); idx >= 0; idx--) + { + if (ourEntry->name[idx] == '.') + { + dotIdx = idx; + break; + } + } + + int baseLen = std::min(8, dotIdx); + for (int idx = 0; idx < baseLen; idx++) + { + m_dataBuffer[idx + 1] = toupper(ourEntry->name[idx]); + if (ourEntry->name[idx] == '\0') + { + baseLen = idx; + break; + } + } + + // copy the extension (up to 3 chars, if we found one) + if (ourEntry->name[dotIdx] == '.') + { + dotIdx++; + for (int idx = 0; idx < 3; idx++) + { + if ((idx + dotIdx) > strlen(ourEntry->name)) + { + break; + } + m_dataBuffer[idx + 8 + 1] = toupper(ourEntry->name[idx + dotIdx]); + } + } + + m_dataBuffer[0xc] = 0; // no attributes + + if (ourEntry->size >= u64(0x100000000)) + { + m_dataBuffer[0x1d] = 0xff; + m_dataBuffer[0x1e] = 0xff; + m_dataBuffer[0x1f] = 0xff; + m_dataBuffer[0x20] = 0xff; + } + else + { + m_dataBuffer[0x1d] = (ourEntry->size & 0xff); + m_dataBuffer[0x1e] = ((ourEntry->size >> 8) & 0xff); + m_dataBuffer[0x1f] = ((ourEntry->size >> 16) & 0xff); + m_dataBuffer[0x20] = ((ourEntry->size >> 24) & 0xff); + } + } + else // not a file or directory, recurse and hope the next one's better + { + return generateNextDirEntry(); + } + + m_dataLen = 32; + return true; +} + +void ch376_device::doNextFileRead() +{ + int lenToRead = (m_readLen >= 255) ? 255 : m_readLen; + u32 actualRead; + + m_readLen -= lenToRead; + m_file->read(&m_dataBuffer[1], m_file_pos, lenToRead, actualRead); + m_dataBuffer[0] = (lenToRead & 0xff); + m_file_pos += actualRead; + m_dataLen = 256; + m_dataPtr = 0; +} diff --git a/src/devices/machine/ch376.h b/src/devices/machine/ch376.h new file mode 100644 index 00000000000..85af7069099 --- /dev/null +++ b/src/devices/machine/ch376.h @@ -0,0 +1,80 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +/*************************************************************************** + + ch376.h + + "File manage and control chip CH376" + This is a module intended to offload USB and USB mass storage + I/O from a small microcontroller or microprocessor. + +***************************************************************************/ + +#ifndef MAME_MACHINE_CH376_H +#define MAME_MACHINE_CH376_H + +#pragma once + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + + +// ======================> ch376_device + +class ch376_device : public device_t +{ +public: + // construction/destruction + ch376_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0); + + u8 read(offs_t offset); + void write(offs_t offset, u8 data); + +protected: + // device-level overrides + virtual void device_start() override; + +private: + enum + { + STATE_IDLE = 0, + STATE_USB_MODE_SET, + STATE_GET_STATUS, + STATE_SET_FILE_NAME, + STATE_SEEK, + STATE_SEEK1, + STATE_SEEK2, + STATE_SEEK3, + STATE_READ_SIZE, + STATE_READ_SIZE2, + STATE_WRITE_SIZE_REPLY, + STATE_READ_WRITE_DATA, + STATE_GET_FILE_SIZE + }; + + bool generateNextDirEntry(); + void doNextFileRead(); + + int m_state; + u8 m_status, m_int_status, m_data, m_last_command; + char m_file_name[16]; + int m_pointer; + std::string m_current_directory; + osd::directory::ptr m_directory; + osd_file::ptr m_file; + u32 m_file_pos; + + u8 m_dataBuffer[512]; + int m_dataPtr, m_dataLen; + u16 m_readLen; + u32 m_cur_file_size; +}; + + +// device type definition +DECLARE_DEVICE_TYPE(CH376, ch376_device) + +#endif // MAME_MACHINE_CH376_H diff --git a/src/mame/drivers/apple2e.cpp b/src/mame/drivers/apple2e.cpp index 3a8dfa7a8f4..d2f6a86cd13 100644 --- a/src/mame/drivers/apple2e.cpp +++ b/src/mame/drivers/apple2e.cpp @@ -168,6 +168,7 @@ MIG RAM page 2 $CE02 is the speaker/slot bitfield and $CE03 is the paddle/accele #include "bus/a2bus/a2videoterm.h" #include "bus/a2bus/a2vulcan.h" #include "bus/a2bus/a2zipdrive.h" +#include "bus/a2bus/booti.h" #include "bus/a2bus/byte8251.h" #include "bus/a2bus/cmsscsi.h" #include "bus/a2bus/computereyes2.h" @@ -4694,6 +4695,7 @@ static void apple2_cards(device_slot_interface &device) device.option_add("sider1", A2BUS_SIDER1); /* Advanced Tech Systems / First Class Peripherals Sider 1 SASI card */ device.option_add("uniprint", A2BUS_UNIPRINT); /* Videx Uniprint parallel printer card */ device.option_add("ccs7710", A2BUS_CCS7710); /* California Computer Systems Model 7710 Asynchronous Serial Interface */ + device.option_add("booti", A2BUS_BOOTI); /* Booti Card */ } static void apple2eaux_cards(device_slot_interface &device) diff --git a/src/mame/drivers/apple2gs.cpp b/src/mame/drivers/apple2gs.cpp index ab228134e4e..490d5ee65be 100644 --- a/src/mame/drivers/apple2gs.cpp +++ b/src/mame/drivers/apple2gs.cpp @@ -124,6 +124,7 @@ #include "bus/a2bus/timemasterho.h" #include "bus/a2bus/uniprint.h" #include "bus/a2bus/uthernet.h" +#include "bus/a2bus/booti.h" #include "bus/a2gameio/gameio.h" @@ -4771,6 +4772,7 @@ static void apple2_cards(device_slot_interface &device) device.option_add("sider1", A2BUS_SIDER1); /* Advanced Tech Systems / First Class Peripherals Sider 1 SASI card */ device.option_add("uniprint", A2BUS_UNIPRINT); /* Videx Uniprint parallel printer card */ device.option_add("ccs7710", A2BUS_CCS7710); /* California Computer Systems Model 7710 Asynchronous Serial Interface */ + device.option_add("booti", A2BUS_BOOTI); /* Booti Card */ } void apple2gs_state::apple2gs(machine_config &config)